Metadata editting and local only JS and pdf to image change and format pages (#44)
* Formatting * changeMeta * pdf to img fix * foramtting * new image * lang changes
This commit is contained in:
parent
aa9f8329d5
commit
c739c9dd2b
56 changed files with 11691 additions and 1409 deletions
17
.github/workflows/codeql.yml
vendored
17
.github/workflows/codeql.yml
vendored
|
@ -7,9 +7,8 @@
|
||||||
# ******** NOTE ********
|
# ******** NOTE ********
|
||||||
# We have attempted to detect the languages in your repository. Please check
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
# the `language` matrix defined below to confirm you have the correct set of
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
# supported CodeQL languages.
|
|
||||||
#
|
name: "Build repo"
|
||||||
name: "CodeQL"
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -42,15 +41,15 @@ jobs:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
# - name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
# uses: github/codeql-action/init@v2
|
||||||
with:
|
# with:
|
||||||
languages: java
|
# languages: java
|
||||||
|
|
||||||
- uses: gradle/gradle-build-action@v2.3.3
|
- uses: gradle/gradle-build-action@v2.3.3
|
||||||
with:
|
with:
|
||||||
gradle-version: 7.6
|
gradle-version: 7.6
|
||||||
arguments: assemble --no-build-cache
|
arguments: assemble --no-build-cache
|
||||||
|
|
||||||
- name: Perform CodeQL analysis
|
#- name: Perform CodeQL analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
# uses: github/codeql-action/analyze@v2
|
||||||
|
|
BIN
docs/stirling-pdf.png
Normal file
BIN
docs/stirling-pdf.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
Binary file not shown.
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 92 KiB |
|
@ -6,8 +6,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class SPdfApplication {
|
public class SPdfApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(SPdfApplication.class, args);
|
SpringApplication.run(SPdfApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.servlet.LocaleResolver;
|
import org.springframework.web.servlet.LocaleResolver;
|
||||||
|
@ -8,8 +10,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||||
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class Beans implements WebMvcConfigurer {
|
public class Beans implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
package stirling.software.SPDF.controller;
|
package stirling.software.SPDF.controller;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
|
@ -30,45 +24,45 @@ import stirling.software.SPDF.utils.PdfUtils;
|
||||||
@Controller
|
@Controller
|
||||||
public class CompressController {
|
public class CompressController {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CompressController.class);
|
private static final Logger logger = LoggerFactory.getLogger(CompressController.class);
|
||||||
|
|
||||||
@GetMapping("/compress-pdf")
|
@GetMapping("/compress-pdf")
|
||||||
public String compressPdfForm(Model model) {
|
public String compressPdfForm(Model model) {
|
||||||
model.addAttribute("currentPage", "compress-pdf");
|
model.addAttribute("currentPage", "compress-pdf");
|
||||||
return "compress-pdf";
|
return "compress-pdf";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/compress-pdf")
|
@PostMapping("/compress-pdf")
|
||||||
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile pdfFile,
|
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("imageCompressionLevel") String imageCompressionLevel)
|
||||||
@RequestParam("imageCompressionLevel") String imageCompressionLevel) throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
// Load a sample PDF document
|
// Load a sample PDF document
|
||||||
PdfDocument document = new PdfDocument();
|
PdfDocument document = new PdfDocument();
|
||||||
document.loadFromBytes(pdfFile.getBytes());
|
document.loadFromBytes(pdfFile.getBytes());
|
||||||
|
|
||||||
// Compress PDF
|
// Compress PDF
|
||||||
document.getFileInfo().setIncrementalUpdate(false);
|
document.getFileInfo().setIncrementalUpdate(false);
|
||||||
document.setCompressionLevel(PdfCompressionLevel.Best);
|
document.setCompressionLevel(PdfCompressionLevel.Best);
|
||||||
|
|
||||||
// compress PDF Images
|
// compress PDF Images
|
||||||
for (int i = 0; i < document.getPages().getCount(); i++) {
|
for (int i = 0; i < document.getPages().getCount(); i++) {
|
||||||
|
|
||||||
PdfPageBase page = document.getPages().get(i);
|
PdfPageBase page = document.getPages().get(i);
|
||||||
PdfImageInfo[] images = page.getImagesInfo();
|
PdfImageInfo[] images = page.getImagesInfo();
|
||||||
if (images != null && images.length > 0)
|
if (images != null && images.length > 0)
|
||||||
for (int j = 0; j < images.length; j++) {
|
for (int j = 0; j < images.length; j++) {
|
||||||
PdfImageInfo image = images[j];
|
PdfImageInfo image = images[j];
|
||||||
PdfBitmap bp = new PdfBitmap(image.getImage());
|
PdfBitmap bp = new PdfBitmap(image.getImage());
|
||||||
// bp.setPngDirectToJpeg(true);
|
// bp.setPngDirectToJpeg(true);
|
||||||
bp.setQuality(Integer.valueOf(imageCompressionLevel));
|
bp.setQuality(Integer.valueOf(imageCompressionLevel));
|
||||||
|
|
||||||
page.replaceImage(j, bp);
|
page.replaceImage(j, bp);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_compressed.pdf");
|
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_compressed.pdf");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,54 +24,52 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
@Controller
|
@Controller
|
||||||
public class MergeController {
|
public class MergeController {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
|
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
|
||||||
|
|
||||||
@GetMapping("/merge-pdfs")
|
@GetMapping("/merge-pdfs")
|
||||||
public String hello(Model model) {
|
public String hello(Model model) {
|
||||||
model.addAttribute("currentPage", "merge-pdfs");
|
model.addAttribute("currentPage", "merge-pdfs");
|
||||||
return "merge-pdfs";
|
return "merge-pdfs";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/merge-pdfs")
|
@PostMapping("/merge-pdfs")
|
||||||
public ResponseEntity<InputStreamResource> mergePdfs(@RequestParam("fileInput") MultipartFile[] files)
|
public ResponseEntity<InputStreamResource> mergePdfs(@RequestParam("fileInput") MultipartFile[] files) throws IOException {
|
||||||
throws IOException {
|
// Read the input PDF files into PDDocument objects
|
||||||
// Read the input PDF files into PDDocument objects
|
List<PDDocument> documents = new ArrayList<>();
|
||||||
List<PDDocument> documents = new ArrayList<>();
|
|
||||||
|
|
||||||
// Loop through the files array and read each file into a PDDocument
|
// Loop through the files array and read each file into a PDDocument
|
||||||
for (MultipartFile file : files) {
|
for (MultipartFile file : files) {
|
||||||
documents.add(PDDocument.load(file.getInputStream()));
|
documents.add(PDDocument.load(file.getInputStream()));
|
||||||
}
|
}
|
||||||
|
|
||||||
PDDocument mergedDoc = mergeDocuments(documents);
|
PDDocument mergedDoc = mergeDocuments(documents);
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
mergedDoc.save(byteArrayOutputStream);
|
mergedDoc.save(byteArrayOutputStream);
|
||||||
mergedDoc.close();
|
mergedDoc.close();
|
||||||
|
|
||||||
// Create an InputStreamResource from the merged PDF
|
// Create an InputStreamResource from the merged PDF
|
||||||
InputStreamResource resource = new InputStreamResource(
|
InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
|
||||||
new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
|
|
||||||
|
|
||||||
// Return the merged PDF as a response
|
// Return the merged PDF as a response
|
||||||
return ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF).body(resource);
|
return ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF).body(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
||||||
// Create a new empty document
|
// Create a new empty document
|
||||||
PDDocument mergedDoc = new PDDocument();
|
PDDocument mergedDoc = new PDDocument();
|
||||||
|
|
||||||
// Iterate over the list of documents and add their pages to the merged document
|
// Iterate over the list of documents and add their pages to the merged document
|
||||||
for (PDDocument doc : documents) {
|
for (PDDocument doc : documents) {
|
||||||
// Get all pages from the current document
|
// Get all pages from the current document
|
||||||
PDPageTree pages = doc.getPages();
|
PDPageTree pages = doc.getPages();
|
||||||
// Iterate over the pages and add them to the merged document
|
// Iterate over the pages and add them to the merged document
|
||||||
for (PDPage page : pages) {
|
for (PDPage page : pages) {
|
||||||
mergedDoc.addPage(page);
|
mergedDoc.addPage(page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the merged document
|
// Return the merged document
|
||||||
return mergedDoc;
|
return mergedDoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -4,9 +4,7 @@ import java.io.IOException;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
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.ui.Model;
|
||||||
|
@ -20,27 +18,26 @@ import stirling.software.SPDF.utils.PdfUtils;
|
||||||
@Controller
|
@Controller
|
||||||
public class OverlayImageController {
|
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(Model model) {
|
public String overlayImage(Model model) {
|
||||||
model.addAttribute("currentPage", "add-image");
|
model.addAttribute("currentPage", "add-image");
|
||||||
return "add-image";
|
return "add-image";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/add-image")
|
@PostMapping("/add-image")
|
||||||
public ResponseEntity<byte[]> overlayImage(@RequestParam("fileInput") MultipartFile pdfFile,
|
public ResponseEntity<byte[]> overlayImage(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("fileInput2") MultipartFile imageFile, @RequestParam("x") float x,
|
||||||
@RequestParam("fileInput2") MultipartFile imageFile, @RequestParam("x") float x,
|
@RequestParam("y") float y) {
|
||||||
@RequestParam("y") float y) {
|
try {
|
||||||
try {
|
byte[] pdfBytes = pdfFile.getBytes();
|
||||||
byte[] pdfBytes = pdfFile.getBytes();
|
byte[] imageBytes = imageFile.getBytes();
|
||||||
byte[] imageBytes = imageFile.getBytes();
|
byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y);
|
||||||
byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y);
|
|
||||||
|
|
||||||
return PdfUtils.bytesToWebResponse(result, pdfFile.getName() + "_overlayed.pdf");
|
return PdfUtils.bytesToWebResponse(result, pdfFile.getName() + "_overlayed.pdf");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Failed to add image to PDF", e);
|
logger.error("Failed to add image to PDF", e);
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,25 @@
|
||||||
package stirling.software.SPDF.controller;
|
package stirling.software.SPDF.controller;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
|
||||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.core.io.InputStreamResource;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
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.RequestParam;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
public class PdfController {
|
public class PdfController {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(PdfController.class);
|
private static final Logger logger = LoggerFactory.getLogger(PdfController.class);
|
||||||
|
|
||||||
@GetMapping("/home")
|
|
||||||
public String root(Model model) {
|
|
||||||
return "redirect:/";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("/")
|
|
||||||
public String home(Model model) {
|
|
||||||
model.addAttribute("currentPage", "home");
|
|
||||||
return "home";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@GetMapping("/home")
|
||||||
|
public String root(Model model) {
|
||||||
|
return "redirect:/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
public String home(Model model) {
|
||||||
|
model.addAttribute("currentPage", "home");
|
||||||
|
return "home";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
package stirling.software.SPDF.controller;
|
package stirling.software.SPDF.controller;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -9,9 +8,6 @@ import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
|
@ -25,104 +21,102 @@ import stirling.software.SPDF.utils.PdfUtils;
|
||||||
@Controller
|
@Controller
|
||||||
public class RearrangePagesPDFController {
|
public class RearrangePagesPDFController {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class);
|
private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class);
|
||||||
|
|
||||||
@GetMapping("/pdf-organizer")
|
@GetMapping("/pdf-organizer")
|
||||||
public String pageOrganizer(Model model) {
|
public String pageOrganizer(Model model) {
|
||||||
model.addAttribute("currentPage", "pdf-organizer");
|
model.addAttribute("currentPage", "pdf-organizer");
|
||||||
return "pdf-organizer";
|
return "pdf-organizer";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/remove-pages")
|
@GetMapping("/remove-pages")
|
||||||
public String pageDeleter(Model model) {
|
public String pageDeleter(Model model) {
|
||||||
model.addAttribute("currentPage", "remove-pages");
|
model.addAttribute("currentPage", "remove-pages");
|
||||||
return "remove-pages";
|
return "remove-pages";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/remove-pages")
|
@PostMapping("/remove-pages")
|
||||||
public ResponseEntity<byte[]> deletePages(@RequestParam("fileInput") MultipartFile pdfFile,
|
public ResponseEntity<byte[]> deletePages(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("pagesToDelete") String pagesToDelete) throws IOException {
|
||||||
@RequestParam("pagesToDelete") String pagesToDelete) throws IOException {
|
|
||||||
|
|
||||||
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
||||||
|
|
||||||
// Split the page order string into an array of page numbers or range of numbers
|
// Split the page order string into an array of page numbers or range of numbers
|
||||||
String[] pageOrderArr = pagesToDelete.split(",");
|
String[] pageOrderArr = pagesToDelete.split(",");
|
||||||
|
|
||||||
List<Integer> pagesToRemove = pageOrderToString(pageOrderArr, document.getNumberOfPages());
|
List<Integer> pagesToRemove = pageOrderToString(pageOrderArr, document.getNumberOfPages());
|
||||||
|
|
||||||
for (int i = pagesToRemove.size() - 1; i >= 0; i--) {
|
for (int i = pagesToRemove.size() - 1; i >= 0; i--) {
|
||||||
int pageIndex = pagesToRemove.get(i);
|
int pageIndex = pagesToRemove.get(i);
|
||||||
document.removePage(pageIndex);
|
document.removePage(pageIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_removed_pages.pdf");
|
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_removed_pages.pdf");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Integer> pageOrderToString(String[] pageOrderArr, int totalPages) {
|
private List<Integer> pageOrderToString(String[] pageOrderArr, int totalPages) {
|
||||||
List<Integer> newPageOrder = new ArrayList<Integer>();
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
// loop through the page order array
|
// loop through the page order array
|
||||||
for (String element : pageOrderArr) {
|
for (String element : pageOrderArr) {
|
||||||
// check if the element contains a range of pages
|
// check if the element contains a range of pages
|
||||||
if (element.contains("-")) {
|
if (element.contains("-")) {
|
||||||
// split the range into start and end page
|
// split the range into start and end page
|
||||||
String[] range = element.split("-");
|
String[] range = element.split("-");
|
||||||
int start = Integer.parseInt(range[0]);
|
int start = Integer.parseInt(range[0]);
|
||||||
int end = Integer.parseInt(range[1]);
|
int end = Integer.parseInt(range[1]);
|
||||||
// check if the end page is greater than total pages
|
// check if the end page is greater than total pages
|
||||||
if (end > totalPages) {
|
if (end > totalPages) {
|
||||||
end = totalPages;
|
end = totalPages;
|
||||||
}
|
}
|
||||||
// loop through the range of pages
|
// loop through the range of pages
|
||||||
for (int j = start; j <= end; j++) {
|
for (int j = start; j <= end; j++) {
|
||||||
// print the current index
|
// print the current index
|
||||||
newPageOrder.add(j - 1);
|
newPageOrder.add(j - 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if the element is a single page
|
// if the element is a single page
|
||||||
newPageOrder.add(Integer.parseInt(element) - 1);
|
newPageOrder.add(Integer.parseInt(element) - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newPageOrder;
|
return newPageOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/rearrange-pages")
|
@PostMapping("/rearrange-pages")
|
||||||
public ResponseEntity<byte[]> rearrangePages(@RequestParam("fileInput") MultipartFile pdfFile,
|
public ResponseEntity<byte[]> rearrangePages(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("pageOrder") String pageOrder) {
|
||||||
@RequestParam("pageOrder") String pageOrder) {
|
try {
|
||||||
try {
|
// Load the input PDF
|
||||||
// Load the input PDF
|
PDDocument document = PDDocument.load(pdfFile.getInputStream());
|
||||||
PDDocument document = PDDocument.load(pdfFile.getInputStream());
|
|
||||||
|
|
||||||
// Split the page order string into an array of page numbers or range of numbers
|
// Split the page order string into an array of page numbers or range of numbers
|
||||||
String[] pageOrderArr = pageOrder.split(",");
|
String[] pageOrderArr = pageOrder.split(",");
|
||||||
// int[] newPageOrder = new int[pageOrderArr.length];
|
// int[] newPageOrder = new int[pageOrderArr.length];
|
||||||
int totalPages = document.getNumberOfPages();
|
int totalPages = document.getNumberOfPages();
|
||||||
|
|
||||||
List<Integer> newPageOrder = pageOrderToString(pageOrderArr, totalPages);
|
List<Integer> newPageOrder = pageOrderToString(pageOrderArr, totalPages);
|
||||||
|
|
||||||
// Create a new list to hold the pages in the new order
|
// Create a new list to hold the pages in the new order
|
||||||
List<PDPage> newPages = new ArrayList<>();
|
List<PDPage> newPages = new ArrayList<>();
|
||||||
for (int i = 0; i < newPageOrder.size(); i++) {
|
for (int i = 0; i < newPageOrder.size(); i++) {
|
||||||
newPages.add(document.getPage(newPageOrder.get(i)));
|
newPages.add(document.getPage(newPageOrder.get(i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all the pages from the original document
|
// Remove all the pages from the original document
|
||||||
for (int i = document.getNumberOfPages() - 1; i >= 0; i--) {
|
for (int i = document.getNumberOfPages() - 1; i >= 0; i--) {
|
||||||
document.removePage(i);
|
document.removePage(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the pages in the new order
|
// Add the pages in the new order
|
||||||
for (PDPage page : newPages) {
|
for (PDPage page : newPages) {
|
||||||
document.addPage(page);
|
document.addPage(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_rearranged.pdf");
|
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_rearranged.pdf");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
||||||
logger.error("Failed rearranging documents", e);
|
logger.error("Failed rearranging documents", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,12 @@
|
||||||
package stirling.software.SPDF.controller;
|
package stirling.software.SPDF.controller;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.ListIterator;
|
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
|
@ -27,34 +20,29 @@ import stirling.software.SPDF.utils.PdfUtils;
|
||||||
@Controller
|
@Controller
|
||||||
public class RotationController {
|
public class RotationController {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(RotationController.class);
|
private static final Logger logger = LoggerFactory.getLogger(RotationController.class);
|
||||||
|
|
||||||
@GetMapping("/rotate-pdf")
|
@GetMapping("/rotate-pdf")
|
||||||
public String rotatePdfForm(Model model) {
|
public String rotatePdfForm(Model model) {
|
||||||
model.addAttribute("currentPage", "rotate-pdf");
|
model.addAttribute("currentPage", "rotate-pdf");
|
||||||
return "rotate-pdf";
|
return "rotate-pdf";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/rotate-pdf")
|
@PostMapping("/rotate-pdf")
|
||||||
public ResponseEntity<byte[]> rotatePDF(@RequestParam("fileInput") MultipartFile pdfFile,
|
public ResponseEntity<byte[]> rotatePDF(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("angle") Integer angle) throws IOException {
|
||||||
@RequestParam("angle") Integer angle) throws IOException {
|
|
||||||
|
|
||||||
// Load the PDF document
|
// Load the PDF document
|
||||||
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
||||||
|
|
||||||
// Get the list of pages in the document
|
// Get the list of pages in the document
|
||||||
PDPageTree pages = document.getPages();
|
PDPageTree pages = document.getPages();
|
||||||
|
|
||||||
// Rotate all pages by the specified angle
|
for (PDPage page : pages) {
|
||||||
Iterator<PDPage> iterPage = pages.iterator();
|
page.setRotation(page.getRotation() + angle);
|
||||||
|
}
|
||||||
|
|
||||||
while (iterPage.hasNext()) {
|
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_rotated.pdf");
|
||||||
PDPage page = iterPage.next();
|
|
||||||
page.setRotation(page.getRotation() + angle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_rotated.pdf");
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,106 +37,104 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
@Controller
|
@Controller
|
||||||
public class SplitPDFController {
|
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(Model model) {
|
public String splitPdfForm(Model model) {
|
||||||
model.addAttribute("currentPage", "split-pdfs");
|
model.addAttribute("currentPage", "split-pdfs");
|
||||||
return "split-pdfs";
|
return "split-pdfs";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/split-pages")
|
@PostMapping("/split-pages")
|
||||||
public ResponseEntity<Resource> splitPdf(@RequestParam("fileInput") MultipartFile file,
|
public ResponseEntity<Resource> splitPdf(@RequestParam("fileInput") MultipartFile file, @RequestParam("pages") String pages) throws IOException {
|
||||||
@RequestParam("pages") String pages) throws IOException {
|
// parse user input
|
||||||
// parse user input
|
|
||||||
|
|
||||||
// open the pdf document
|
// open the pdf document
|
||||||
InputStream inputStream = file.getInputStream();
|
InputStream inputStream = file.getInputStream();
|
||||||
PDDocument document = PDDocument.load(inputStream);
|
PDDocument document = PDDocument.load(inputStream);
|
||||||
|
|
||||||
List<Integer> pageNumbers = new ArrayList<>();
|
List<Integer> pageNumbers = new ArrayList<>();
|
||||||
pages = pages.replaceAll("\\s+", ""); // remove whitespaces
|
pages = pages.replaceAll("\\s+", ""); // remove whitespaces
|
||||||
if (pages.toLowerCase().equals("all")) {
|
if (pages.toLowerCase().equals("all")) {
|
||||||
for (int i = 0; i < document.getNumberOfPages(); i++) {
|
for (int i = 0; i < document.getNumberOfPages(); i++) {
|
||||||
pageNumbers.add(i);
|
pageNumbers.add(i);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
List<String> pageNumbersStr = new ArrayList<>(Arrays.asList(pages.split(",")));
|
List<String> pageNumbersStr = new ArrayList<>(Arrays.asList(pages.split(",")));
|
||||||
if (!pageNumbersStr.contains(String.valueOf(document.getNumberOfPages()))) {
|
if (!pageNumbersStr.contains(String.valueOf(document.getNumberOfPages()))) {
|
||||||
String lastpage = String.valueOf(document.getNumberOfPages());
|
String lastpage = String.valueOf(document.getNumberOfPages());
|
||||||
pageNumbersStr.add(lastpage);
|
pageNumbersStr.add(lastpage);
|
||||||
}
|
}
|
||||||
for (String page : pageNumbersStr) {
|
for (String page : pageNumbersStr) {
|
||||||
if (page.contains("-")) {
|
if (page.contains("-")) {
|
||||||
String[] range = page.split("-");
|
String[] range = page.split("-");
|
||||||
int start = Integer.parseInt(range[0]);
|
int start = Integer.parseInt(range[0]);
|
||||||
int end = Integer.parseInt(range[1]);
|
int end = Integer.parseInt(range[1]);
|
||||||
for (int i = start; i <= end; i++) {
|
for (int i = start; i <= end; i++) {
|
||||||
pageNumbers.add(i);
|
pageNumbers.add(i);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pageNumbers.add(Integer.parseInt(page));
|
pageNumbers.add(Integer.parseInt(page));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Splitting PDF into pages: {}",
|
logger.info("Splitting PDF into pages: {}", pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||||
pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
|
||||||
|
|
||||||
// split the document
|
// split the document
|
||||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
||||||
int currentPage = 0;
|
int currentPage = 0;
|
||||||
for (int pageNumber : pageNumbers) {
|
for (int pageNumber : pageNumbers) {
|
||||||
try (PDDocument splitDocument = new PDDocument()) {
|
try (PDDocument splitDocument = new PDDocument()) {
|
||||||
for (int i = currentPage; i < pageNumber; i++) {
|
for (int i = currentPage; i < pageNumber; i++) {
|
||||||
PDPage page = document.getPage(i);
|
PDPage page = document.getPage(i);
|
||||||
splitDocument.addPage(page);
|
splitDocument.addPage(page);
|
||||||
logger.debug("Adding page {} to split document", i);
|
logger.debug("Adding page {} to split document", i);
|
||||||
}
|
}
|
||||||
currentPage = pageNumber;
|
currentPage = pageNumber;
|
||||||
logger.debug("Setting current page to {}", currentPage);
|
logger.debug("Setting current page to {}", currentPage);
|
||||||
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
splitDocument.save(baos);
|
splitDocument.save(baos);
|
||||||
|
|
||||||
splitDocumentsBoas.add(baos);
|
splitDocumentsBoas.add(baos);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Failed splitting documents and saving them", e);
|
logger.error("Failed splitting documents and saving them", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// closing the original document
|
// closing the original document
|
||||||
document.close();
|
document.close();
|
||||||
|
|
||||||
// create the zip file
|
// create the zip file
|
||||||
Path zipFile = Paths.get("split_documents.zip");
|
Path zipFile = Paths.get("split_documents.zip");
|
||||||
URI uri = URI.create("jar:file:" + zipFile.toUri().getPath());
|
URI uri = URI.create("jar:file:" + zipFile.toUri().getPath());
|
||||||
Map<String, String> env = new HashMap<>();
|
Map<String, String> env = new HashMap<>();
|
||||||
env.put("create", "true");
|
env.put("create", "true");
|
||||||
FileSystem zipfs = FileSystems.newFileSystem(uri, env);
|
FileSystem zipfs = FileSystems.newFileSystem(uri, env);
|
||||||
|
|
||||||
// loop through the split documents and write them to the zip file
|
// loop through the split documents and write them to the zip file
|
||||||
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
||||||
String fileName = "split_document_" + (i + 1) + ".pdf";
|
String fileName = "split_document_" + (i + 1) + ".pdf";
|
||||||
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
|
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
|
||||||
byte[] pdf = baos.toByteArray();
|
byte[] pdf = baos.toByteArray();
|
||||||
Path pathInZipfile = zipfs.getPath(fileName);
|
Path pathInZipfile = zipfs.getPath(fileName);
|
||||||
try (OutputStream os = Files.newOutputStream(pathInZipfile)) {
|
try (OutputStream os = Files.newOutputStream(pathInZipfile)) {
|
||||||
os.write(pdf);
|
os.write(pdf);
|
||||||
logger.info("Wrote split document {} to zip file", fileName);
|
logger.info("Wrote split document {} to zip file", fileName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Failed writing to zip", e);
|
logger.error("Failed writing to zip", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
zipfs.close();
|
zipfs.close();
|
||||||
logger.info("Successfully created zip file with split documents: {}", zipFile.toString());
|
logger.info("Successfully created zip file with split documents: {}", zipFile.toString());
|
||||||
byte[] data = Files.readAllBytes(zipFile);
|
byte[] data = Files.readAllBytes(zipFile);
|
||||||
ByteArrayResource resource = new ByteArrayResource(data);
|
ByteArrayResource resource = new ByteArrayResource(data);
|
||||||
new File("split_documents.zip").delete();
|
new File("split_documents.zip").delete();
|
||||||
// return the Resource in the response
|
// return the Resource in the response
|
||||||
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=split_documents.zip")
|
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=split_documents.zip").contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
.contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource);
|
.contentLength(resource.contentLength()).body(resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,11 @@ package stirling.software.SPDF.controller.converters;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.rendering.ImageType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.core.io.ByteArrayResource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
@ -20,51 +23,75 @@ import stirling.software.SPDF.utils.PdfUtils;
|
||||||
@Controller
|
@Controller
|
||||||
public class ConvertImgPDFController {
|
public class ConvertImgPDFController {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class);
|
private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class);
|
||||||
|
|
||||||
@GetMapping("/img-to-pdf")
|
@GetMapping("/img-to-pdf")
|
||||||
public String convertToPdfForm(Model model) {
|
public String convertToPdfForm(Model model) {
|
||||||
model.addAttribute("currentPage", "img-to-pdf");
|
model.addAttribute("currentPage", "img-to-pdf");
|
||||||
return "convert/img-to-pdf";
|
return "convert/img-to-pdf";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/pdf-to-img")
|
@GetMapping("/pdf-to-img")
|
||||||
public String pdfToimgForm(Model model) {
|
public String pdfToimgForm(Model model) {
|
||||||
model.addAttribute("currentPage", "pdf-to-img");
|
model.addAttribute("currentPage", "pdf-to-img");
|
||||||
return "convert/pdf-to-img";
|
return "convert/pdf-to-img";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/img-to-pdf")
|
@PostMapping("/img-to-pdf")
|
||||||
public ResponseEntity<byte[]> convertToPdf(@RequestParam("fileInput") MultipartFile file) throws IOException {
|
public ResponseEntity<byte[]> convertToPdf(@RequestParam("fileInput") MultipartFile file) throws IOException {
|
||||||
// Convert the file to PDF and get the resulting bytes
|
// Convert the file to PDF and get the resulting bytes
|
||||||
byte[] bytes = PdfUtils.convertToPdf(file.getInputStream());
|
byte[] bytes = PdfUtils.convertToPdf(file.getInputStream());
|
||||||
logger.info("File {} successfully converted to pdf", file.getOriginalFilename());
|
logger.info("File {} successfully converted to pdf", file.getOriginalFilename());
|
||||||
|
|
||||||
return PdfUtils.bytesToWebResponse(bytes, file.getName() + "_coverted.pdf");
|
return PdfUtils.bytesToWebResponse(bytes, file.getName() + "_coverted.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/pdf-to-img")
|
@PostMapping("/pdf-to-img")
|
||||||
public ResponseEntity<byte[]> convertToImage(@RequestParam("fileInput") MultipartFile file,
|
public ResponseEntity<Resource> convertToImage(@RequestParam("fileInput") MultipartFile file, @RequestParam("imageFormat") String imageFormat,
|
||||||
@RequestParam("imageFormat") String imageFormat) throws IOException {
|
@RequestParam("singleOrMultiple") String singleOrMultiple, @RequestParam("colorType") String colorType, @RequestParam("dpi") String dpi) throws IOException {
|
||||||
byte[] pdfBytes = file.getBytes();
|
|
||||||
// returns bytes for image
|
|
||||||
byte[] result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toLowerCase());
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat)));
|
|
||||||
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
|
|
||||||
ResponseEntity<byte[]> response = new ResponseEntity<>(result, headers, HttpStatus.OK);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getMediaType(String imageFormat) {
|
byte[] pdfBytes = file.getBytes();
|
||||||
if (imageFormat.equalsIgnoreCase("PNG"))
|
ImageType colorTypeResult = ImageType.RGB;
|
||||||
return "image/png";
|
if ("greyscale".equals(colorType)) {
|
||||||
else if (imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG"))
|
colorTypeResult = ImageType.GRAY;
|
||||||
return "image/jpeg";
|
} else if ("blackwhite".equals(colorType)) {
|
||||||
else if (imageFormat.equalsIgnoreCase("GIF"))
|
colorTypeResult = ImageType.BINARY;
|
||||||
return "image/gif";
|
}
|
||||||
else
|
// returns bytes for image
|
||||||
return "application/octet-stream";
|
boolean singleImage = singleOrMultiple.equals("single");
|
||||||
}
|
byte[] result = null;
|
||||||
|
try {
|
||||||
|
result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toLowerCase(), colorTypeResult, singleImage, Integer.valueOf(dpi));
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (singleImage) {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat)));
|
||||||
|
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
|
||||||
|
ResponseEntity<Resource> response = new ResponseEntity<>(new ByteArrayResource(result), headers, HttpStatus.OK);
|
||||||
|
return response;
|
||||||
|
} else {
|
||||||
|
ByteArrayResource resource = new ByteArrayResource(result);
|
||||||
|
// return the Resource in the response
|
||||||
|
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=converted_documents.zip").contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
.contentLength(resource.contentLength()).body(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMediaType(String imageFormat) {
|
||||||
|
if (imageFormat.equalsIgnoreCase("PNG"))
|
||||||
|
return "image/png";
|
||||||
|
else if (imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG"))
|
||||||
|
return "image/jpeg";
|
||||||
|
else if (imageFormat.equalsIgnoreCase("GIF"))
|
||||||
|
return "image/gif";
|
||||||
|
else
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
package stirling.software.SPDF.controller.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
|
||||||
|
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 MetadataController {
|
||||||
|
|
||||||
|
@GetMapping("/change-metadata")
|
||||||
|
public String addWatermarkForm(Model model) {
|
||||||
|
model.addAttribute("currentPage", "change-metadata");
|
||||||
|
return "security/change-metadata";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String checkUndefined(String entry) {
|
||||||
|
// Check if the string is "undefined"
|
||||||
|
if("undefined".equals(entry)) {
|
||||||
|
// Return null if it is
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Return the original string if it's not "undefined"
|
||||||
|
return entry;
|
||||||
|
|
||||||
|
}
|
||||||
|
@PostMapping("/update-metadata")
|
||||||
|
public ResponseEntity<byte[]> metadata(@RequestParam("fileInput") MultipartFile pdfFile,
|
||||||
|
@RequestParam(value = "deleteAll", required = false, defaultValue = "false") Boolean deleteAll, @RequestParam(value = "author", required = false) String author,
|
||||||
|
@RequestParam(value = "creationDate", required = false) String creationDate, @RequestParam(value = "creator", required = false) String creator,
|
||||||
|
@RequestParam(value = "keywords", required = false) String keywords, @RequestParam(value = "modificationDate", required = false) String modificationDate,
|
||||||
|
@RequestParam(value = "producer", required = false) String producer, @RequestParam(value = "subject", required = false) String subject,
|
||||||
|
@RequestParam(value = "title", required = false) String title, @RequestParam(value = "trapped", required = false) String trapped,
|
||||||
|
@RequestParam Map<String, String> allRequestParams) throws IOException {
|
||||||
|
|
||||||
|
// Load the PDF file into a PDDocument
|
||||||
|
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
||||||
|
|
||||||
|
// Get the document information from the PDF
|
||||||
|
PDDocumentInformation info = document.getDocumentInformation();
|
||||||
|
|
||||||
|
// Check if each metadata value is "undefined" and set it to null if it is
|
||||||
|
author = checkUndefined(author);
|
||||||
|
creationDate = checkUndefined(creationDate);
|
||||||
|
creator = checkUndefined(creator);
|
||||||
|
keywords = checkUndefined(keywords);
|
||||||
|
modificationDate = checkUndefined(modificationDate);
|
||||||
|
producer = checkUndefined(producer);
|
||||||
|
subject = checkUndefined(subject);
|
||||||
|
title = checkUndefined(title);
|
||||||
|
trapped = checkUndefined(trapped);
|
||||||
|
|
||||||
|
// If the "deleteAll" flag is set, remove all metadata from the document information
|
||||||
|
if (deleteAll) {
|
||||||
|
for (String key : info.getMetadataKeys()) {
|
||||||
|
info.setCustomMetadataValue(key, null);
|
||||||
|
}
|
||||||
|
author = null;
|
||||||
|
creationDate = null;
|
||||||
|
creator = null;
|
||||||
|
keywords = null;
|
||||||
|
modificationDate = null;
|
||||||
|
producer = null;
|
||||||
|
subject = null;
|
||||||
|
title = null;
|
||||||
|
trapped = null;
|
||||||
|
} else {
|
||||||
|
// Iterate through the request parameters and set the metadata values
|
||||||
|
for (Entry<String, String> entry : allRequestParams.entrySet()) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
// Check if the key is a standard metadata key
|
||||||
|
if (!key.equalsIgnoreCase("Author") && !key.equalsIgnoreCase("CreationDate") && !key.equalsIgnoreCase("Creator") && !key.equalsIgnoreCase("Keywords")
|
||||||
|
&& !key.equalsIgnoreCase("modificationDate") && !key.equalsIgnoreCase("Producer") && !key.equalsIgnoreCase("Subject") && !key.equalsIgnoreCase("Title")
|
||||||
|
&& !key.equalsIgnoreCase("Trapped") && !key.contains("customKey") && !key.contains("customValue")) {
|
||||||
|
info.setCustomMetadataValue(key, entry.getValue());
|
||||||
|
} else if (key.contains("customKey")) {
|
||||||
|
int number = Integer.parseInt(key.replaceAll("\\D", ""));
|
||||||
|
String customKey = entry.getValue();
|
||||||
|
String customValue = allRequestParams.get("customValue" + number);
|
||||||
|
info.setCustomMetadataValue(customKey, customValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (creationDate != null && creationDate.length() > 0) {
|
||||||
|
Calendar creationDateCal = Calendar.getInstance();
|
||||||
|
try {
|
||||||
|
creationDateCal.setTime(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(creationDate));
|
||||||
|
} catch (ParseException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
info.setCreationDate(creationDateCal);
|
||||||
|
} else {
|
||||||
|
info.setCreationDate(null);
|
||||||
|
}
|
||||||
|
if (modificationDate != null && modificationDate.length() > 0) {
|
||||||
|
Calendar modificationDateCal = Calendar.getInstance();
|
||||||
|
try {
|
||||||
|
modificationDateCal.setTime(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(modificationDate));
|
||||||
|
} catch (ParseException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
info.setModificationDate(modificationDateCal);
|
||||||
|
} else {
|
||||||
|
info.setModificationDate(null);
|
||||||
|
}
|
||||||
|
info.setCreator(creator);
|
||||||
|
info.setKeywords(keywords);
|
||||||
|
info.setAuthor(author);
|
||||||
|
info.setProducer(producer);
|
||||||
|
info.setSubject(subject);
|
||||||
|
info.setTitle(title);
|
||||||
|
info.setTrapped(trapped);
|
||||||
|
|
||||||
|
document.setDocumentInformation(info);
|
||||||
|
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_metadata.pdf");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,18 +1,12 @@
|
||||||
package stirling.software.SPDF.controller.security;
|
package stirling.software.SPDF.controller.security;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
|
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
|
||||||
import org.apache.pdfbox.pdmodel.encryption.StandardDecryptionMaterial;
|
|
||||||
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
|
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
|
@ -26,67 +20,62 @@ import stirling.software.SPDF.utils.PdfUtils;
|
||||||
@Controller
|
@Controller
|
||||||
public class PasswordController {
|
public class PasswordController {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(PasswordController.class);
|
private static final Logger logger = LoggerFactory.getLogger(PasswordController.class);
|
||||||
|
|
||||||
@GetMapping("/add-password")
|
@GetMapping("/add-password")
|
||||||
public String addPasswordForm(Model model) {
|
public String addPasswordForm(Model model) {
|
||||||
model.addAttribute("currentPage", "add-password");
|
model.addAttribute("currentPage", "add-password");
|
||||||
return "security/add-password";
|
return "security/add-password";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/remove-password")
|
@GetMapping("/remove-password")
|
||||||
public String removePasswordForm(Model model) {
|
public String removePasswordForm(Model model) {
|
||||||
model.addAttribute("currentPage", "remove-password");
|
model.addAttribute("currentPage", "remove-password");
|
||||||
return "security/remove-password";
|
return "security/remove-password";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/change-permissions")
|
@GetMapping("/change-permissions")
|
||||||
public String permissionsForm(Model model) {
|
public String permissionsForm(Model model) {
|
||||||
model.addAttribute("currentPage", "change-permissions");
|
model.addAttribute("currentPage", "change-permissions");
|
||||||
return "security/change-permissions";
|
return "security/change-permissions";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/remove-password")
|
@PostMapping("/remove-password")
|
||||||
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile fileInput,
|
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile fileInput, @RequestParam(name = "password") String password) throws IOException {
|
||||||
@RequestParam(name = "password") String password) throws IOException {
|
PDDocument document = PDDocument.load(fileInput.getBytes(), password);
|
||||||
PDDocument document = PDDocument.load(fileInput.getBytes(), password);
|
document.setAllSecurityToBeRemoved(true);
|
||||||
document.setAllSecurityToBeRemoved(true);
|
return PdfUtils.pdfDocToWebResponse(document, fileInput.getName() + "_password_removed.pdf");
|
||||||
return PdfUtils.pdfDocToWebResponse(document, fileInput.getName() + "_password_removed.pdf");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/add-password")
|
@PostMapping("/add-password")
|
||||||
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile fileInput,
|
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile fileInput, @RequestParam(defaultValue = "", name = "password") String password,
|
||||||
@RequestParam(defaultValue = "", name = "password") String password,
|
@RequestParam(defaultValue = "128", name = "keyLength") int keyLength, @RequestParam(defaultValue = "false", name = "canAssembleDocument") boolean canAssembleDocument,
|
||||||
@RequestParam(defaultValue = "128", name = "keyLength") int keyLength,
|
@RequestParam(defaultValue = "false", name = "canExtractContent") boolean canExtractContent,
|
||||||
@RequestParam(defaultValue = "false", name = "canAssembleDocument") boolean canAssembleDocument,
|
@RequestParam(defaultValue = "false", name = "canExtractForAccessibility") boolean canExtractForAccessibility,
|
||||||
@RequestParam(defaultValue = "false", name = "canExtractContent") boolean canExtractContent,
|
@RequestParam(defaultValue = "false", name = "canFillInForm") boolean canFillInForm, @RequestParam(defaultValue = "false", name = "canModify") boolean canModify,
|
||||||
@RequestParam(defaultValue = "false", name = "canExtractForAccessibility") boolean canExtractForAccessibility,
|
@RequestParam(defaultValue = "false", name = "canModifyAnnotations") boolean canModifyAnnotations,
|
||||||
@RequestParam(defaultValue = "false", name = "canFillInForm") boolean canFillInForm,
|
@RequestParam(defaultValue = "false", name = "canPrint") boolean canPrint, @RequestParam(defaultValue = "false", name = "canPrintFaithful") boolean canPrintFaithful)
|
||||||
@RequestParam(defaultValue = "false", name = "canModify") boolean canModify,
|
throws IOException {
|
||||||
@RequestParam(defaultValue = "false", name = "canModifyAnnotations") boolean canModifyAnnotations,
|
|
||||||
@RequestParam(defaultValue = "false", name = "canPrint") boolean canPrint,
|
|
||||||
@RequestParam(defaultValue = "false", name = "canPrintFaithful") boolean canPrintFaithful)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
PDDocument document = PDDocument.load(fileInput.getBytes());
|
PDDocument document = PDDocument.load(fileInput.getBytes());
|
||||||
AccessPermission ap = new AccessPermission();
|
AccessPermission ap = new AccessPermission();
|
||||||
|
|
||||||
ap.setCanAssembleDocument(!canAssembleDocument);
|
ap.setCanAssembleDocument(!canAssembleDocument);
|
||||||
ap.setCanExtractContent(!canExtractContent);
|
ap.setCanExtractContent(!canExtractContent);
|
||||||
ap.setCanExtractForAccessibility(!canExtractForAccessibility);
|
ap.setCanExtractForAccessibility(!canExtractForAccessibility);
|
||||||
ap.setCanFillInForm(!canFillInForm);
|
ap.setCanFillInForm(!canFillInForm);
|
||||||
ap.setCanModify(!canModify);
|
ap.setCanModify(!canModify);
|
||||||
ap.setCanModifyAnnotations(!canModifyAnnotations);
|
ap.setCanModifyAnnotations(!canModifyAnnotations);
|
||||||
ap.setCanPrint(!canPrint);
|
ap.setCanPrint(!canPrint);
|
||||||
ap.setCanPrintFaithful(!canPrintFaithful);
|
ap.setCanPrintFaithful(!canPrintFaithful);
|
||||||
StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap);
|
StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap);
|
||||||
spp.setEncryptionKeyLength(keyLength);
|
spp.setEncryptionKeyLength(keyLength);
|
||||||
|
|
||||||
spp.setPermissions(ap);
|
spp.setPermissions(ap);
|
||||||
|
|
||||||
document.protect(spp);
|
document.protect(spp);
|
||||||
|
|
||||||
return PdfUtils.pdfDocToWebResponse(document, fileInput.getName() + "_passworded.pdf");
|
return PdfUtils.pdfDocToWebResponse(document, fileInput.getName() + "_passworded.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package stirling.software.SPDF.controller.security;
|
package stirling.software.SPDF.controller.security;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
@ -9,11 +8,7 @@ import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
import org.apache.pdfbox.pdmodel.font.PDFont;
|
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
|
|
||||||
import org.apache.pdfbox.util.Matrix;
|
import org.apache.pdfbox.util.Matrix;
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
|
@ -27,57 +22,53 @@ import stirling.software.SPDF.utils.PdfUtils;
|
||||||
@Controller
|
@Controller
|
||||||
public class WatermarkController {
|
public class WatermarkController {
|
||||||
|
|
||||||
@GetMapping("/add-watermark")
|
@GetMapping("/add-watermark")
|
||||||
public String addWatermarkForm(Model model) {
|
public String addWatermarkForm(Model model) {
|
||||||
model.addAttribute("currentPage", "add-watermark");
|
model.addAttribute("currentPage", "add-watermark");
|
||||||
return "security/add-watermark";
|
return "security/add-watermark";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/add-watermark")
|
@PostMapping("/add-watermark")
|
||||||
public ResponseEntity<byte[]> addWatermark(@RequestParam("fileInput") MultipartFile pdfFile,
|
public ResponseEntity<byte[]> addWatermark(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText,
|
||||||
@RequestParam("watermarkText") String watermarkText,
|
@RequestParam(defaultValue = "30", name = "fontSize") float fontSize, @RequestParam(defaultValue = "0", name = "rotation") float rotation,
|
||||||
@RequestParam(defaultValue = "30", name = "fontSize") float fontSize,
|
@RequestParam(defaultValue = "50", name = "widthSpacer") int widthSpacer, @RequestParam(defaultValue = "50", name = "heightSpacer") int heightSpacer)
|
||||||
@RequestParam(defaultValue = "0", name = "rotation") float rotation,
|
throws IOException {
|
||||||
@RequestParam(defaultValue = "50", name = "widthSpacer") int widthSpacer,
|
|
||||||
@RequestParam(defaultValue = "50", name = "heightSpacer") int heightSpacer) throws IOException {
|
|
||||||
|
|
||||||
// Load the input PDF
|
// Load the input PDF
|
||||||
PDDocument document = PDDocument.load(pdfFile.getInputStream());
|
PDDocument document = PDDocument.load(pdfFile.getInputStream());
|
||||||
|
|
||||||
// Create a page in the document
|
// Create a page in the document
|
||||||
for (PDPage page : document.getPages()) {
|
for (PDPage page : document.getPages()) {
|
||||||
// Get the page's content stream
|
// Get the page's content stream
|
||||||
PDPageContentStream contentStream = new PDPageContentStream(document, page,
|
PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);
|
||||||
PDPageContentStream.AppendMode.APPEND, true);
|
|
||||||
|
|
||||||
// Set font of watermark
|
// Set font of watermark
|
||||||
PDFont font = PDType1Font.HELVETICA_BOLD;
|
PDFont font = PDType1Font.HELVETICA_BOLD;
|
||||||
contentStream.beginText();
|
contentStream.beginText();
|
||||||
contentStream.setFont(font, fontSize);
|
contentStream.setFont(font, fontSize);
|
||||||
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
|
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
|
||||||
|
|
||||||
// Set size and location of watermark
|
// Set size and location of watermark
|
||||||
float pageWidth = page.getMediaBox().getWidth();
|
float pageWidth = page.getMediaBox().getWidth();
|
||||||
float pageHeight = page.getMediaBox().getHeight();
|
float pageHeight = page.getMediaBox().getHeight();
|
||||||
float watermarkWidth = widthSpacer + font.getStringWidth(watermarkText) * fontSize / 1000;
|
float watermarkWidth = widthSpacer + font.getStringWidth(watermarkText) * fontSize / 1000;
|
||||||
float watermarkHeight = heightSpacer + fontSize;
|
float watermarkHeight = heightSpacer + fontSize;
|
||||||
int watermarkRows = (int) (pageHeight / watermarkHeight + 1);
|
int watermarkRows = (int) (pageHeight / watermarkHeight + 1);
|
||||||
int watermarkCols = (int) (pageWidth / watermarkWidth + 1);
|
int watermarkCols = (int) (pageWidth / watermarkWidth + 1);
|
||||||
|
|
||||||
// Add the watermark text
|
// Add the watermark text
|
||||||
for (int i = 0; i < watermarkRows; i++) {
|
for (int i = 0; i < watermarkRows; i++) {
|
||||||
for (int j = 0; j < watermarkCols; j++) {
|
for (int j = 0; j < watermarkCols; j++) {
|
||||||
contentStream.setTextMatrix(Matrix.getRotateInstance((float) Math.toRadians(rotation),
|
contentStream.setTextMatrix(Matrix.getRotateInstance((float) Math.toRadians(rotation), j * watermarkWidth, i * watermarkHeight));
|
||||||
j * watermarkWidth, i * watermarkHeight));
|
contentStream.showTextWithPositioning(new Object[] { watermarkText });
|
||||||
contentStream.showTextWithPositioning(new Object[] { watermarkText });
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
contentStream.endText();
|
contentStream.endText();
|
||||||
|
|
||||||
// Close the content stream
|
// Close the content stream
|
||||||
contentStream.close();
|
contentStream.close();
|
||||||
}
|
}
|
||||||
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_watermarked.pdf");
|
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_watermarked.pdf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package stirling.software.SPDF.utils;
|
package stirling.software.SPDF.utils;
|
||||||
|
|
||||||
|
import java.awt.Graphics;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
@ -7,12 +8,13 @@ import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Iterator;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import javax.imageio.IIOImage;
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.ImageWriter;
|
|
||||||
import javax.imageio.stream.ImageOutputStream;
|
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
|
@ -31,138 +33,164 @@ import com.spire.pdf.PdfDocument;
|
||||||
|
|
||||||
public class PdfUtils {
|
public class PdfUtils {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
|
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
|
||||||
|
|
||||||
public static byte[] convertToPdf(InputStream imageStream) throws IOException {
|
public static byte[] convertToPdf(InputStream imageStream) throws IOException {
|
||||||
|
|
||||||
// Create a File object for the image
|
// Create a File object for the image
|
||||||
File imageFile = new File("image.jpg");
|
File imageFile = new File("image.jpg");
|
||||||
|
|
||||||
try (FileOutputStream fos = new FileOutputStream(imageFile); InputStream input = imageStream) {
|
try (FileOutputStream fos = new FileOutputStream(imageFile); InputStream input = imageStream) {
|
||||||
byte[] buffer = new byte[1024];
|
byte[] buffer = new byte[1024];
|
||||||
int len;
|
int len;
|
||||||
// Read from the input stream and write to the file
|
// Read from the input stream and write to the file
|
||||||
while ((len = input.read(buffer)) != -1) {
|
while ((len = input.read(buffer)) != -1) {
|
||||||
fos.write(buffer, 0, len);
|
fos.write(buffer, 0, len);
|
||||||
}
|
}
|
||||||
logger.info("Image successfully written to file: {}", imageFile.getAbsolutePath());
|
logger.info("Image successfully written to file: {}", imageFile.getAbsolutePath());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Error writing image to file: {}", imageFile.getAbsolutePath(), e);
|
logger.error("Error writing image to file: {}", imageFile.getAbsolutePath(), e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (PDDocument doc = new PDDocument()) {
|
try (PDDocument doc = new PDDocument()) {
|
||||||
// Create a new PDF page
|
// Create a new PDF page
|
||||||
PDPage page = new PDPage();
|
PDPage page = new PDPage();
|
||||||
doc.addPage(page);
|
doc.addPage(page);
|
||||||
|
|
||||||
// Create an image object from the image file
|
// Create an image object from the image file
|
||||||
PDImageXObject image = PDImageXObject.createFromFileByContent(imageFile, doc);
|
PDImageXObject image = PDImageXObject.createFromFileByContent(imageFile, doc);
|
||||||
|
|
||||||
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
|
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
|
||||||
// Draw the image onto the page
|
// Draw the image onto the page
|
||||||
contentStream.drawImage(image, 0, 0);
|
contentStream.drawImage(image, 0, 0);
|
||||||
logger.info("Image successfully added to PDF");
|
logger.info("Image successfully added to PDF");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Error adding image to PDF", e);
|
logger.error("Error adding image to PDF", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a ByteArrayOutputStream to save the PDF to
|
// Create a ByteArrayOutputStream to save the PDF to
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
doc.save(byteArrayOutputStream);
|
doc.save(byteArrayOutputStream);
|
||||||
logger.info("PDF successfully saved to byte array");
|
logger.info("PDF successfully saved to byte array");
|
||||||
return byteArrayOutputStream.toByteArray();
|
return byteArrayOutputStream.toByteArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] convertFromPdf(byte[] inputStream, String imageType) throws IOException {
|
public static byte[] convertFromPdf(byte[] inputStream, String imageType, ImageType colorType, boolean singleImage, int DPI)
|
||||||
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
|
throws IOException, Exception {
|
||||||
// Create a PDFRenderer to convert the PDF to an image
|
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
BufferedImage bim = pdfRenderer.renderImageWithDPI(0, 300, ImageType.RGB);
|
int pageCount = document.getNumberOfPages();
|
||||||
|
List<BufferedImage> images = new ArrayList<>();
|
||||||
|
|
||||||
// Get an ImageWriter for the specified image type
|
// Create images of all pages
|
||||||
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(imageType);
|
for (int i = 0; i < pageCount; i++) {
|
||||||
ImageWriter writer = writers.next();
|
images.add(pdfRenderer.renderImageWithDPI(i, 300, colorType));
|
||||||
|
}
|
||||||
|
|
||||||
// Create a ByteArrayOutputStream to save the image to
|
if (singleImage) {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
// Combine all images into a single big image
|
||||||
try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) {
|
BufferedImage combined = new BufferedImage(images.get(0).getWidth(), images.get(0).getHeight() * pageCount, BufferedImage.TYPE_INT_RGB);
|
||||||
writer.setOutput(ios);
|
Graphics g = combined.getGraphics();
|
||||||
// Write the image to the output stream
|
for (int i = 0; i < images.size(); i++) {
|
||||||
writer.write(new IIOImage(bim, null, null));
|
g.drawImage(images.get(i), 0, i * images.get(0).getHeight(), null);
|
||||||
// Log that the image was successfully written to the byte array
|
}
|
||||||
logger.info("Image successfully written to byte array");
|
images = Arrays.asList(combined);
|
||||||
}
|
}
|
||||||
return baos.toByteArray();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Log an error message if there is an issue converting the PDF to an image
|
|
||||||
logger.error("Error converting PDF to image", e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] overlayImage(byte[] pdfBytes, byte[] imageBytes, float x, float y) throws IOException {
|
// Create a ByteArrayOutputStream to save the image(s) to
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
if (singleImage) {
|
||||||
|
// Write the image to the output stream
|
||||||
|
ImageIO.write(images.get(0), "PNG", baos);
|
||||||
|
|
||||||
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes))) {
|
// Log that the image was successfully written to the byte array
|
||||||
// Get the first page of the PDF
|
logger.info("Image successfully written to byte array");
|
||||||
PDPage page = document.getPage(0);
|
} else {
|
||||||
try (PDPageContentStream contentStream = new PDPageContentStream(document, page,
|
// Zip the images and return as byte array
|
||||||
PDPageContentStream.AppendMode.APPEND, true)) {
|
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
|
||||||
// Create an image object from the image bytes
|
for (int i = 0; i < images.size(); i++) {
|
||||||
PDImageXObject image = PDImageXObject.createFromByteArray(document, imageBytes, "");
|
BufferedImage image = images.get(i);
|
||||||
// Draw the image onto the page at the specified x and y coordinates
|
try (ByteArrayOutputStream baosImage = new ByteArrayOutputStream()) {
|
||||||
contentStream.drawImage(image, x, y);
|
ImageIO.write(image, "PNG", baosImage);
|
||||||
logger.info("Image successfully overlayed onto PDF");
|
|
||||||
}
|
|
||||||
// Create a ByteArrayOutputStream to save the PDF to
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
document.save(baos);
|
|
||||||
logger.info("PDF successfully saved to byte array");
|
|
||||||
return baos.toByteArray();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Log an error message if there is an issue overlaying the image onto the PDF
|
|
||||||
logger.error("Error overlaying image onto PDF", e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> pdfDocToWebResponse(PdfDocument document, String docName) throws IOException {
|
// Add the image to the zip file
|
||||||
|
zos.putNextEntry(new ZipEntry(String.format("page_%d.%s", i + 1, "png")));
|
||||||
|
zos.write(baosImage.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Log that the images were successfully written to the byte array
|
||||||
|
logger.info("Images successfully written to byte array as a zip");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return baos.toByteArray();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Log an error message if there is an issue converting the PDF to an image
|
||||||
|
logger.error("Error converting PDF to image", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Open Byte Array and save document to it
|
public static byte[] overlayImage(byte[] pdfBytes, byte[] imageBytes, float x, float y) throws IOException {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
document.saveToStream(baos);
|
|
||||||
// Close the document
|
|
||||||
document.close();
|
|
||||||
|
|
||||||
return PdfUtils.boasToWebResponse(baos, docName);
|
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes))) {
|
||||||
}
|
// Get the first page of the PDF
|
||||||
|
PDPage page = document.getPage(0);
|
||||||
|
try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true)) {
|
||||||
|
// Create an image object from the image bytes
|
||||||
|
PDImageXObject image = PDImageXObject.createFromByteArray(document, imageBytes, "");
|
||||||
|
// Draw the image onto the page at the specified x and y coordinates
|
||||||
|
contentStream.drawImage(image, x, y);
|
||||||
|
logger.info("Image successfully overlayed onto PDF");
|
||||||
|
}
|
||||||
|
// Create a ByteArrayOutputStream to save the PDF to
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
document.save(baos);
|
||||||
|
logger.info("PDF successfully saved to byte array");
|
||||||
|
return baos.toByteArray();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Log an error message if there is an issue overlaying the image onto the PDF
|
||||||
|
logger.error("Error overlaying image onto PDF", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> pdfDocToWebResponse(PDDocument document, String docName) throws IOException {
|
public static ResponseEntity<byte[]> pdfDocToWebResponse(PdfDocument document, String docName) throws IOException {
|
||||||
|
|
||||||
// Open Byte Array and save document to it
|
// Open Byte Array and save document to it
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
document.save(baos);
|
document.saveToStream(baos);
|
||||||
// Close the document
|
// Close the document
|
||||||
document.close();
|
document.close();
|
||||||
|
|
||||||
return PdfUtils.boasToWebResponse(baos, docName);
|
return PdfUtils.boasToWebResponse(baos, docName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName)
|
public static ResponseEntity<byte[]> pdfDocToWebResponse(PDDocument document, String docName) throws IOException {
|
||||||
throws IOException {
|
|
||||||
return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName);
|
|
||||||
|
|
||||||
}
|
// Open Byte Array and save document to it
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
document.save(baos);
|
||||||
|
// Close the document
|
||||||
|
document.close();
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName) throws IOException {
|
return PdfUtils.boasToWebResponse(baos, docName);
|
||||||
|
}
|
||||||
|
|
||||||
// Return the PDF as a response
|
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName) throws IOException {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName);
|
||||||
headers.setContentType(MediaType.APPLICATION_PDF);
|
|
||||||
headers.setContentLength(bytes.length);
|
}
|
||||||
headers.setContentDispositionFormData("attachment", docName);
|
|
||||||
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
|
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName) throws IOException {
|
||||||
}
|
|
||||||
|
// Return the PDF as a response
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_PDF);
|
||||||
|
headers.setContentLength(bytes.length);
|
||||||
|
headers.setContentDispositionFormData("attachment", docName);
|
||||||
|
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,10 @@ genericSubmit=إرسال
|
||||||
processTimeWarning=تحذير: يمكن أن تستغرق هذه العملية ما يصل إلى دقيقة حسب حجم الملف
|
processTimeWarning=تحذير: يمكن أن تستغرق هذه العملية ما يصل إلى دقيقة حسب حجم الملف
|
||||||
pageOrderPrompt=ترتيب الصفحات (أدخل قائمة بأرقام الصفحات مفصولة بفواصل):
|
pageOrderPrompt=ترتيب الصفحات (أدخل قائمة بأرقام الصفحات مفصولة بفواصل):
|
||||||
goToPage=اذهب
|
goToPage=اذهب
|
||||||
|
true=\u0635\u062D\u064A\u062D
|
||||||
|
false=\u062E\u0637\u0623
|
||||||
|
unknown=\u063A\u064A\u0631 \u0645\u0639\u0631\u0648\u0641
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
#############
|
#############
|
||||||
|
@ -65,7 +69,8 @@ home.removePassword.desc=إزالة الحماية بكلمة مرور من مس
|
||||||
home.compressPdfs.title=ضغط ملفات PDF
|
home.compressPdfs.title=ضغط ملفات PDF
|
||||||
home.compressPdfs.desc=ضغط ملفات PDF لتقليل حجم الملف.
|
home.compressPdfs.desc=ضغط ملفات PDF لتقليل حجم الملف.
|
||||||
|
|
||||||
|
home.changeMetadata.title = \u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
|
||||||
|
home.changeMetadata.desc = \u062A\u063A\u064A\u064A\u0631 / \u0625\u0632\u0627\u0644\u0629 / \u0625\u0636\u0627\u0641\u0629 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF
|
||||||
|
|
||||||
#Add image
|
#Add image
|
||||||
addImage.title=إضافة صورة
|
addImage.title=إضافة صورة
|
||||||
|
@ -129,6 +134,13 @@ imageToPDF.submit=تحول
|
||||||
pdfToImage.title=تحويل PDF إلى صورة
|
pdfToImage.title=تحويل PDF إلى صورة
|
||||||
pdfToImage.header=تحويل PDF إلى صورة
|
pdfToImage.header=تحويل PDF إلى صورة
|
||||||
pdfToImage.selectText=تنسيق الصورة
|
pdfToImage.selectText=تنسيق الصورة
|
||||||
|
pdfToImage.singleOrMultiple = \u0646\u0648\u0639 \u0646\u062A\u064A\u062C\u0629 \u0627\u0644\u0635\u0648\u0631\u0629
|
||||||
|
pdfToImage.single = \u0635\u0648\u0631\u0629 \u0648\u0627\u062D\u062F\u0629 \u0643\u0628\u064A\u0631\u0629
|
||||||
|
pdfToImage.multi = \u0635\u0648\u0631 \u0645\u062A\u0639\u062F\u062F\u0629
|
||||||
|
pdfToImage.colorType = \u0646\u0648\u0639 \u0627\u0644\u0644\u0648\u0646
|
||||||
|
pdfToImage.color = \u0627\u0644\u0644\u0648\u0646
|
||||||
|
pdfToImage.grey = \u062A\u062F\u0631\u062C \u0627\u0644\u0631\u0645\u0627\u062F\u064A
|
||||||
|
pdfToImage.blackwhite = \u0623\u0628\u064A\u0636 \u0648\u0623\u0633\u0648\u062F (\u0642\u062F \u064A\u0641\u0642\u062F \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A!)
|
||||||
pdfToImage.submit=تحول
|
pdfToImage.submit=تحول
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
@ -183,3 +195,21 @@ removePassword.header=إزالة كلمة المرور (فك التشفير)
|
||||||
removePassword.selectText.1=حدد PDF لفك التشفير
|
removePassword.selectText.1=حدد PDF لفك التشفير
|
||||||
removePassword.selectText.2=كلمة المرور
|
removePassword.selectText.2=كلمة المرور
|
||||||
removePassword.submit=إزالة
|
removePassword.submit=إزالة
|
||||||
|
|
||||||
|
changeMetadata.title = \u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
|
||||||
|
changeMetadata.header = \u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
|
||||||
|
changeMetadata.selectText.1 = \u064A\u0631\u062C\u0649 \u062A\u0639\u062F\u064A\u0644 \u0627\u0644\u0645\u062A\u063A\u064A\u0631\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0631\u063A\u0628 \u0641\u064A \u062A\u063A\u064A\u064A\u0631\u0647\u0627
|
||||||
|
changeMetadata.selectText.2 = \u062D\u0630\u0641 \u0643\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629
|
||||||
|
changeMetadata.selectText.3 = \u0625\u0638\u0647\u0627\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629 \u0627\u0644\u0645\u062E\u0635\u0635\u0629:
|
||||||
|
changeMetadata.author = \u0627\u0644\u0645\u0624\u0644\u0641:
|
||||||
|
changeMetadata.creationDate = \u062A\u0627\u0631\u064A\u062E \u0627\u0644\u0625\u0646\u0634\u0627\u0621 (yyyy / MM / dd HH: mm: ss):
|
||||||
|
changeMetadata.creator = \u0627\u0644\u0645\u0646\u0634\u0626:
|
||||||
|
changeMetadata.keywords = \u0627\u0644\u0643\u0644\u0645\u0627\u062A \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629:
|
||||||
|
changeMetadata.modDate = \u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062A\u0639\u062F\u064A\u0644 (yyyy / MM / dd HH: mm: ss):
|
||||||
|
changeMetadata.producer = \u0627\u0644\u0645\u0646\u062A\u062C:
|
||||||
|
changeMetadata.subject = \u0627\u0644\u0645\u0648\u0636\u0648\u0639:
|
||||||
|
changeMetadata.title = \u0627\u0644\u0639\u0646\u0648\u0627\u0646:
|
||||||
|
changeMetadata.trapped = \u0645\u062D\u0627\u0635\u0631:
|
||||||
|
changeMetadata.selectText.4 = \u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0635\u0641\u064A\u0629 \u0623\u062E\u0631\u0649:
|
||||||
|
changeMetadata.selectText.5 = \u0625\u0636\u0627\u0641\u0629 \u0625\u062F\u062E\u0627\u0644 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u062E\u0635\u0635
|
||||||
|
changeMetadata.submit = \u062A\u063A\u064A\u064A\u0631
|
||||||
|
|
|
@ -12,6 +12,10 @@ genericSubmit=Einreichen
|
||||||
processTimeWarning=Achtung: Abhängig von der Dateigröße kann dieser Prozess bis zu einer Minute dauern
|
processTimeWarning=Achtung: Abhängig von der Dateigröße kann dieser Prozess bis zu einer Minute dauern
|
||||||
pageOrderPrompt=Seitenreihenfolge (Geben Sie eine durch Komma getrennte Liste von Seitenzahlen ein):
|
pageOrderPrompt=Seitenreihenfolge (Geben Sie eine durch Komma getrennte Liste von Seitenzahlen ein):
|
||||||
goToPage=Los
|
goToPage=Los
|
||||||
|
true=Wahr
|
||||||
|
false=Falsch
|
||||||
|
unknown=Unbekannt
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
#############
|
#############
|
||||||
|
@ -61,7 +65,8 @@ home.removePassword.desc=Den Passwortschutz eines PDFs entfernen.
|
||||||
home.compressPdfs.title=PDF komprimieren
|
home.compressPdfs.title=PDF komprimieren
|
||||||
home.compressPdfs.desc=PDF komprimieren um die Dateigröße zu reduzieren.
|
home.compressPdfs.desc=PDF komprimieren um die Dateigröße zu reduzieren.
|
||||||
|
|
||||||
|
home.changeMetadata.title=Metadaten ändern
|
||||||
|
home.changeMetadata.desc=Ändern/Entfernen/Hinzufügen von Metadaten aus einem PDF-Dokument
|
||||||
|
|
||||||
#Add image
|
#Add image
|
||||||
addImage.title=Bild hinzufügen
|
addImage.title=Bild hinzufügen
|
||||||
|
@ -126,6 +131,13 @@ imageToPDF.submit=Umwandeln
|
||||||
pdfToImage.title=PDF zu Bild
|
pdfToImage.title=PDF zu Bild
|
||||||
pdfToImage.header=PDF zu Bild
|
pdfToImage.header=PDF zu Bild
|
||||||
pdfToImage.selectText=Bildformat
|
pdfToImage.selectText=Bildformat
|
||||||
|
pdfToImage.singleOrMultiple=Bildergebnistyp
|
||||||
|
pdfToImage.single=Einzelnes großes Bild
|
||||||
|
pdfToImage.multi=Mehrere Bilder
|
||||||
|
pdfToImage.colorType=Farbtyp
|
||||||
|
pdfToImage.color=Farbe
|
||||||
|
pdfToImage.grey=Graustufen
|
||||||
|
pdfToImage.blackwhite=Schwarzweiß (Datenverlust möglich!)
|
||||||
pdfToImage.submit=Umwandeln
|
pdfToImage.submit=Umwandeln
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
@ -182,7 +194,23 @@ removePassword.selectText.2=Passwort
|
||||||
removePassword.submit=Entfernen
|
removePassword.submit=Entfernen
|
||||||
|
|
||||||
|
|
||||||
|
changeMetadata.title=Metadaten ändern
|
||||||
|
changeMetadata.header=Metadaten ändern
|
||||||
|
changeMetadata.selectText.1=Bitte bearbeiten Sie die Variablen, die Sie ändern möchten
|
||||||
|
changeMetadata.selectText.2=Alle Metadaten löschen
|
||||||
|
changeMetadata.selectText.3=Benutzerdefinierte Metadaten anzeigen:
|
||||||
|
changeMetadata.author=Autor:
|
||||||
|
changeMetadata.creationDate=Erstellungsdatum (jjjj/MM/tt HH:mm:ss):
|
||||||
|
changeMetadata.creator=Ersteller:
|
||||||
|
changeMetadata.keywords=Schlüsselwörter:
|
||||||
|
changeMetadata.modDate=Änderungsdatum (JJJJ/MM/TT HH:mm:ss):
|
||||||
|
changeMetadata.producer=Produzent:
|
||||||
|
changeMetadata.subject=Betreff:
|
||||||
|
changeMetadata.title=Titel:
|
||||||
|
changeMetadata.trapped=Gefangen:
|
||||||
|
changeMetadata.selectText.4=Andere Metadaten:
|
||||||
|
changeMetadata.selectText.5=Benutzerdefinierten Metadateneintrag hinzufügen
|
||||||
|
changeMetadata.submit=Ändern
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,10 @@ imgPrompt=Choose Image
|
||||||
genericSubmit=Submit
|
genericSubmit=Submit
|
||||||
processTimeWarning=Warning: This process can take up to a minute depending on file-size
|
processTimeWarning=Warning: This process can take up to a minute depending on file-size
|
||||||
pageOrderPrompt=Page Order (Enter a comma-separated list of page numbers) :
|
pageOrderPrompt=Page Order (Enter a comma-separated list of page numbers) :
|
||||||
goToPage=go
|
goToPage=Go
|
||||||
|
true=True
|
||||||
|
false=False
|
||||||
|
unknown=Unknown
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
#############
|
#############
|
||||||
|
@ -61,6 +64,8 @@ home.removePassword.desc=Remove password protection from your PDF document.
|
||||||
home.compressPdfs.title=Compress PDFs
|
home.compressPdfs.title=Compress PDFs
|
||||||
home.compressPdfs.desc=Compress PDFs to reduce their file size.
|
home.compressPdfs.desc=Compress PDFs to reduce their file size.
|
||||||
|
|
||||||
|
home.changeMetadata.title=Change Metadata
|
||||||
|
home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document
|
||||||
|
|
||||||
|
|
||||||
#Add image
|
#Add image
|
||||||
|
@ -125,6 +130,13 @@ imageToPDF.submit=Convert
|
||||||
pdfToImage.title=PDF to Image
|
pdfToImage.title=PDF to Image
|
||||||
pdfToImage.header=PDF to Image
|
pdfToImage.header=PDF to Image
|
||||||
pdfToImage.selectText=Image Format
|
pdfToImage.selectText=Image Format
|
||||||
|
pdfToImage.singleOrMultiple=Image result type
|
||||||
|
pdfToImage.single=Single Big Image
|
||||||
|
pdfToImage.multi=Multiple Images
|
||||||
|
pdfToImage.colorType=Colour type
|
||||||
|
pdfToImage.color=Colour
|
||||||
|
pdfToImage.grey=Greyscale
|
||||||
|
pdfToImage.blackwhite=Black and White (May lose data!)
|
||||||
pdfToImage.submit=Convert
|
pdfToImage.submit=Convert
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
@ -179,3 +191,48 @@ removePassword.header=Remove password (Decrypt)
|
||||||
removePassword.selectText.1=Select PDF to Decrypt
|
removePassword.selectText.1=Select PDF to Decrypt
|
||||||
removePassword.selectText.2=Password
|
removePassword.selectText.2=Password
|
||||||
removePassword.submit=Remove
|
removePassword.submit=Remove
|
||||||
|
|
||||||
|
changeMetadata.title=Change Metadata
|
||||||
|
changeMetadata.header=Change Metadata
|
||||||
|
changeMetadata.selectText.1=Please edit the variables you wish to change
|
||||||
|
changeMetadata.selectText.2=Delete all metadata
|
||||||
|
changeMetadata.selectText.3=Show Custom Metadata:
|
||||||
|
changeMetadata.author=Author:
|
||||||
|
changeMetadata.creationDate=Creation Date (yyyy/MM/dd HH:mm:ss):
|
||||||
|
changeMetadata.creator=Creator:
|
||||||
|
changeMetadata.keywords=Keywords:
|
||||||
|
changeMetadata.modDate=Modification Date (yyyy/MM/dd HH:mm:ss):
|
||||||
|
changeMetadata.producer=Producer:
|
||||||
|
changeMetadata.subject=Subject:
|
||||||
|
changeMetadata.title=Title:
|
||||||
|
changeMetadata.trapped=Trapped:
|
||||||
|
changeMetadata.selectText.4=Other Metadata:
|
||||||
|
changeMetadata.selectText.5=Add Custom Metadata Entry
|
||||||
|
changeMetadata.submit=Change
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,10 @@ imgPrompt=Choose Image
|
||||||
genericSubmit=Submit
|
genericSubmit=Submit
|
||||||
processTimeWarning=Warning: This process can take up to a minute depending on file-size
|
processTimeWarning=Warning: This process can take up to a minute depending on file-size
|
||||||
pageOrderPrompt=Page Order (Enter a comma-separated list of page numbers) :
|
pageOrderPrompt=Page Order (Enter a comma-separated list of page numbers) :
|
||||||
goToPage=go
|
goToPage=Go
|
||||||
|
true=True
|
||||||
|
false=False
|
||||||
|
unknown=Unknown
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
#############
|
#############
|
||||||
|
@ -61,6 +64,8 @@ home.removePassword.desc=Remove password protection from your PDF document.
|
||||||
home.compressPdfs.title=Compress PDFs
|
home.compressPdfs.title=Compress PDFs
|
||||||
home.compressPdfs.desc=Compress PDFs to reduce their file size.
|
home.compressPdfs.desc=Compress PDFs to reduce their file size.
|
||||||
|
|
||||||
|
home.changeMetadata.title=Change Metadata
|
||||||
|
home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document
|
||||||
|
|
||||||
|
|
||||||
#Add image
|
#Add image
|
||||||
|
@ -125,8 +130,16 @@ imageToPDF.submit=Convert
|
||||||
pdfToImage.title=PDF to Image
|
pdfToImage.title=PDF to Image
|
||||||
pdfToImage.header=PDF to Image
|
pdfToImage.header=PDF to Image
|
||||||
pdfToImage.selectText=Image Format
|
pdfToImage.selectText=Image Format
|
||||||
|
pdfToImage.singleOrMultiple=Image result type
|
||||||
|
pdfToImage.single=Single Big Image
|
||||||
|
pdfToImage.multi=Multiple Images
|
||||||
|
pdfToImage.colorType=Color type
|
||||||
|
pdfToImage.color=Color
|
||||||
|
pdfToImage.grey=Grayscale
|
||||||
|
pdfToImage.blackwhite=Black and White (May lose data!)
|
||||||
pdfToImage.submit=Convert
|
pdfToImage.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
addPassword.title=Add Password
|
addPassword.title=Add Password
|
||||||
addPassword.header=Add password (Encrypt)
|
addPassword.header=Add password (Encrypt)
|
||||||
|
@ -180,8 +193,23 @@ removePassword.selectText.1=Select PDF to Decrypt
|
||||||
removePassword.selectText.2=Password
|
removePassword.selectText.2=Password
|
||||||
removePassword.submit=Remove
|
removePassword.submit=Remove
|
||||||
|
|
||||||
|
changeMetadata.title=Change Metadata
|
||||||
|
changeMetadata.header=Change Metadata
|
||||||
|
changeMetadata.selectText.1=Please edit the variables you wish to change
|
||||||
|
changeMetadata.selectText.2=Delete all metadata
|
||||||
|
changeMetadata.selectText.3=Show Custom Metadata:
|
||||||
|
changeMetadata.author=Author:
|
||||||
|
changeMetadata.creationDate=Creation Date (yyyy/MM/dd HH:mm:ss):
|
||||||
|
changeMetadata.creator=Creator:
|
||||||
|
changeMetadata.keywords=Keywords:
|
||||||
|
changeMetadata.modDate=Modification Date (yyyy/MM/dd HH:mm:ss):
|
||||||
|
changeMetadata.producer=Producer:
|
||||||
|
changeMetadata.subject=Subject:
|
||||||
|
changeMetadata.title=Title:
|
||||||
|
changeMetadata.trapped=Trapped:
|
||||||
|
changeMetadata.selectText.4=Other Metadata:
|
||||||
|
changeMetadata.selectText.5=Add Custom Metadata Entry
|
||||||
|
changeMetadata.submit=Change
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,11 @@ imgPrompt=Choisir une image
|
||||||
genericSubmit=Soumettre
|
genericSubmit=Soumettre
|
||||||
processTimeWarning=Attention : ce processus peut prendre jusqu'à une minute en fonction de la taille du fichier
|
processTimeWarning=Attention : ce processus peut prendre jusqu'à une minute en fonction de la taille du fichier
|
||||||
pageOrderPrompt=Ordre des pages (Entrez une liste de numéros de page séparés par des virgules) :
|
pageOrderPrompt=Ordre des pages (Entrez une liste de numéros de page séparés par des virgules) :
|
||||||
goToPage=aller
|
goToPage=Aller
|
||||||
|
true=Vrai
|
||||||
|
false=Faux
|
||||||
|
unknown=Inconnu
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
#############
|
#############
|
||||||
|
@ -65,6 +69,8 @@ home.removePassword.desc=Supprimez la protection par mot de passe de votre docum
|
||||||
home.compressPdfs.title=Compresser les PDF
|
home.compressPdfs.title=Compresser les PDF
|
||||||
home.compressPdfs.desc=Compressez les PDF pour réduire leur taille de fichier.
|
home.compressPdfs.desc=Compressez les PDF pour réduire leur taille de fichier.
|
||||||
|
|
||||||
|
home.changeMetadata.title=Modifier les métadonnées
|
||||||
|
home.changeMetadata.desc=Modifier/Supprimer/Ajouter des métadonnées d'un document PDF
|
||||||
|
|
||||||
|
|
||||||
#Add image
|
#Add image
|
||||||
|
@ -129,6 +135,13 @@ imageToPDF.submit=Convertir
|
||||||
pdfToImage.title=PDF vers image
|
pdfToImage.title=PDF vers image
|
||||||
pdfToImage.header=PDF vers image
|
pdfToImage.header=PDF vers image
|
||||||
pdfToImage.selectText=Format d'image
|
pdfToImage.selectText=Format d'image
|
||||||
|
pdfToImage.singleOrMultiple=Type de résultat d'image
|
||||||
|
pdfToImage.single=Une seule grande image
|
||||||
|
pdfToImage.multi=Plusieurs images
|
||||||
|
pdfToImage.colorType=Type de couleur
|
||||||
|
pdfToImage.color=Couleur
|
||||||
|
pdfToImage.grey=Niveaux de gris
|
||||||
|
pdfToImage.blackwhite=Noir et Blanc (Peut perdre des données !)
|
||||||
pdfToImage.submit=Convertir
|
pdfToImage.submit=Convertir
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
@ -183,3 +196,21 @@ removePassword.header=Supprimer le mot de passe (Déchiffrer)
|
||||||
removePassword.selectText.1=Sélectionnez le PDF à déchiffrer
|
removePassword.selectText.1=Sélectionnez le PDF à déchiffrer
|
||||||
removePassword.selectText.2=Mot de passe
|
removePassword.selectText.2=Mot de passe
|
||||||
removePassword.submit=Supprimer
|
removePassword.submit=Supprimer
|
||||||
|
|
||||||
|
changeMetadata.title=Modifier les métadonnées
|
||||||
|
changeMetadata.header=Modifier les métadonnées
|
||||||
|
changeMetadata.selectText.1=Veuillez modifier les variables que vous souhaitez modifier
|
||||||
|
changeMetadata.selectText.2=Supprimer toutes les métadonnées
|
||||||
|
changeMetadata.selectText.3=Afficher les métadonnées personnalisées:
|
||||||
|
changeMetadata.author=Auteur:
|
||||||
|
changeMetadata.creationDate=Date de création (aaaa/MM/jj HH:mm:ss):
|
||||||
|
changeMetadata.creator=Créateur:
|
||||||
|
changeMetadata.keywords=Mots clés:
|
||||||
|
changeMetadata.modDate=Date de modification (aaaa/MM/jj HH:mm:ss):
|
||||||
|
changeMetadata.producer=Producteur:
|
||||||
|
changeMetadata.subject=Objet:
|
||||||
|
changeMetadata.title=Titre:
|
||||||
|
changeMetadata.trapped=Piégé:
|
||||||
|
changeMetadata.selectText.4=Autres métadonnées:
|
||||||
|
changeMetadata.selectText.5=Ajouter une entrée de métadonnées personnalisées
|
||||||
|
changeMetadata.submit=Modifier
|
||||||
|
|
2018
src/main/resources/static/css/bootstrap-icons.css
vendored
Normal file
2018
src/main/resources/static/css/bootstrap-icons.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
6
src/main/resources/static/css/bootstrap.min.css
vendored
Normal file
6
src/main/resources/static/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
src/main/resources/static/css/fonts/bootstrap-icons.woff
Normal file
BIN
src/main/resources/static/css/fonts/bootstrap-icons.woff
Normal file
Binary file not shown.
BIN
src/main/resources/static/css/fonts/bootstrap-icons.woff2
Normal file
BIN
src/main/resources/static/css/fonts/bootstrap-icons.woff2
Normal file
Binary file not shown.
5
src/main/resources/static/images/docker.svg
Normal file
5
src/main/resources/static/images/docker.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="50px" height="50px" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path stroke="#007bff" stroke-width="38" d="M 297.507 242.806 L 339.507 242.806 M 247.507 242.806 L 289.507 242.806 M 198.507 242.806 L 240.507 242.806 M 149.507 242.806 L 190.507 242.806 M 99.507 242.806 L 141.507 242.806 M 149.507 196.806 L 190.507 196.806 M 198.507 196.806 L 240.507 196.806 M 247.507 196.806 L 289.507 196.806 M 247.507 150.806 L 289.507 150.806"/>
|
||||||
|
<path fill="#007bff" d="M 473.507 244.806 C 473.507 244.806 455.507 227.806 418.507 233.806 C 414.507 204.806 383.507 187.806 383.507 187.806 C 383.507 187.806 354.507 222.806 375.507 261.806 C 369.507 264.806 359.507 268.806 344.507 268.806 L 69.507 268.806 C 64.507 287.806 64.507 413.806 202.507 413.806 C 301.507 413.806 375.507 367.806 410.507 283.806 C 462.507 287.806 473.507 244.806 473.507 244.806"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 919 B |
17
src/main/resources/static/images/github.svg
Normal file
17
src/main/resources/static/images/github.svg
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="35px" height="35px" viewBox="0 -0.5 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
|
||||||
|
<title>Github-color</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs>
|
||||||
|
|
||||||
|
</defs>
|
||||||
|
<g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Color-" transform="translate(-700.000000, -560.000000)" fill="#007bff">
|
||||||
|
<path d="M723.9985,560 C710.746,560 700,570.787092 700,584.096644 C700,594.740671 706.876,603.77183 716.4145,606.958412 C717.6145,607.179786 718.0525,606.435849 718.0525,605.797328 C718.0525,605.225068 718.0315,603.710086 718.0195,601.699648 C711.343,603.155898 709.9345,598.469394 709.9345,598.469394 C708.844,595.686405 707.2705,594.94548 707.2705,594.94548 C705.091,593.450075 707.4355,593.480194 707.4355,593.480194 C709.843,593.650366 711.1105,595.963499 711.1105,595.963499 C713.2525,599.645538 716.728,598.58234 718.096,597.964902 C718.3135,596.407754 718.9345,595.346062 719.62,594.743683 C714.2905,594.135281 708.688,592.069123 708.688,582.836167 C708.688,580.205279 709.6225,578.054788 711.1585,576.369634 C710.911,575.759726 710.0875,573.311058 711.3925,569.993458 C711.3925,569.993458 713.4085,569.345902 717.9925,572.46321 C719.908,571.928599 721.96,571.662047 724.0015,571.651505 C726.04,571.662047 728.0935,571.928599 730.0105,572.46321 C734.5915,569.345902 736.603,569.993458 736.603,569.993458 C737.9125,573.311058 737.089,575.759726 736.8415,576.369634 C738.3805,578.054788 739.309,580.205279 739.309,582.836167 C739.309,592.091712 733.6975,594.129257 728.3515,594.725612 C729.2125,595.469549 729.9805,596.939353 729.9805,599.18773 C729.9805,602.408949 729.9505,605.006706 729.9505,605.797328 C729.9505,606.441873 730.3825,607.191834 731.6005,606.9554 C741.13,603.762794 748,594.737659 748,584.096644 C748,570.787092 737.254,560 723.9985,560" id="Github">
|
||||||
|
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2 KiB |
6
src/main/resources/static/js/bootstrap.min.js
vendored
Normal file
6
src/main/resources/static/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
src/main/resources/static/js/jquery.min.js
vendored
Normal file
2
src/main/resources/static/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/main/resources/static/js/popper.min.js
vendored
Normal file
1
src/main/resources/static/js/popper.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
22
src/main/resources/static/pdfjs/pdf.min.js
vendored
Normal file
22
src/main/resources/static/pdfjs/pdf.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
22
src/main/resources/static/pdfjs/pdf.worker.min.js
vendored
Normal file
22
src/main/resources/static/pdfjs/pdf.worker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7702
src/main/resources/static/pdfjs/pdf_viewer.js
Normal file
7702
src/main/resources/static/pdfjs/pdf_viewer.js
Normal file
File diff suppressed because it is too large
Load diff
7
src/main/resources/static/pdfjs/pdf_viewer.min.css
vendored
Normal file
7
src/main/resources/static/pdfjs/pdf_viewer.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -6,49 +6,35 @@
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="page-container">
|
<div id="page-container">
|
||||||
<div id="content-wrap">
|
<div id="content-wrap">
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
<br>
|
<br> <br>
|
||||||
<br>
|
<div class="container">
|
||||||
<div class="container">
|
<div class="row justify-content-center">
|
||||||
<div class="row justify-content-center">
|
<div class="col-md-6">
|
||||||
<div class="col-md-6">
|
<h2 th:text="#{addImage.header}"></h2>
|
||||||
<h2 th:text="#{addImage.header}"></h2>
|
<form method="post" th:action="@{add-image}" enctype="multipart/form-data">
|
||||||
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
||||||
|
<div class="custom-file">
|
||||||
|
<input type="file" class="custom-file-input" id="fileInput2" name="fileInput2" required>
|
||||||
<form method="post" th:action="@{add-image}"
|
<label class="custom-file-label" for="fileInput2" th:text="#{imgPrompt}"></label>
|
||||||
enctype="multipart/form-data">
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
<div class="custom-file">
|
<label for="x">X</label> <input type="number" class="form-control" id="x" name="x" step="0.01" required>
|
||||||
<input type="file" class="custom-file-input" id="fileInput"
|
</div>
|
||||||
name="fileInput" required> <label
|
<div class="form-group">
|
||||||
class="custom-file-label" for="fileInput" th:text="#{pdfPrompt}"></label>
|
<label for="y">Y</label> <input type="number" class="form-control" id="y" name="y" step="0.01" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="custom-file">
|
<button type="submit" class="btn btn-primary" th:text="#{addImage.submit}"></button>
|
||||||
<input type="file" class="custom-file-input" id="fileInput2"
|
</form>
|
||||||
name="fileInput2" required> <label
|
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
||||||
class="custom-file-label" for="fileInput2" th:text="#{imgPrompt}"></label>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<label for="x">X</label> <input type="number" class="form-control"
|
</div>
|
||||||
id="x" name="x" step="0.01" required>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label for="y">Y</label> <input type="number" class="form-control"
|
|
||||||
id="y" name="y" step="0.01" required>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{addImage.submit}"></button>
|
|
||||||
</form>
|
|
||||||
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -5,40 +5,32 @@
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{compress.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{compress.title})}"></th:block>
|
||||||
|
|
||||||
|
|
||||||
<body> <div id="page-container">
|
<body>
|
||||||
<div id="content-wrap">
|
<div id="page-container">
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div id="content-wrap">
|
||||||
<br>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
<br>
|
<br> <br>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2 th:text="#{compress.header}"></h2>
|
<h2 th:text="#{compress.header}"></h2>
|
||||||
<form action="#" th:action="@{compress-pdf}"
|
<form action="#" th:action="@{compress-pdf}" th:object="${rotateForm}" method="post" enctype="multipart/form-data">
|
||||||
th:object="${rotateForm}" method="post"
|
<p th:text="#{processTimeWarning}"></p>
|
||||||
enctype="multipart/form-data">
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
||||||
<p th:text="#{processTimeWarning}"></p>
|
<div class="form-group">
|
||||||
<div class="custom-file">
|
<label for="imageCompressionLevel" th:text="#{compress.compressLevel}"></label>
|
||||||
<input type="file" class="custom-file-input" id="fileInput"
|
<input type="number" class="form-control" id="imageCompressionLevel" name="imageCompressionLevel" step="1" value="1" min="1" max="100" required>
|
||||||
name="fileInput" required> <label
|
</div>
|
||||||
class="custom-file-label" for="fileInput" th:text="#{pdfPrompt}"></label>
|
<button type="submit" class="btn btn-primary" th:text="#{compress.submit}"></button>
|
||||||
</div>
|
</form>
|
||||||
<div class="form-group">
|
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
||||||
<label for="imageCompressionLevel" th:text="#{compress.compressLevel}"></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" th:text="#{compress.submit}"></button>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -3,37 +3,33 @@
|
||||||
|
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{imageToPDF.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{imageToPDF.title})}"></th:block>
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="page-container">
|
<div id="page-container">
|
||||||
<div id="content-wrap">
|
<div id="content-wrap">
|
||||||
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
|
<br> <br>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h2 th:text="#{imageToPDF.header}"></h2>
|
||||||
|
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<form method="post" enctype="multipart/form-data" th:action="@{img-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" th:text="#{imgPrompt}"></label>
|
||||||
|
</div>
|
||||||
|
<br> <br>
|
||||||
|
<button type="submit" class="btn btn-primary" th:text="#{imageToPDF.submit}"></button>
|
||||||
|
|
||||||
<br>
|
</form>
|
||||||
<br>
|
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
</div>
|
||||||
<div class="row justify-content-center">
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
<div class="col-md-6">
|
</div>
|
||||||
<h2 th:text="#{imageToPDF.header}"></h2>
|
|
||||||
|
|
||||||
<form method="post" enctype="multipart/form-data" th:action="@{img-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" th:text="#{imgPrompt}"></label>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{imageToPDF.submit}"></button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,45 +1,57 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
|
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{pdfToImage})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{pdfToImage.title})}"></th:block>
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="page-container">
|
<div id="page-container">
|
||||||
<div id="content-wrap">
|
<div id="content-wrap">
|
||||||
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
|
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<br> <br>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h2 th:text="#{pdfToImage.header}"></h2>
|
||||||
|
<p th:text="#{processTimeWarning}"></p>
|
||||||
|
<form method="post" enctype="multipart/form-data" th:action="@{pdf-to-img}">
|
||||||
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label th:text="#{pdfToImage.selectText}"></label>
|
||||||
|
<select class="form-control" name="imageFormat">
|
||||||
|
<option value="png">PNG</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label th:text="#{pdfToImage.singleOrMultiple}"></label>
|
||||||
|
<select class="form-control" name="singleOrMultiple">
|
||||||
|
<option value="single" th:text="#{pdfToImage.single}"></option>
|
||||||
|
<option value="multiple" th:text="#{pdfToImage.multi}"></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label th:text="#{pdfToImage.colorType}"></label>
|
||||||
|
<select class="form-control" name="colorType">
|
||||||
|
<option value="color" th:text="#{pdfToImage.color}"></option>
|
||||||
|
<option value="greyscale" th:text="#{pdfToImage.grey}"></option>
|
||||||
|
<option value="blackwhite" th:text="#{pdfToImage.blackwhite}"></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="dpi">DPI:</label>
|
||||||
|
<input type="number" name="dpi" class="form-control" id="dpi" min="1" max="100" step="1" value="30" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary" th:text="#{pdfToImage.submit}"></button>
|
||||||
|
</form>
|
||||||
|
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
||||||
|
|
||||||
<br>
|
</div>
|
||||||
<br>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
</div>
|
||||||
<div class="row justify-content-center">
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
<div class="col-md-6">
|
</div>
|
||||||
<h2 th:text="#{pdfToImage.header}"></h2>
|
|
||||||
|
|
||||||
<form method="post" enctype="multipart/form-data" th:action="@{pdf-to-img}">
|
|
||||||
<div class="custom-file">
|
|
||||||
<input type="file" class="custom-file-input" id="fileInput" name="fileInput" required>
|
|
||||||
<label class="custom-file-label" for="fileInput" th:text="#{pdfPrompt}"></label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label th:text="#{pdfToImage.selectText}"></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" th:text="#{pdfToImage.submit}"></button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,5 +1,5 @@
|
||||||
<div th:fragment="card" class="feature-card">
|
<div th:fragment="card" class="feature-card">
|
||||||
<h5 class="card-title" th:text="${cardTitle}"></h5>
|
<h5 class="card-title" th:text="${cardTitle}"></h5>
|
||||||
<p class="card-text" th:text="${cardText}"></p>
|
<p class="card-text" th:text="${cardText}"></p>
|
||||||
<a class="btn btn-primary" th:href="${cardLink}" th:text="#{goToPage}"></a>
|
<a class="btn btn-primary" th:href="${cardLink}" th:text="#{goToPage}"></a>
|
||||||
</div>
|
</div>
|
|
@ -1,31 +1,29 @@
|
||||||
<head th:fragment="head">
|
<head th:fragment="head">
|
||||||
|
|
||||||
<!-- Metadata -->
|
<!-- Metadata -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title th:text="'S-PDF ' + ${title}"></title>
|
<title th:text="'S-PDF ' + ${title}"></title>
|
||||||
<link rel="shortcut icon" href="favicon.svg">
|
<link rel="shortcut icon" href="favicon.svg">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<!-- jQuery -->
|
<!-- jQuery -->
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
|
<script src="js/jquery.min.js"></script>
|
||||||
|
|
||||||
<!-- Bootstrap -->
|
<!-- Bootstrap -->
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
|
<script src="js/popper.min.js"></script>
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
|
<script src="js/bootstrap.min.js"></script>
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.3/font/bootstrap-icons.css">
|
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="css/bootstrap-icons.css">
|
||||||
|
|
||||||
<!-- FontAwesome -->
|
<!-- PDF.js -->
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.1/css/all.css">
|
<script src="pdfjs/pdf.min.js"></script>
|
||||||
|
<link href="pdfjs/pdf_viewer.min.css" rel="stylesheet">
|
||||||
|
|
||||||
<!-- PDF.js -->
|
<!-- Custom -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/pdfjs-dist@3.3.122/build/pdf.min.js"></script>
|
<link rel="stylesheet" href="css/general.css">
|
||||||
<link href="https://cdn.jsdelivr.net/npm/pdfjs-dist@3.3.122/web/pdf_viewer.min.css" rel="stylesheet">
|
<link rel="stylesheet" th:href="@{css/dark-mode.css}" id="dark-mode-styles">
|
||||||
|
|
||||||
<!-- Custom -->
|
<script>
|
||||||
<link rel="stylesheet" href="general.css">
|
|
||||||
<link rel="stylesheet" th:href="@{dark-mode.css}" id="dark-mode-styles">
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function toggleDarkMode() {
|
function toggleDarkMode() {
|
||||||
var checkbox = document.getElementById("toggle-dark-mode");
|
var checkbox = document.getElementById("toggle-dark-mode");
|
||||||
var darkModeStyles = document.getElementById("dark-mode-styles");
|
var darkModeStyles = document.getElementById("dark-mode-styles");
|
||||||
|
@ -54,9 +52,9 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<th:block th:fragment="filelist">
|
<th:block th:fragment="filelist">
|
||||||
<div id="fileList"></div>
|
<div id="fileList"></div>
|
||||||
<div id="fileList2"></div>
|
<div id="fileList2"></div>
|
||||||
<script>
|
<script>
|
||||||
var input = document.getElementById("fileInput");
|
var input = document.getElementById("fileInput");
|
||||||
var output = document.getElementById("fileList");
|
var output = document.getElementById("fileList");
|
||||||
|
|
||||||
|
@ -71,7 +69,7 @@
|
||||||
output.innerHTML = fileNames;
|
output.innerHTML = fileNames;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
var input2 = document.getElementById("fileInput2");
|
var input2 = document.getElementById("fileInput2");
|
||||||
var output2 = document.getElementById("fileList2");
|
var output2 = document.getElementById("fileList2");
|
||||||
if (input2 != null && !input2) {
|
if (input2 != null && !input2) {
|
||||||
|
@ -89,7 +87,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
if (dropContainer) {
|
if (dropContainer) {
|
||||||
dropContainer.ondragover = dropContainer.ondragenter = function(evt) {
|
dropContainer.ondragover = dropContainer.ondragenter = function(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
@ -112,14 +110,14 @@
|
||||||
</th:block>
|
</th:block>
|
||||||
|
|
||||||
<th:block th:fragment="fileSelector(name, multiple)">
|
<th:block th:fragment="fileSelector(name, multiple)">
|
||||||
<div class="custom-file-chooser">
|
<div class="custom-file-chooser">
|
||||||
<div class="custom-file">
|
<div class="custom-file">
|
||||||
<input type="file" class="custom-file-input" th:name="${name}" th:id="${name}+'-input'" th:multiple="${multiple}">
|
<input type="file" class="custom-file-input" th:name="${name}" th:id="${name}+'-input'" th:multiple="${multiple}">
|
||||||
<label class="custom-file-label" th:for="${name}+'-input'" th:text="#{pdfPrompt}"></label>
|
<label class="custom-file-label" th:for="${name}+'-input'" th:text="#{pdfPrompt}"></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
$([[${"#"+name+"-input"}]]).on("change", function() {
|
$([[${"#"+name+"-input"}]]).on("change", function() {
|
||||||
const files = $(this).get(0).files;
|
const files = $(this).get(0).files;
|
||||||
const fileNames = Array.from(files).map(f => f.name).join(", ");
|
const fileNames = Array.from(files).map(f => f.name).join(", ");
|
||||||
|
@ -131,9 +129,9 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.custom-file-label {
|
.custom-file-label {
|
||||||
padding-right: 90px;
|
padding-right: 90px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</th:block>
|
</th:block>
|
|
@ -1,6 +1,6 @@
|
||||||
<div th:fragment="footer">
|
<div th:fragment="footer">
|
||||||
<footer id="footer" class="text-center py-3">
|
<footer id="footer" class="text-center py-3">
|
||||||
<a href="https://github.com/Frooodle/Stirling-PDF" target="_blank" class="mx-1"> <i class="fab fa-github fa-2x"></i></a>
|
<a href="https://github.com/Frooodle/Stirling-PDF" target="_blank" class="mx-1"><img src="images/github.svg"></img></a>
|
||||||
<a href="https://hub.docker.com/r/frooodle/s-pdf" target="_blank" class="mx-1"> <i class="fab fa-docker fa-2x"></i></a>
|
<a href="https://hub.docker.com/r/frooodle/s-pdf" target="_blank" class="mx-1"><img src="images/docker.svg"></img></a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
|
@ -45,6 +45,7 @@
|
||||||
<a class="dropdown-item" href="#" th:href="@{remove-password}" th:classappend="${currentPage}=='remove-password' ? 'active' : ''" th:text="#{home.removePassword.title}"></a>
|
<a class="dropdown-item" href="#" th:href="@{remove-password}" th:classappend="${currentPage}=='remove-password' ? 'active' : ''" th:text="#{home.removePassword.title}"></a>
|
||||||
<a class="dropdown-item" href="#" th:href="@{change-permissions}" th:classappend="${currentPage}=='change-permissions' ? 'active' : ''" th:text="#{home.permissions.title}"></a>
|
<a class="dropdown-item" href="#" th:href="@{change-permissions}" th:classappend="${currentPage}=='change-permissions' ? 'active' : ''" th:text="#{home.permissions.title}"></a>
|
||||||
<a class="dropdown-item" href="#" th:href="@{add-watermark}" th:classappend="${currentPage}=='add-watermark' ? 'active' : ''" th:text="#{home.watermark.title}"></a>
|
<a class="dropdown-item" href="#" th:href="@{add-watermark}" th:classappend="${currentPage}=='add-watermark' ? 'active' : ''" th:text="#{home.watermark.title}"></a>
|
||||||
|
<a class="dropdown-item" href="#" th:href="@{change-metadata}" th:classappend="${currentPage}=='change-metadata' ? 'active' : ''" th:text="#{home.changeMetadata.title}"></a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -66,9 +67,9 @@
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<input type="checkbox" id="toggle-dark-mode" checked="true" th:onclick="javascript:toggleDarkMode()">
|
<input type="checkbox" id="toggle-dark-mode" checked="true" th:onclick="javascript:toggleDarkMode()">
|
||||||
|
|
||||||
<a class="nav-link" href="#" for="toggle-dark-mode" th:text="#{navbar.darkmode}"></a>
|
<a class="nav-link" href="#" for="toggle-dark-mode" th:text="#{navbar.darkmode}"></a>
|
||||||
|
|
||||||
|
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="languageDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<a class="nav-link dropdown-toggle" href="#" id="languageDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
<i class="bi bi-globe2"></i>
|
<i class="bi bi-globe2"></i>
|
||||||
|
@ -77,7 +78,7 @@
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="en_US">English (US)</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="en_US">English (US)</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="en_GB">English (UK)</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="en_GB">English (UK)</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="ar_AR">العربية</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="ar_AR">العربية</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="de_DE">German</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="de_DE">Deutsch</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="fr_FR">Français</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="fr_FR">Français</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -5,62 +5,63 @@
|
||||||
<th:block th:insert="~{fragments/common :: head(title='')}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title='')}"></th:block>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.features-container {
|
.features-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(21rem, 3fr));
|
grid-template-columns: repeat(auto-fill, minmax(21rem, 3fr));
|
||||||
gap: 25px 30px;
|
gap: 25px 30px;
|
||||||
}
|
}
|
||||||
.feature-card {
|
|
||||||
border: 1px solid rgba(0,0,0,.125);
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
padding: 1.25rem;
|
|
||||||
|
|
||||||
display: flex;
|
.feature-card {
|
||||||
flex-direction: column;
|
border: 1px solid rgba(0, 0, 0, .125);
|
||||||
align-items: flex-start;
|
border-radius: 0.25rem;
|
||||||
}
|
padding: 1.25rem;
|
||||||
.feature-card .card-text {
|
display: flex;
|
||||||
flex: 1;
|
flex-direction: column;
|
||||||
}
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card .card-text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div id="page-container">
|
<div id="page-container">
|
||||||
<div id="content-wrap">
|
<div id="content-wrap">
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
<!-- Jumbotron -->
|
<!-- Jumbotron -->
|
||||||
<div class="jumbotron jumbotron-fluid" id="jumbotron">
|
<div class="jumbotron jumbotron-fluid" id="jumbotron">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="display-4">Stirling PDF</h1>
|
<h1 class="display-4">Stirling PDF</h1>
|
||||||
<p class="lead" th:text="#{home.desc}"></p>
|
<p class="lead" th:text="#{home.desc}"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Features -->
|
<!-- Features -->
|
||||||
<div class="features-container container">
|
<div class="features-container container">
|
||||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.merge.title}, cardText=#{home.merge.desc}, cardLink='merge-pdfs')}"></div>
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.merge.title}, cardText=#{home.merge.desc}, cardLink='merge-pdfs')}"></div>
|
||||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.split.title}, cardText=#{home.split.desc}, cardLink='split-pdfs')}"></div>
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.split.title}, cardText=#{home.split.desc}, cardLink='split-pdfs')}"></div>
|
||||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.rotate.title}, cardText=#{home.rotate.desc}, cardLink='rotate-pdf')}"></div>
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.rotate.title}, cardText=#{home.rotate.desc}, cardLink='rotate-pdf')}"></div>
|
||||||
|
|
||||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.imageToPdf.title}, cardText=#{home.imageToPdf.desc}, cardLink='img-to-pdf')}"></div>
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.imageToPdf.title}, cardText=#{home.imageToPdf.desc}, cardLink='img-to-pdf')}"></div>
|
||||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.pdfToImage.title}, cardText=#{home.pdfToImage.desc}, cardLink='pdf-to-img')}"></div>
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.pdfToImage.title}, cardText=#{home.pdfToImage.desc}, cardLink='pdf-to-img')}"></div>
|
||||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.pdfOrganiser.title}, cardText=#{home.pdfOrganiser.desc}, cardLink='pdf-organizer')}"></div>
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.pdfOrganiser.title}, cardText=#{home.pdfOrganiser.desc}, cardLink='pdf-organizer')}"></div>
|
||||||
|
|
||||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.addImage.title}, cardText=#{home.addImage.desc}, cardLink='add-image')}"></div>
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.addImage.title}, cardText=#{home.addImage.desc}, cardLink='add-image')}"></div>
|
||||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.watermark.title}, cardText=#{home.watermark.desc}, cardLink='add-watermark')}"></div>
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.watermark.title}, cardText=#{home.watermark.desc}, cardLink='add-watermark')}"></div>
|
||||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.permissions.title}, cardText=#{home.permissions.desc}, cardLink='change-permissions')}"></div>
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.permissions.title}, cardText=#{home.permissions.desc}, cardLink='change-permissions')}"></div>
|
||||||
|
|
||||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.removePages.title}, cardText=#{home.removePages.desc}, cardLink='remove-pages')}"></div>
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.removePages.title}, cardText=#{home.removePages.desc}, cardLink='remove-pages')}"></div>
|
||||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.addPassword.title}, cardText=#{home.addPassword.desc}, cardLink='add-password')}"></div>
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.addPassword.title}, cardText=#{home.addPassword.desc}, cardLink='add-password')}"></div>
|
||||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.removePassword.title}, cardText=#{home.removePassword.desc}, cardLink='remove-password')}"></div>
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.removePassword.title}, cardText=#{home.removePassword.desc}, cardLink='remove-password')}"></div>
|
||||||
|
|
||||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.compressPdfs.title}, cardText=#{home.compressPdfs.desc}, cardLink='compress-pdf')}"></div>
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.compressPdfs.title}, cardText=#{home.compressPdfs.desc}, cardLink='compress-pdf')}"></div>
|
||||||
|
<div th:replace="~{fragments/card :: card(cardTitle=#{home.changeMetadata.title}, cardText=#{home.changeMetadata.desc}, cardLink='change-metadata')}"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -4,130 +4,124 @@
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{merge.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{merge.title})}"></th:block>
|
||||||
|
|
||||||
|
|
||||||
<body> <div id="page-container">
|
<body>
|
||||||
<div id="content-wrap">
|
<div id="page-container">
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div id="content-wrap">
|
||||||
<br>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
<br>
|
<br> <br>
|
||||||
<div class="container" id="dropContainer">
|
<div class="container" id="dropContainer">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2 th:text="#{merge.header}"></h2>
|
<h2 th:text="#{merge.header}"></h2>
|
||||||
<form action="merge-pdfs" method="post"
|
<form action="merge-pdfs" method="post" enctype="multipart/form-data">
|
||||||
enctype="multipart/form-data">
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<label th:text="#{multiPdfDropPrompt}"></label>
|
||||||
<label th:text="#{multiPdfDropPrompt}"></label>
|
<div class="custom-file">
|
||||||
<div class="custom-file">
|
<input type="file" class="custom-file-input" id="fileInput" name="fileInput" multiple required>
|
||||||
<input type="file" class="custom-file-input" id="fileInput"
|
<label class="custom-file-label" th:text="#{pdfPrompt}"></label>
|
||||||
name="fileInput" multiple required> <label
|
</div>
|
||||||
class="custom-file-label" th:text="#{pdfPrompt}">s</label>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
</div>
|
<ul id="selectedFiles" class="list-group"></ul>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<ul id="selectedFiles" class="list-group"></ul>
|
<div class="form-group text-center">
|
||||||
</div>
|
<button type="submit" class="btn btn-primary" th:text="#{merge.submit}"></button>
|
||||||
<div class="form-group text-center">
|
</div>
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{merge.submit}"></button>
|
</form>
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document
|
||||||
|
.getElementById(
|
||||||
|
"fileInput")
|
||||||
|
.addEventListener(
|
||||||
|
"change",
|
||||||
|
function() {
|
||||||
|
var files = this.files;
|
||||||
|
var list = document
|
||||||
|
.getElementById("selectedFiles");
|
||||||
|
list.innerHTML = "";
|
||||||
|
for (var i = 0; i < files.length; i++) {
|
||||||
|
var item = document
|
||||||
|
.createElement("li");
|
||||||
|
item.className = "list-group-item d-flex justify-content-between align-items-center";
|
||||||
|
item.textContent = files[i].name;
|
||||||
|
item.innerHTML += '<div><button class="btn btn-secondary move-up"><i class="fas fa-arrow-up"></i></button> <button class="btn btn-secondary move-down"><i class="fas fa-arrow-down"></i></button></div>';
|
||||||
|
list
|
||||||
|
.appendChild(item);
|
||||||
|
}
|
||||||
|
var moveUpButtons = document
|
||||||
|
.querySelectorAll(".move-up");
|
||||||
|
for (var i = 0; i < moveUpButtons.length; i++) {
|
||||||
|
moveUpButtons[i]
|
||||||
|
.addEventListener(
|
||||||
|
"click",
|
||||||
|
function(
|
||||||
|
event) {
|
||||||
|
event
|
||||||
|
.preventDefault();
|
||||||
|
var parent = this.parentNode.parentNode;
|
||||||
|
var grandParent = parent.parentNode;
|
||||||
|
if (parent.previousElementSibling) {
|
||||||
|
grandParent
|
||||||
|
.insertBefore(
|
||||||
|
parent,
|
||||||
|
parent.previousElementSibling);
|
||||||
|
updateFiles();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var moveDownButtons = document
|
||||||
|
.querySelectorAll(".move-down");
|
||||||
|
for (var i = 0; i < moveDownButtons.length; i++) {
|
||||||
|
moveDownButtons[i]
|
||||||
|
.addEventListener(
|
||||||
|
"click",
|
||||||
|
function(
|
||||||
|
event) {
|
||||||
|
event
|
||||||
|
.preventDefault();
|
||||||
|
var parent = this.parentNode.parentNode;
|
||||||
|
var grandParent = parent.parentNode;
|
||||||
|
if (parent.nextElementSibling) {
|
||||||
|
grandParent
|
||||||
|
.insertBefore(
|
||||||
|
parent.nextElementSibling,
|
||||||
|
parent);
|
||||||
|
updateFiles();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFiles() {
|
||||||
|
var dataTransfer = new DataTransfer();
|
||||||
|
var liElements = document
|
||||||
|
.querySelectorAll("#selectedFiles li");
|
||||||
|
|
||||||
|
for (var i = 0; i < liElements.length; i++) {
|
||||||
|
var fileNameFromList = liElements[i].innerText
|
||||||
<script>
|
.replace(
|
||||||
document
|
"\nMove Up Move Down",
|
||||||
.getElementById("fileInput")
|
"");
|
||||||
.addEventListener(
|
var fileFromFiles
|
||||||
"change",
|
for (var j = 0; j < files.length; j++) {
|
||||||
function() {
|
var file = files[j];
|
||||||
var files = this.files;
|
if (file.name === fileNameFromList) {
|
||||||
var list = document
|
dataTransfer.items
|
||||||
.getElementById("selectedFiles");
|
.add(file);
|
||||||
list.innerHTML = "";
|
break;
|
||||||
for (var i = 0; i < files.length; i++) {
|
}
|
||||||
var item = document
|
|
||||||
.createElement("li");
|
|
||||||
item.className = "list-group-item d-flex justify-content-between align-items-center";
|
|
||||||
item.textContent = files[i].name;
|
|
||||||
item.innerHTML += '<div><button class="btn btn-secondary move-up">Move Up</button> <button class="btn btn-secondary move-down">Move Down</button></div>';
|
|
||||||
list.appendChild(item);
|
|
||||||
}
|
|
||||||
var moveUpButtons = document
|
|
||||||
.querySelectorAll(".move-up");
|
|
||||||
for (var i = 0; i < moveUpButtons.length; i++) {
|
|
||||||
moveUpButtons[i]
|
|
||||||
.addEventListener(
|
|
||||||
"click",
|
|
||||||
function(event) {
|
|
||||||
event
|
|
||||||
.preventDefault();
|
|
||||||
var parent = this.parentNode.parentNode;
|
|
||||||
var grandParent = parent.parentNode;
|
|
||||||
if (parent.previousElementSibling) {
|
|
||||||
grandParent
|
|
||||||
.insertBefore(
|
|
||||||
parent,
|
|
||||||
parent.previousElementSibling);
|
|
||||||
updateFiles();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var moveDownButtons = document
|
|
||||||
.querySelectorAll(".move-down");
|
|
||||||
for (var i = 0; i < moveDownButtons.length; i++) {
|
|
||||||
moveDownButtons[i]
|
|
||||||
.addEventListener(
|
|
||||||
"click",
|
|
||||||
function(event) {
|
|
||||||
event
|
|
||||||
.preventDefault();
|
|
||||||
var parent = this.parentNode.parentNode;
|
|
||||||
var grandParent = parent.parentNode;
|
|
||||||
if (parent.nextElementSibling) {
|
|
||||||
grandParent
|
|
||||||
.insertBefore(
|
|
||||||
parent.nextElementSibling,
|
|
||||||
parent);
|
|
||||||
updateFiles();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateFiles() {
|
|
||||||
var dataTransfer = new DataTransfer();
|
|
||||||
var liElements = document
|
|
||||||
.querySelectorAll("#selectedFiles li");
|
|
||||||
|
|
||||||
for (var i = 0; i < liElements.length; i++) {
|
|
||||||
var fileNameFromList = liElements[i].innerText
|
|
||||||
.replace(
|
|
||||||
"\nMove Up Move Down",
|
|
||||||
"");
|
|
||||||
var fileFromFiles
|
|
||||||
for (var j = 0; j < files.length; j++) {
|
|
||||||
var file = files[j];
|
|
||||||
if (file.name === fileNameFromList) {
|
|
||||||
dataTransfer.items
|
|
||||||
.add(file);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
document
|
||||||
|
.getElementById("fileInput").files = dataTransfer.files;
|
||||||
}
|
}
|
||||||
document
|
});
|
||||||
.getElementById("fileInput").files = dataTransfer.files;
|
</script>
|
||||||
}
|
</div>
|
||||||
});
|
</div>
|
||||||
</script>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -4,37 +4,31 @@
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{pdfOrganiser.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{pdfOrganiser.title})}"></th:block>
|
||||||
|
|
||||||
|
|
||||||
<body> <div id="page-container">
|
<body>
|
||||||
<div id="content-wrap">
|
<div id="page-container">
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div id="content-wrap">
|
||||||
<br>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
<br>
|
<br> <br>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2 th:text="#{pdfOrganiser.header}"></h2>
|
<h2 th:text="#{pdfOrganiser.header}"></h2>
|
||||||
|
|
||||||
<form th:action="@{rearrange-pages}" method="post"
|
<form th:action="@{rearrange-pages}" method="post" enctype="multipart/form-data">
|
||||||
enctype="multipart/form-data">
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
||||||
<div class="custom-file">
|
<div class="form-group">
|
||||||
<input type="file" class="custom-file-input" id="fileInput"
|
<label for="pageOrder" th:text="#{pageOrderPrompt}"></label>
|
||||||
name="fileInput" required> <label
|
<input type="text" class="form-control" id="fileInput" name="pageOrder" placeholder="(e.g. 1,3,2 or 4-8,2,10-12)" required>
|
||||||
class="custom-file-label" for="fileInput" th:text="#{pdfPrompt}"></label>
|
</div>
|
||||||
</div>
|
<button type="submit" class="btn btn-primary" th:text="#{pdfOrganiser.submit}"></button>
|
||||||
<div class="form-group">
|
</form>
|
||||||
<label for="pageOrder" th:text="#{pageOrderPrompt}"></label> <input type="text" class="form-control"
|
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
||||||
id="fileInput" name="pageOrder"
|
|
||||||
placeholder="(e.g. 1,3,2 or 4-8,2,10-12)" required>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{pdfOrganiser.submit}"></button>
|
|
||||||
</form>
|
|
||||||
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -4,37 +4,31 @@
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{pageRemover.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{pageRemover.title})}"></th:block>
|
||||||
|
|
||||||
|
|
||||||
<body> <div id="page-container">
|
<body>
|
||||||
<div id="content-wrap">
|
<div id="page-container">
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div id="content-wrap">
|
||||||
<br>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
<br>
|
<br> <br>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2 th:text="#{pageRemover.header}"></h2>
|
<h2 th:text="#{pageRemover.header}"></h2>
|
||||||
|
|
||||||
<form th:action="@{remove-pages}" method="post"
|
<form th:action="@{remove-pages}" method="post" enctype="multipart/form-data">
|
||||||
enctype="multipart/form-data">
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
||||||
<div class="custom-file">
|
<div class="form-group">
|
||||||
<input type="file" class="custom-file-input" id="fileInput"
|
<label for="pagesToDelete" th:text="#{pageRemover.pagesToDelete}"></label>
|
||||||
name="fileInput" required> <label
|
<input type="text" class="form-control" id="fileInput" name="pagesToDelete" placeholder="(e.g. 1,2,6 or 1-10,15-30)" required>
|
||||||
class="custom-file-label" for="fileInput" th:text="#{pdfPrompt}"></label>
|
</div>
|
||||||
</div>
|
<button type="submit" class="btn btn-primary" th:text="#{pageRemover.submit}"></button>
|
||||||
<div class="form-group">
|
</form>
|
||||||
<label for="pagesToDelete" th:text="#{pageRemover.pagesToDelete}"></label> <input type="text" class="form-control"
|
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
||||||
id="fileInput" name="pagesToDelete"
|
|
||||||
placeholder="(e.g. 1,2,6 or 1-10,15-30)" required>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{pageRemover.submit}"></button>
|
|
||||||
</form>
|
|
||||||
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -5,45 +5,51 @@
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{rotate.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{rotate.title})}"></th:block>
|
||||||
|
|
||||||
|
|
||||||
<body> <div id="page-container">
|
<body>
|
||||||
<div id="content-wrap">
|
<div id="page-container">
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div id="content-wrap">
|
||||||
<br>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
<br>
|
<br> <br>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2 th:text="#{rotate.header}"></h2>
|
<h2 th:text="#{rotate.header}"></h2>
|
||||||
|
|
||||||
<form action="#" th:action="@{rotate-pdf}" th:object="${rotateForm}" method="post" enctype="multipart/form-data">
|
<form action="#" th:action="@{rotate-pdf}" th:object="${rotateForm}" method="post" enctype="multipart/form-data">
|
||||||
<div th:replace="fragments/common :: fileSelector(name='fileInput', multiple=false)"></div>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
||||||
<input type="hidden" id="angleInput" name="angle" value="0">
|
<input type="hidden" id="angleInput" name="angle" value="0">
|
||||||
|
|
||||||
<div id="editSection" style="display: none">
|
<div id="editSection" style="display: none">
|
||||||
<div class="previewContainer">
|
<div class="previewContainer">
|
||||||
<img id="pdf-preview"/>
|
<img id="pdf-preview" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="buttonContainer">
|
<div class="buttonContainer">
|
||||||
<button type="button" class="btn btn-secondary" onclick="rotate(-90)">
|
<button type="button" class="btn btn-secondary" onclick="rotate(-90)">
|
||||||
<i class="bi bi-arrow-counterclockwise"></i>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
|
||||||
</button>
|
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z" />
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{rotate.submit}"></button>
|
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z" />
|
||||||
<button type="button" class="btn btn-secondary" onclick="rotate(90)">
|
</svg>
|
||||||
<i class="bi bi-arrow-clockwise"></i>
|
</button>
|
||||||
</button>
|
<button type="submit" class="btn btn-primary" th:text="#{rotate.submit}"></button>
|
||||||
</div>
|
<button type="button" class="btn btn-secondary" onclick="rotate(90)">
|
||||||
</div>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
|
||||||
</form>
|
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z" />
|
||||||
|
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const angleInput = document.getElementById("angleInput");
|
const angleInput = document.getElementById("angleInput");
|
||||||
const fileInput = document.getElementById("fileInput-input");
|
const fileInput = document.getElementById("fileInput-input");
|
||||||
const preview = document.getElementById("pdf-preview");
|
const preview = document.getElementById("pdf-preview");
|
||||||
|
@ -90,36 +96,37 @@
|
||||||
angleInput.value = newAngle;
|
angleInput.value = newAngle;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
#pdf-preview {
|
#pdf-preview {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
display: block;
|
display: block;
|
||||||
max-width: calc(100% - 30px);
|
max-width: calc(100% - 30px);
|
||||||
max-height: calc(100% - 30px);
|
max-height: calc(100% - 30px);
|
||||||
box-shadow: 0 0 4px rgba(100,100,100,.25);
|
box-shadow: 0 0 4px rgba(100, 100, 100, .25);
|
||||||
transition: rotate .3s;
|
transition: rotate .3s;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
translate: -50% -50%;
|
translate: -50% -50%;
|
||||||
}
|
}
|
||||||
.previewContainer {
|
|
||||||
aspect-ratio: 1;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid rgba(0,0,0,.125);
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
margin: 1rem 0;
|
|
||||||
padding: 15px;
|
|
||||||
display: block;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonContainer {
|
.previewContainer {
|
||||||
display: flex;
|
aspect-ratio: 1;
|
||||||
justify-content: space-around;
|
width: 100%;
|
||||||
}
|
border: 1px solid rgba(0, 0, 0, .125);
|
||||||
</style>
|
border-radius: 0.25rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 15px;
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -3,94 +3,78 @@
|
||||||
|
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{addPassword.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{addPassword.title})}"></th:block>
|
||||||
|
|
||||||
<body> <div id="page-container">
|
<body>
|
||||||
<div id="content-wrap">
|
<div id="page-container">
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div id="content-wrap">
|
||||||
<br>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
<br>
|
<br> <br>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2 th:text="#{addPassword.header}"></h2>
|
<h2 th:text="#{addPassword.header}"></h2>
|
||||||
|
|
||||||
<form action="add-password" method="post"
|
<form action="add-password" method="post" enctype="multipart/form-data">
|
||||||
enctype="multipart/form-data">
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<label th:text="#{addPassword.selectText.1}"></label>
|
||||||
<label th:text="#{addPassword.selectText.1}"></label>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
||||||
<div class="custom-file">
|
</div>
|
||||||
<input type="file" class="custom-file-input" id="fileInput"
|
<div class="form-group">
|
||||||
name="fileInput" required> <label
|
<label th:text="#{addPassword.selectText.2}"></label> <input type="password" class="form-control" id="password" name="password" required>
|
||||||
class="custom-file-label" th:text="#{pdfPrompt}"></label>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
</div>
|
<label th:text="#{addPassword.selectText.3}"></label> <select class="form-control" id="keyLength" name="keyLength">
|
||||||
<div class="form-group">
|
<option value="40">40</option>
|
||||||
<label th:text="#{addPassword.selectText.2}"></label> <input type="password"
|
<option value="128">128</option>
|
||||||
class="form-control" id="password" name="password" required>
|
<option value="256">256</option>
|
||||||
</div>
|
</select> <small class="form-text text-muted" th:text="#{addPassword.selectText.4}"></small>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<label th:text="#{addPassword.selectText.3}"></label> <select class="form-control"
|
<div class="form-group">
|
||||||
id="keyLength" name="keyLength">
|
<label th:text="#{addPassword.selectText.5}"></label>
|
||||||
<option value="40">40</option>
|
<div class="form-check">
|
||||||
<option value="128">128</option>
|
<input class="form-check-input" type="checkbox" id="canAssembleDocument" name="canAssembleDocument">
|
||||||
<option value="256">256</option>
|
<label class="form-check-label" for="canAssembleDocument" th:text="#{addPassword.selectText.6}"></label>
|
||||||
</select> <small class="form-text text-muted" th:text="#{addPassword.selectText.4}"></small>
|
</div>
|
||||||
</div>
|
<div class="form-check">
|
||||||
<div class="form-group">
|
<input class="form-check-input" type="checkbox" id="canExtractContent" name="canExtractContent">
|
||||||
<label th:text="#{addPassword.selectText.5}"></label>
|
<label class="form-check-label" for="canExtractContent" th:text="#{addPassword.selectText.7}"></label>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox"
|
<div class="form-check">
|
||||||
id="canAssembleDocument" name="canAssembleDocument"> <label
|
<input class="form-check-input" type="checkbox" id="canExtractForAccessibility" name="canExtractForAccessibility">
|
||||||
class="form-check-label" for="canAssembleDocument" th:text="#{addPassword.selectText.6}"></label>
|
<label class="form-check-label" for="canExtractForAccessibility" th:text="#{addPassword.selectText.8}"></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox"
|
<input class="form-check-input" type="checkbox" id="canFillInForm" name="canFillInForm">
|
||||||
id="canExtractContent" name="canExtractContent"> <label
|
<label class="form-check-label" for="canFillInForm" th:text="#{addPassword.selectText.9}"></label>
|
||||||
class="form-check-label" for="canExtractContent" th:text="#{addPassword.selectText.7}"></label>
|
</div>
|
||||||
</div>
|
<div class="form-check">
|
||||||
<div class="form-check">
|
<input class="form-check-input" type="checkbox" id="canModify" name="canModify">
|
||||||
<input class="form-check-input" type="checkbox"
|
<label class="form-check-label" for="canModify" th:text="#{addPassword.selectText.10}"></label>
|
||||||
id="canExtractForAccessibility"
|
</div>
|
||||||
name="canExtractForAccessibility"> <label
|
<div class="form-check">
|
||||||
class="form-check-label" for="canExtractForAccessibility" th:text="#{addPassword.selectText.8}"></label>
|
<input class="form-check-input" type="checkbox" id="canModifyAnnotations" name="canModifyAnnotations">
|
||||||
</div>
|
<label class="form-check-label" for="canModifyAnnotations" th:text="#{addPassword.selectText.11}"></label>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox"
|
<div class="form-check">
|
||||||
id="canFillInForm" name="canFillInForm"> <label
|
<input class="form-check-input" type="checkbox" id="canPrint" name="canPrint">
|
||||||
class="form-check-label" for="canFillInForm" th:text="#{addPassword.selectText.9}"></label>
|
<label class="form-check-label" for="canPrint" th:text="#{addPassword.selectText.12}"></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" id="canModify"
|
<input class="form-check-input" type="checkbox" id="canPrintFaithful" name="canPrintFaithful">
|
||||||
name="canModify"> <label class="form-check-label"
|
<label class="form-check-label" for="canPrintFaithful" th:text="#{addPassword.selectText.13}"></label>
|
||||||
for="canModify" th:text="#{addPassword.selectText.10}"></label>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="form-check">
|
|
||||||
<input class="form-check-input" type="checkbox"
|
|
||||||
id="canModifyAnnotations" name="canModifyAnnotations"> <label
|
|
||||||
class="form-check-label" for="canModifyAnnotations" th:text="#{addPassword.selectText.11}"></label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check">
|
|
||||||
<input class="form-check-input" type="checkbox" id="canPrint"
|
|
||||||
name="canPrint"> <label class="form-check-label"
|
|
||||||
for="canPrint" th:text="#{addPassword.selectText.12}"></label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check">
|
|
||||||
<input class="form-check-input" type="checkbox"
|
|
||||||
id="canPrintFaithful" name="canPrintFaithful"> <label
|
|
||||||
class="form-check-label" for="canPrintFaithful" th:text="#{addPassword.selectText.13}"></label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div class="form-group text-center">
|
<div class="form-group text-center">
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{addPassword.submit}"></button>
|
<button type="submit" class="btn btn-primary" th:text="#{addPassword.submit}"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -3,54 +3,50 @@
|
||||||
|
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{watermark.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{watermark.title})}"></th:block>
|
||||||
|
|
||||||
<body> <div id="page-container">
|
<body>
|
||||||
<div id="content-wrap">
|
<div id="page-container">
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div id="content-wrap">
|
||||||
<br>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
<br>
|
<br> <br>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2 th:text="#{watermark.header}"></h2>
|
<h2 th:text="#{watermark.header}"></h2>
|
||||||
|
|
||||||
<form method="post" enctype="multipart/form-data" action="add-watermark">
|
<form method="post" enctype="multipart/form-data" action="add-watermark">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label th:text="#{watermark.selectText.1}"></label>
|
<label th:text="#{watermark.selectText.1}"></label>
|
||||||
<div class="custom-file">
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
||||||
<input type="file" class="custom-file-input" id="fileInput"
|
</div>
|
||||||
name="fileInput" required> <label
|
<div class="form-group">
|
||||||
class="custom-file-label" th:text="#{pdfPrompt}"></label>
|
<label for="watermarkText" th:text="#{watermark.selectText.2}"></label>
|
||||||
</div>
|
<input type="text" id="watermarkText" name="watermarkText" class="form-control" placeholder="Stirling-PDF" required />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="watermarkText" th:text="#{watermark.selectText.2}"></label>
|
<label for="fontSize" th:text="#{watermark.selectText.3}"></label>
|
||||||
<input type="text" id="watermarkText" name="watermarkText" class="form-control" placeholder="Stirling-PDF" required/>
|
<input type="text" id="fontSize" name="fontSize" class="form-control" value="30" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="fontSize" th:text="#{watermark.selectText.3}"></label>
|
<label for="rotation" th:text="#{watermark.selectText.4}"></label>
|
||||||
<input type="text" id="fontSize" name="fontSize" class="form-control" value="30"/>
|
<input type="text" id="rotation" name="rotation" class="form-control" value="45" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="rotation" th:text="#{watermark.selectText.4}"></label>
|
<label for="widthSpacer" th:text="#{watermark.selectText.5}"></label>
|
||||||
<input type="text" id="rotation" name="rotation" class="form-control" value="45"/>
|
<input type="text" id="widthSpacer" name="widthSpacer" class="form-control" value="50" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="widthSpacer" th:text="#{watermark.selectText.5}"></label>
|
<label for="heightSpacer" th:text="#{watermark.selectText.6}"></label>
|
||||||
<input type="text" id="widthSpacer" name="widthSpacer" class="form-control" value="50"/>
|
<input type="text" id="heightSpacer" name="heightSpacer" class="form-control" value="50" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group text-center">
|
||||||
<label for="heightSpacer" th:text="#{watermark.selectText.6}"></label>
|
<input type="submit" th:value="#{watermark.submit}" class="btn btn-primary" />
|
||||||
<input type="text" id="heightSpacer" name="heightSpacer" class="form-control" value="50"/>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
<div class="form-group text-center">
|
</div>
|
||||||
<input type="submit" th:value="#{watermark.submit}" class="btn btn-primary"/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
254
src/main/resources/templates/security/change-metadata.html
Normal file
254
src/main/resources/templates/security/change-metadata.html
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
|
<th:block th:insert="~{fragments/common :: head(title=#{changeMetadata.title})}"></th:block>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="page-container">
|
||||||
|
<div id="content-wrap">
|
||||||
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
|
<br> <br>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h2 th:text="#{changeMetadata.header}"></h2>
|
||||||
|
|
||||||
|
<form method="post" id="form1" enctype="multipart/form-data" th:action="@{/update-metadata}">
|
||||||
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
||||||
|
<p class="text-muted" th:text="#{changeMetadata.selectText.1}"></p>
|
||||||
|
|
||||||
|
<div class="form-group-inline form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" id="deleteAll" name="deleteAll">
|
||||||
|
<label class="ml-3" for="deleteAll" th:text="#{changeMetadata.selectText.2}" ></label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group-inline form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" id="customModeCheckbox">
|
||||||
|
<label class="ml-3" for="customModeCheckbox" th:text="#{changeMetadata.selectText.3}"></label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-check-label" for="author" th:text="#{changeMetadata.author}"></label>
|
||||||
|
<input type="text" class="form-control" id="author" name="author">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-check-label" for="creationDate" th:text="#{changeMetadata.creationDate}"></label>
|
||||||
|
<input type="text" class="form-control" id="creationDate" name="creationDate" placeholder="2020/12/25 18:30:59">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-check-label" for="creator" th:text="#{changeMetadata.creator}"></label>
|
||||||
|
<input type="text" class="form-control" id="creator" name="creator">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-check-label" for="keywords" th:text="#{changeMetadata.keywords}"></label>
|
||||||
|
<input type="text" class="form-control" id="keywords" name="keywords">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-check-label" for="modificationDate" th:text="#{changeMetadata.modDate}"></label>
|
||||||
|
<input type="text" class="form-control" id="modificationDate" name="modificationDate" placeholder="2020/12/25 18:30:59">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-check-label" for="producer" th:text="#{changeMetadata.producer}"></label>
|
||||||
|
<input type="text" class="form-control" id="producer" name="producer">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-check-label" for="subject" th:text="#{changeMetadata.subject}"></label>
|
||||||
|
<input type="text" class="form-control" id="subject" name="subject">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-check-label" for="title" th:text="#{changeMetadata.title}"></label>
|
||||||
|
<input type="text" class="form-control" id="title" name="title">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-check-label" for="trapped" th:text="#{changeMetadata.trapped}"></label>
|
||||||
|
<select class="form-control" id="trapped" name="trapped">
|
||||||
|
<option value="True" th:text="#{true}"></option>
|
||||||
|
<option value="False" th:text="#{false}" selected></option>
|
||||||
|
<option value="Unknown" th:text="#{unknown}"></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="customMetadata" style="display: none;">
|
||||||
|
<h3 th:text="#{changeMetadata.selectText.4}"></h3>
|
||||||
|
<div class="form-group" id="otherMetadataEntries"></div>
|
||||||
|
</div>
|
||||||
|
<div id="customMetadataEntries"></div>
|
||||||
|
<button type="button" class="btn btn-secondary" id="addMetadataBtn" th:text="#{changeMetadata.selectText.5}"></button>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<button class="btn btn-primary" type="submit" th:text="#{changeMetadata.submit}"></button>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const deleteAllCheckbox = document.querySelector("#deleteAll");
|
||||||
|
const inputs = document.querySelectorAll(".form-control");
|
||||||
|
const customMetadataDiv = document.getElementById('customMetadata');
|
||||||
|
const otherMetadataEntriesDiv = document.getElementById('otherMetadataEntries');
|
||||||
|
|
||||||
|
deleteAllCheckbox.addEventListener("change", function(event) {
|
||||||
|
if (event.target !== deleteAllCheckbox) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs.forEach(input => {
|
||||||
|
if (input === deleteAllCheckbox) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
input.disabled = deleteAllCheckbox.checked;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const customModeCheckbox = document.getElementById('customModeCheckbox');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const addMetadataBtn = document.getElementById("addMetadataBtn");
|
||||||
|
const customMetadataFormContainer = document.getElementById("customMetadataEntries");
|
||||||
|
var count = 1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const fileInput = document.querySelector("#fileInput-input");
|
||||||
|
const authorInput = document.querySelector("#author");
|
||||||
|
const creationDateInput = document.querySelector("#creationDate");
|
||||||
|
const creatorInput = document.querySelector("#creator");
|
||||||
|
const keywordsInput = document.querySelector("#keywords");
|
||||||
|
const modificationDateInput = document.querySelector("#modificationDate");
|
||||||
|
const producerInput = document.querySelector("#producer");
|
||||||
|
const subjectInput = document.querySelector("#subject");
|
||||||
|
const titleInput = document.querySelector("#title");
|
||||||
|
const trappedInput = document.querySelector("#trapped");
|
||||||
|
|
||||||
|
var lastPDFFileMeta = null;
|
||||||
|
fileInput.addEventListener("change", async function() {
|
||||||
|
|
||||||
|
while (otherMetadataEntriesDiv.firstChild) {
|
||||||
|
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
|
||||||
|
}
|
||||||
|
while (customMetadataFormContainer.firstChild) {
|
||||||
|
customMetadataFormContainer.removeChild(customMetadataFormContainer.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const file = this.files[0];
|
||||||
|
var url = URL.createObjectURL(file)
|
||||||
|
|
||||||
|
const pdf = await pdfjsLib.getDocument(url).promise;
|
||||||
|
const pdfMetadata = await pdf.getMetadata();
|
||||||
|
lastPDFFile = pdfMetadata?.info
|
||||||
|
console.log(pdfMetadata);
|
||||||
|
authorInput.value = pdfMetadata?.info?.Author;
|
||||||
|
creationDateInput.value = convertDateFormat(pdfMetadata?.info?.CreationDate);
|
||||||
|
creatorInput.value = pdfMetadata?.info?.Creator;
|
||||||
|
keywordsInput.value = pdfMetadata?.info?.Keywords;
|
||||||
|
modificationDateInput.value = convertDateFormat(pdfMetadata?.info?.ModDate);
|
||||||
|
producerInput.value = pdfMetadata?.info?.Producer;
|
||||||
|
subjectInput.value = pdfMetadata?.info?.Subject;
|
||||||
|
titleInput.value = pdfMetadata?.info?.Title;
|
||||||
|
console.log(pdfMetadata?.info);
|
||||||
|
const trappedValue = pdfMetadata?.info?.Trapped;
|
||||||
|
// Get all options in the select element
|
||||||
|
const options = trappedInput.options;
|
||||||
|
// Loop through all options to find the one with a matching value
|
||||||
|
for (let i = 0; i < options.length; i++) {
|
||||||
|
if (options[i].value === trappedValue) {
|
||||||
|
options[i].selected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addExtra();
|
||||||
|
});
|
||||||
|
|
||||||
|
addMetadataBtn.addEventListener("click", () => {
|
||||||
|
const keyInput = document.createElement("input");
|
||||||
|
keyInput.type = "text";
|
||||||
|
keyInput.placeholder = "Key";
|
||||||
|
keyInput.className = "form-control";
|
||||||
|
keyInput.name = "customKey" + count;
|
||||||
|
|
||||||
|
const valueInput = document.createElement("input");
|
||||||
|
valueInput.type = "text";
|
||||||
|
valueInput.placeholder = "Value";
|
||||||
|
valueInput.className = "form-control";
|
||||||
|
valueInput.name = "customValue" + count;
|
||||||
|
count = count + 1;
|
||||||
|
|
||||||
|
const formGroup = document.createElement("div");
|
||||||
|
formGroup.className = "form-group";
|
||||||
|
formGroup.appendChild(keyInput);
|
||||||
|
formGroup.appendChild(valueInput);
|
||||||
|
|
||||||
|
|
||||||
|
customMetadataFormContainer.appendChild(formGroup);
|
||||||
|
});
|
||||||
|
function convertDateFormat(dateTimeString) {
|
||||||
|
if (!dateTimeString || dateTimeString.length < 17) {
|
||||||
|
return dateTimeString;
|
||||||
|
}
|
||||||
|
|
||||||
|
const year = dateTimeString.substring(2, 6);
|
||||||
|
const month = dateTimeString.substring(6, 8);
|
||||||
|
const day = dateTimeString.substring(8, 10);
|
||||||
|
const hour = dateTimeString.substring(10, 12);
|
||||||
|
const minute = dateTimeString.substring(12, 14);
|
||||||
|
const second = dateTimeString.substring(14, 16);
|
||||||
|
|
||||||
|
return year + "/" + month + "/" + day + " " + hour + ":" + minute + ":" + second;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addExtra() {
|
||||||
|
const event = document.getElementById("customModeCheckbox");
|
||||||
|
|
||||||
|
|
||||||
|
if (event.checked && lastPDFFile?.Custom) {
|
||||||
|
customMetadataDiv.style.display = 'block';
|
||||||
|
for (const [key, value] of Object.entries(lastPDFFile.Custom)) {
|
||||||
|
if (key === 'Author' || key === 'CreationDate' || key === 'Creator' || key === 'Keywords' || key === 'ModDate' || key === 'Producer' || key === 'Subject' || key === 'Title' || key === 'Trapped') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const entryDiv = document.createElement('div');
|
||||||
|
entryDiv.className = 'form-group';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
entryDiv.innerHTML = `<div class="form-group"><label class="form-check-label" for="${key}">${key}:</label><input name="${key}" value="${value}" type="text" class="form-control" id="${key}"></div>`;
|
||||||
|
otherMetadataEntriesDiv.appendChild(entryDiv);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
customMetadataDiv.style.display = 'none';
|
||||||
|
while (otherMetadataEntriesDiv.firstChild) {
|
||||||
|
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
customModeCheckbox.addEventListener('change', (event) => {
|
||||||
|
|
||||||
|
addExtra();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -4,81 +4,68 @@
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{permissions.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{permissions.title})}"></th:block>
|
||||||
|
|
||||||
|
|
||||||
<body> <div id="page-container">
|
<body>
|
||||||
<div id="content-wrap">
|
<div id="page-container">
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div id="content-wrap">
|
||||||
<br>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
<br>
|
<br> <br>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2 th:text="#{permissions.header}"></h2>
|
<h2 th:text="#{permissions.header}"></h2>
|
||||||
<p th:text="#{permissions.warning}"></p>
|
<p th:text="#{permissions.warning}"></p>
|
||||||
<form action="add-password" method="post"
|
<form action="add-password" method="post" enctype="multipart/form-data">
|
||||||
enctype="multipart/form-data">
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<label th:text="#{permissions.selectText.1}"></label>
|
||||||
<label th:text="#{permissions.selectText.1}"></label>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
||||||
<div class="custom-file">
|
</div>
|
||||||
<input type="file" class="custom-file-input" id="fileInput"
|
<div class="form-group">
|
||||||
name="fileInput"> <label class="custom-file-label" th:text="#{pdfPrompt}"></label>
|
<label th:text="#{permissions.selectText.2}"></label>
|
||||||
</div>
|
<div class="form-check">
|
||||||
</div>
|
<input class="form-check-input" type="checkbox" id="canAssembleDocument" name="canAssembleDocument">
|
||||||
<div class="form-group">
|
<label class="form-check-label" for="canAssembleDocument" th:text="#{permissions.selectText.3}"></label>
|
||||||
<label th:text="#{permissions.selectText.2}"></label>
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox"
|
<input class="form-check-input" type="checkbox" id="canExtractContent" name="canExtractContent">
|
||||||
id="canAssembleDocument" name="canAssembleDocument"> <label
|
<label class="form-check-label" for="canExtractContent" th:text="#{permissions.selectText.4}"></label>
|
||||||
class="form-check-label" for="canAssembleDocument" th:text="#{permissions.selectText.3}"></label>
|
</div>
|
||||||
</div>
|
<div class="form-check">
|
||||||
<div class="form-check">
|
<input class="form-check-input" type="checkbox" id="canExtractForAccessibility" name="canExtractForAccessibility">
|
||||||
<input class="form-check-input" type="checkbox"
|
<label class="form-check-label" for="canExtractForAccessibility" th:text="#{permissions.selectText.5}"></label>
|
||||||
id="canExtractContent" name="canExtractContent"> <label
|
</div>
|
||||||
class="form-check-label" for="canExtractContent" th:text="#{permissions.selectText.4}"></label>
|
<div class="form-check">
|
||||||
</div>
|
<input class="form-check-input" type="checkbox" id="canFillInForm" name="canFillInForm">
|
||||||
<div class="form-check">
|
<label class="form-check-label" for="canFillInForm" th:text="#{permissions.selectText.6}"></label>
|
||||||
<input class="form-check-input" type="checkbox"
|
</div>
|
||||||
id="canExtractForAccessibility"
|
<div class="form-check">
|
||||||
name="canExtractForAccessibility"> <label
|
<input class="form-check-input" type="checkbox" id="canModify" name="canModify">
|
||||||
class="form-check-label" for="canExtractForAccessibility" th:text="#{permissions.selectText.5}"></label>
|
<label class="form-check-label" for="canModify" th:text="#{permissions.selectText.7}"></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox"
|
<input class="form-check-input" type="checkbox" id="canModifyAnnotations" name="canModifyAnnotations">
|
||||||
id="canFillInForm" name="canFillInForm"> <label
|
<label class="form-check-label" for="canModifyAnnotations" th:text="#{permissions.selectText.8}"></label>
|
||||||
class="form-check-label" for="canFillInForm" th:text="#{permissions.selectText.6}"></label>
|
</div>
|
||||||
</div>
|
<div class="form-check">
|
||||||
<div class="form-check">
|
<input class="form-check-input" type="checkbox" id="canPrint" name="canPrint">
|
||||||
<input class="form-check-input" type="checkbox" id="canModify"
|
<label class="form-check-label" for="canPrint" th:text="#{permissions.selectText.9}"></label>
|
||||||
name="canModify"> <label class="form-check-label"
|
</div>
|
||||||
for="canModify" th:text="#{permissions.selectText.7}"></label>
|
<div class="form-check">
|
||||||
</div>
|
<input class="form-check-input" type="checkbox" id="canPrintFaithful" name="canPrintFaithful">
|
||||||
<div class="form-check">
|
<label class="form-check-label" for="canPrintFaithful" th:text="#{permissions.selectText.10}"></label>
|
||||||
<input class="form-check-input" type="checkbox"
|
</div>
|
||||||
id="canModifyAnnotations" name="canModifyAnnotations"> <label
|
|
||||||
class="form-check-label" for="canModifyAnnotations" th:text="#{permissions.selectText.8}"></label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check">
|
|
||||||
<input class="form-check-input" type="checkbox" id="canPrint"
|
|
||||||
name="canPrint"> <label class="form-check-label"
|
|
||||||
for="canPrint" th:text="#{permissions.selectText.9}"></label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check">
|
|
||||||
<input class="form-check-input" type="checkbox"
|
|
||||||
id="canPrintFaithful" name="canPrintFaithful"> <label
|
|
||||||
class="form-check-label" for="canPrintFaithful" th:text="#{permissions.selectText.10}"></label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div class="form-group text-center">
|
<div class="form-group text-center">
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{permissions.submit}"></button>
|
<button type="submit" class="btn btn-primary" th:text="#{permissions.submit}"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -3,40 +3,35 @@
|
||||||
|
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{removePassword.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{removePassword.title})}"></th:block>
|
||||||
|
|
||||||
<body> <div id="page-container">
|
<body>
|
||||||
<div id="content-wrap">
|
<div id="page-container">
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div id="content-wrap">
|
||||||
<br>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
<br>
|
<br> <br>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2 th:text="#{removePassword.header}"></h2>
|
<h2 th:text="#{removePassword.header}"></h2>
|
||||||
|
|
||||||
<form action="add-password" method="post"
|
<form action="add-password" method="post" enctype="multipart/form-data">
|
||||||
enctype="multipart/form-data">
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<label th:text="#{removePassword.selectText.1}"></label>
|
||||||
<label th:text="#{removePassword.selectText.1}"></label>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
||||||
<div class="custom-file">
|
</div>
|
||||||
<input type="file" class="custom-file-input" id="fileInput"
|
<div class="form-group">
|
||||||
name="fileInput" required> <label
|
<label th:text="#{removePassword.selectText.2}"></label>
|
||||||
class="custom-file-label" th:text="#{pdfPrompt}"></label>
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<br />
|
||||||
<div class="form-group">
|
<div class="form-group text-center">
|
||||||
<label th:text="#{removePassword.selectText.2}"></label> <input type="password"
|
<button type="submit" class="btn btn-primary" th:text="#{removePassword.submit}"></button>
|
||||||
class="form-control" id="password" name="password" required>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
<br />
|
</div>
|
||||||
<div class="form-group text-center">
|
</div>
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{removePassword.submit}"></button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -6,49 +6,39 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div id="page-container">
|
<div id="page-container">
|
||||||
<div id="content-wrap">
|
<div id="content-wrap">
|
||||||
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
|
<br> <br>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h1 th:text="#{split.header}"></h1>
|
||||||
|
<p th:text="#{split.desc.1}"></p>
|
||||||
|
<p th:text="#{split.desc.2}"></p>
|
||||||
|
<p th:text="#{split.desc.3}"></p>
|
||||||
|
<p th:text="#{split.desc.4}"></p>
|
||||||
|
<p th:text="#{split.desc.5}"></p>
|
||||||
|
<p th:text="#{split.desc.6}"></p>
|
||||||
|
<p th:text="#{split.desc.7}"></p>
|
||||||
|
<p th:text="#{split.desc.8}"></p>
|
||||||
|
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<form th:action="@{split-pages}" method="post" enctype="multipart/form-data">
|
||||||
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
||||||
|
|
||||||
<br>
|
<div class="form-group">
|
||||||
<br>
|
<label for="pages" th:text="#{split.splitPages}"></label>
|
||||||
|
<input type="text" class="form-control" id="pages" name="pages" placeholder="1,3,5-10" required>
|
||||||
<div class="container">
|
</div>
|
||||||
<div class="row justify-content-center">
|
<br>
|
||||||
<div class="col-md-6">
|
<button type="submit" class="btn btn-primary" th:text="#{split.submit}"></button>
|
||||||
<h1 th:text="#{split.header}"></h1>
|
</form>
|
||||||
|
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
||||||
<p th:text="#{split.desc.1}"></p>
|
</div>
|
||||||
<p th:text="#{split.desc.2}"></p>
|
</div>
|
||||||
<p th:text="#{split.desc.3}"></p>
|
</div>
|
||||||
<p th:text="#{split.desc.4}"></p>
|
</div>
|
||||||
<p th:text="#{split.desc.5}"></p>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
<p th:text="#{split.desc.6}"></p>
|
</div>
|
||||||
<p th:text="#{split.desc.7}"></p>
|
|
||||||
<p th:text="#{split.desc.8}"></p>
|
|
||||||
|
|
||||||
<form th:action="@{split-pages}" 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" th:text="#{pdfPrompt}"></label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="pages" th:text="#{split.splitPages}"></label>
|
|
||||||
<input type="text" class="form-control" id="pages" name="pages" placeholder="1,3,5-10" required>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{split.submit}"></button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
Reference in a new issue