mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-01-14 00:10:40 +01:00
Add rotate and compress (#7)
* Add files via upload * Update build.gradle * Update general.css
This commit is contained in:
parent
b56e5a0e05
commit
cd4bd2a796
@ -5,11 +5,15 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = 'stirling.software'
|
group = 'stirling.software'
|
||||||
version = '0.1.0'
|
version = '0.2.0'
|
||||||
sourceCompatibility = '17'
|
sourceCompatibility = '17'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
url "https://repo.e-iceblue.com/nexus/content/groups/public/"
|
||||||
|
name "com.e-iceblue"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -18,6 +22,7 @@ dependencies {
|
|||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
implementation 'org.apache.pdfbox:pdfbox:2.0.27'
|
implementation 'org.apache.pdfbox:pdfbox:2.0.27'
|
||||||
implementation 'org.apache.logging.log4j:log4j-core:2.19.0'
|
implementation 'org.apache.logging.log4j:log4j-core:2.19.0'
|
||||||
|
implementation 'e-iceblue:spire.pdf.free:5.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
@ -27,6 +32,7 @@ jar {
|
|||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
task printVersion {
|
task printVersion {
|
||||||
println project.version
|
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);
|
private static final Logger logger = LoggerFactory.getLogger(FromPDFController.class);
|
||||||
|
|
||||||
@GetMapping("/convert-from-pdf")
|
|
||||||
public String convertFromPdfForm() {
|
|
||||||
return "convert-from-pdf";
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/convert-from-pdf")
|
@PostMapping("/convert-from-pdf")
|
||||||
public ResponseEntity<byte[]> convertToImage(@RequestParam("fileInput") MultipartFile file,
|
public ResponseEntity<byte[]> convertToImage(@RequestParam("fileInput") MultipartFile file,
|
||||||
@ -42,7 +38,7 @@ public class FromPDFController {
|
|||||||
private String getMediaType(String imageFormat) {
|
private String getMediaType(String imageFormat) {
|
||||||
if(imageFormat.equalsIgnoreCase("PNG"))
|
if(imageFormat.equalsIgnoreCase("PNG"))
|
||||||
return "image/png";
|
return "image/png";
|
||||||
else if(imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG") )
|
else if(imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG"))
|
||||||
return "image/jpeg";
|
return "image/jpeg";
|
||||||
else if(imageFormat.equalsIgnoreCase("GIF"))
|
else if(imageFormat.equalsIgnoreCase("GIF"))
|
||||||
return "image/gif";
|
return "image/gif";
|
||||||
|
@ -9,6 +9,7 @@ import org.springframework.http.HttpStatus;
|
|||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@ -22,7 +23,8 @@ public class OverlayImageController {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class);
|
private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class);
|
||||||
|
|
||||||
@GetMapping("/add-image")
|
@GetMapping("/add-image")
|
||||||
public String overlayImage() {
|
public String overlayImage(Model model) {
|
||||||
|
model.addAttribute("currentPage", "add-image");
|
||||||
return "add-image";
|
return "add-image";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,13 +33,13 @@ public class PdfController {
|
|||||||
|
|
||||||
@GetMapping("/merge-pdfs")
|
@GetMapping("/merge-pdfs")
|
||||||
public String hello(Model model) {
|
public String hello(Model model) {
|
||||||
model.addAttribute("message", "Hello, World!");
|
model.addAttribute("currentPage", "merge-pdfs");
|
||||||
return "merge-pdfs";
|
return "merge-pdfs";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/home")
|
@GetMapping("/home")
|
||||||
public String home(Model model) {
|
public String home(Model model) {
|
||||||
model.addAttribute("message", "Hello, World!");
|
model.addAttribute("currentPage", "home");
|
||||||
return "home";
|
return "home";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ public class RearrangePagesPDFController {
|
|||||||
|
|
||||||
@GetMapping("/pdf-organizer")
|
@GetMapping("/pdf-organizer")
|
||||||
public String pageOrganizer(Model model) {
|
public String pageOrganizer(Model model) {
|
||||||
|
model.addAttribute("currentPage", "pdf-organizer");
|
||||||
return "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.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@ -39,7 +40,8 @@ public class SplitPDFController {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class);
|
private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class);
|
||||||
|
|
||||||
@GetMapping("/split-pdfs")
|
@GetMapping("/split-pdfs")
|
||||||
public String splitPdfForm() {
|
public String splitPdfForm(Model model) {
|
||||||
|
model.addAttribute("currentPage", "split-pdfs");
|
||||||
return "split-pdfs";
|
return "split-pdfs";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,4 +2,4 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
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>
|
<!DOCTYPE html>
|
||||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||||
<head>
|
|
||||||
<th:block th:insert="~{common :: head}"></th:block>
|
<head>
|
||||||
<title>S-PDF</title>
|
<th:block th:insert="~{common :: head}"></th:block>
|
||||||
</head>
|
<title>S-PDF</title>
|
||||||
|
</head>
|
||||||
<body>
|
|
||||||
<div th:insert="~{navbar.html :: navbar}"></div>
|
<body>
|
||||||
<!-- Jumbotron -->
|
<div th:insert="~{navbar.html :: navbar}"></div>
|
||||||
<div class="jumbotron jumbotron-fluid" id="jumbotron">
|
<!-- Jumbotron -->
|
||||||
<div class="container">
|
<div class="jumbotron jumbotron-fluid" id="jumbotron">
|
||||||
<h1 class="display-4">Stirling PDF</h1>
|
<div class="container">
|
||||||
<p class="lead">Your locally hosted one-stop-shop for all your
|
<h1 class="display-4">Stirling PDF</h1>
|
||||||
PDF needs. (Made 100% in ChatGPT in 1 day as a experiment)</p>
|
<p class="lead">Your locally hosted one-stop-shop for all your
|
||||||
</div>
|
PDF needs. (Made 100% in ChatGPT in 1 day as a experiment)</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<!-- Features -->
|
|
||||||
<div class="container">
|
<!-- Features -->
|
||||||
<div class="row h-100">
|
|
||||||
<div class="col-4 h-100">
|
|
||||||
<div class="dark-card card">
|
<div class="container">
|
||||||
<div class="card-body">
|
<div class="row h-100">
|
||||||
<h5 class="card-title">Merge PDFs</h5>
|
<div class="col-4 h-100">
|
||||||
<p class="card-text">Easily merge multiple PDFs into one.</p>
|
<div class="dark-card card">
|
||||||
<a href="#" class="btn btn-primary" th:href="@{/merge-pdfs}">Go</a>
|
<div class="card-body">
|
||||||
</div>
|
<h5 class="card-title">Merge PDFs</h5>
|
||||||
</div>
|
<p class="card-text">Easily merge multiple PDFs into one.</p>
|
||||||
</div>
|
<a href="#" class="btn btn-primary" th:href="@{/merge-pdfs}">Go</a>
|
||||||
<div class="col-4 h-100">
|
</div>
|
||||||
<div class="dark-card card">
|
</div>
|
||||||
<div class="card-body">
|
</div>
|
||||||
<h5 class="card-title">Split PDFs</h5>
|
<div class="col-4 h-100">
|
||||||
<p class="card-text">Split your PDFs into multiple single-page
|
<div class="dark-card card">
|
||||||
documents or at specific page numbers.</p>
|
<div class="card-body">
|
||||||
<a href="#" class="btn btn-primary" th:href="@{/split-pdfs}">Go</a>
|
<h5 class="card-title">Split PDFs</h5>
|
||||||
</div>
|
<p class="card-text">Split PDFs into multiple documents</p>
|
||||||
</div>
|
<a href="#" class="btn btn-primary" th:href="@{/split-pdfs}">Go</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4 h-100">
|
</div>
|
||||||
<div class="dark-card card">
|
</div>
|
||||||
<div class="card-body">
|
<div class="col-4 h-100">
|
||||||
<h5 class="card-title">Convert to PDF</h5>
|
<div class="dark-card card">
|
||||||
<p class="card-text">Convert images to PDF.</p>
|
<div class="card-body">
|
||||||
<a href="#" class="btn btn-primary" th:href="@{/convert-to-pdf}">Go</a>
|
<h5 class="card-title">Add image to PDF</h5>
|
||||||
</div>
|
<p class="card-text">Adds image/watermark to a PDF</p>
|
||||||
</div>
|
<a href="#" class="btn btn-primary" th:href="@{/add-image}">Go</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row h-100">
|
</div>
|
||||||
<div class="col-4 h-100">
|
</div>
|
||||||
<div class="dark-card card">
|
<br>
|
||||||
<div class="card-body dark-card">
|
<div class="row h-100">
|
||||||
<h5 class="card-title">Convert from PDF</h5>
|
<div class="col-4 h-100">
|
||||||
<p class="card-text">Convert PDF to Image.</p>
|
<div class="dark-card card">
|
||||||
<a href="#" class="btn btn-primary" th:href="@{/convert-from-pdf}">Go</a>
|
<div class="card-body">
|
||||||
</div>
|
<h5 class="card-title">Convert to/from PDF</h5>
|
||||||
</div>
|
<p class="card-text">Convert images to/from PDF.</p>
|
||||||
</div>
|
<a href="#" class="btn btn-primary" th:href="@{/convert-pdf}">Go</a>
|
||||||
<div class="col-4 h-100">
|
</div>
|
||||||
<div class="dark-card card">
|
</div>
|
||||||
<div class="card-body">
|
</div>
|
||||||
<h5 class="card-title">Add image to PDF</h5>
|
|
||||||
<p class="card-text">Adds image/watermark to a PDF</p>
|
<div class="col-4 h-100">
|
||||||
<a href="#" class="btn btn-primary" th:href="@{/add-image}">Go</a>
|
<div class="dark-card card">
|
||||||
</div>
|
<div class="card-body">
|
||||||
</div>
|
<h5 class="card-title">PDF Organizer</h5>
|
||||||
</div>
|
<p class="card-text">Remove/Rearrange pages in any order</p>
|
||||||
<div class="col-4 h-100">
|
<a href="#" class="btn btn-primary" th:href="@{/pdf-organizer}">Go</a>
|
||||||
<div class="dark-card card">
|
</div>
|
||||||
<div class="card-body">
|
</div>
|
||||||
<h5 class="card-title">PDF Organizer</h5>
|
</div>
|
||||||
<p class="card-text">Rearrange PDF pages into any order (or
|
|
||||||
remove)</p>
|
<div class="col-4 h-100">
|
||||||
<a href="#" class="btn btn-primary" th:href="@{/pdf-organizer}">Go</a>
|
<div class="dark-card card">
|
||||||
</div>
|
<div class="card-body">
|
||||||
</div>
|
<h5 class="card-title">Rotate PDFs</h5>
|
||||||
</div>
|
<p class="card-text">Easily rotate your PDFs.</p>
|
||||||
</div>
|
<a href="#" class="btn btn-primary" th:href="@{/rotate-pdf}">Go</a>
|
||||||
</div>
|
</div>
|
||||||
<div th:insert="~{footer.html :: footer}"></div>
|
</div>
|
||||||
</body>
|
</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>
|
</html>
|
@ -12,29 +12,31 @@
|
|||||||
|
|
||||||
<li class="nav-item"><a class="nav-link" href="#"
|
<li class="nav-item"><a class="nav-link" href="#"
|
||||||
th:href="@{/merge-pdfs}"
|
th:href="@{/merge-pdfs}"
|
||||||
th:classappend="${currentPage}=='/merge-pdfs' ? 'active' : ''">Merge
|
th:classappend="${currentPage}=='merge-pdfs' ? 'active' : ''">Merge
|
||||||
PDFs</a></li>
|
PDFs</a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="#"
|
<li class="nav-item"><a class="nav-link" href="#"
|
||||||
th:href="@{/split-pdfs}"
|
th:href="@{/split-pdfs}"
|
||||||
th:classappend="${currentPage}=='/split-pdfs' ? 'active' : ''">Split
|
th:classappend="${currentPage}=='split-pdfs' ? 'active' : ''">Split
|
||||||
PDFs</a></li>
|
PDFs</a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="#"
|
<li class="nav-item"><a class="nav-link" href="#"
|
||||||
th:href="@{/convert-to-pdf}"
|
th:href="@{/convert-pdf}"
|
||||||
th:classappend="${currentPage}=='/convert-to-pdf' ? 'active' : ''">Convert
|
th:classappend="${currentPage}=='convert-pdf' ? 'active' : ''">Convert
|
||||||
to PDF</a></li>
|
to/from 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>
|
|
||||||
<li class="nav-item"><a class="nav-link" href="#"
|
<li class="nav-item"><a class="nav-link" href="#"
|
||||||
th:href="@{/add-image}"
|
th:href="@{/add-image}"
|
||||||
th:classappend="${currentPage}=='/add-image' ? 'active' : ''">Add
|
th:classappend="${currentPage}=='add-image' ? 'active' : ''">Add
|
||||||
image to PDF</a></li>
|
image to PDF</a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="#"
|
<li class="nav-item"><a class="nav-link" href="#"
|
||||||
th:href="@{/pdf-organizer}"
|
th:href="@{/pdf-organizer}"
|
||||||
th:classappend="${currentPage}=='/pdf-organizer' ? 'active' : ''">PDF
|
th:classappend="${currentPage}=='pdf-organizer' ? 'active' : ''">PDF
|
||||||
Organizer</a></li>
|
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"
|
<input type="checkbox" id="toggle-dark-mode"
|
||||||
th:onclick="javascript:toggleDarkMode()">
|
th:onclick="javascript:toggleDarkMode()">
|
||||||
<a class="nav-link" href="#" for="toggle-dark-mode">Dark Mode</a>
|
<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 New Issue
Block a user