1
0
mirror of https://github.com/Stirling-Tools/Stirling-PDF.git synced 2024-11-17 12:40:11 +01:00

language cleanups and sanitize

This commit is contained in:
Anthony Stirling 2023-07-16 18:57:21 +01:00
parent d07e3e6522
commit 92b9142902
13 changed files with 1120 additions and 871 deletions

View File

@ -1,200 +1,208 @@
package stirling.software.SPDF.config; package stirling.software.SPDF.config;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
public class EndpointConfiguration { public class EndpointConfiguration {
private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class); private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class);
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>(); private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>(); private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>();
public EndpointConfiguration() { public EndpointConfiguration() {
init(); init();
processEnvironmentConfigs(); processEnvironmentConfigs();
} }
public void enableEndpoint(String endpoint) { public void enableEndpoint(String endpoint) {
endpointStatuses.put(endpoint, true); endpointStatuses.put(endpoint, true);
} }
public void disableEndpoint(String endpoint) { public void disableEndpoint(String endpoint) {
if(!endpointStatuses.containsKey(endpoint) || endpointStatuses.get(endpoint) != false) { if(!endpointStatuses.containsKey(endpoint) || endpointStatuses.get(endpoint) != false) {
logger.info("Disabling {}", endpoint); logger.info("Disabling {}", endpoint);
endpointStatuses.put(endpoint, false); endpointStatuses.put(endpoint, false);
} }
} }
public boolean isEndpointEnabled(String endpoint) { public boolean isEndpointEnabled(String endpoint) {
if (endpoint.startsWith("/")) { if (endpoint.startsWith("/")) {
endpoint = endpoint.substring(1); endpoint = endpoint.substring(1);
} }
return endpointStatuses.getOrDefault(endpoint, true); return endpointStatuses.getOrDefault(endpoint, true);
} }
public void addEndpointToGroup(String group, String endpoint) { public void addEndpointToGroup(String group, String endpoint) {
endpointGroups.computeIfAbsent(group, k -> new HashSet<>()).add(endpoint); endpointGroups.computeIfAbsent(group, k -> new HashSet<>()).add(endpoint);
} }
public void enableGroup(String group) { public void enableGroup(String group) {
Set<String> endpoints = endpointGroups.get(group); Set<String> endpoints = endpointGroups.get(group);
if (endpoints != null) { if (endpoints != null) {
for (String endpoint : endpoints) { for (String endpoint : endpoints) {
enableEndpoint(endpoint); enableEndpoint(endpoint);
} }
} }
} }
public void disableGroup(String group) { public void disableGroup(String group) {
Set<String> endpoints = endpointGroups.get(group); Set<String> endpoints = endpointGroups.get(group);
if (endpoints != null) { if (endpoints != null) {
for (String endpoint : endpoints) { for (String endpoint : endpoints) {
disableEndpoint(endpoint); disableEndpoint(endpoint);
} }
} }
} }
public void init() { public void init() {
// Adding endpoints to "PageOps" group // Adding endpoints to "PageOps" group
addEndpointToGroup("PageOps", "remove-pages"); addEndpointToGroup("PageOps", "remove-pages");
addEndpointToGroup("PageOps", "merge-pdfs"); addEndpointToGroup("PageOps", "merge-pdfs");
addEndpointToGroup("PageOps", "split-pdfs"); addEndpointToGroup("PageOps", "split-pdfs");
addEndpointToGroup("PageOps", "pdf-organizer"); addEndpointToGroup("PageOps", "pdf-organizer");
addEndpointToGroup("PageOps", "rotate-pdf"); addEndpointToGroup("PageOps", "rotate-pdf");
addEndpointToGroup("PageOps", "multi-page-layout"); addEndpointToGroup("PageOps", "multi-page-layout");
addEndpointToGroup("PageOps", "scale-pages"); addEndpointToGroup("PageOps", "scale-pages");
addEndpointToGroup("PageOps", "adjust-contrast");
// Adding endpoints to "Convert" group addEndpointToGroup("PageOps", "crop");
addEndpointToGroup("Convert", "pdf-to-img"); addEndpointToGroup("PageOps", "auto-split-pdf");
addEndpointToGroup("Convert", "img-to-pdf");
addEndpointToGroup("Convert", "pdf-to-pdfa"); // Adding endpoints to "Convert" group
addEndpointToGroup("Convert", "file-to-pdf"); addEndpointToGroup("Convert", "pdf-to-img");
addEndpointToGroup("Convert", "xlsx-to-pdf"); addEndpointToGroup("Convert", "img-to-pdf");
addEndpointToGroup("Convert", "pdf-to-word"); addEndpointToGroup("Convert", "pdf-to-pdfa");
addEndpointToGroup("Convert", "pdf-to-presentation"); addEndpointToGroup("Convert", "file-to-pdf");
addEndpointToGroup("Convert", "pdf-to-text"); addEndpointToGroup("Convert", "xlsx-to-pdf");
addEndpointToGroup("Convert", "pdf-to-html"); addEndpointToGroup("Convert", "pdf-to-word");
addEndpointToGroup("Convert", "pdf-to-xml"); addEndpointToGroup("Convert", "pdf-to-presentation");
addEndpointToGroup("Convert", "pdf-to-text");
// Adding endpoints to "Security" group addEndpointToGroup("Convert", "pdf-to-html");
addEndpointToGroup("Security", "add-password"); addEndpointToGroup("Convert", "pdf-to-xml");
addEndpointToGroup("Security", "remove-password");
addEndpointToGroup("Security", "change-permissions"); // Adding endpoints to "Security" group
addEndpointToGroup("Security", "add-watermark"); addEndpointToGroup("Security", "add-password");
addEndpointToGroup("Security", "cert-sign"); addEndpointToGroup("Security", "remove-password");
addEndpointToGroup("Security", "change-permissions");
addEndpointToGroup("Security", "add-watermark");
addEndpointToGroup("Security", "cert-sign");
// Adding endpoints to "Other" group addEndpointToGroup("Security", "sanitize-pdf");
addEndpointToGroup("Other", "ocr-pdf");
addEndpointToGroup("Other", "add-image");
addEndpointToGroup("Other", "compress-pdf"); // Adding endpoints to "Other" group
addEndpointToGroup("Other", "extract-images"); addEndpointToGroup("Other", "ocr-pdf");
addEndpointToGroup("Other", "change-metadata"); addEndpointToGroup("Other", "add-image");
addEndpointToGroup("Other", "extract-image-scans"); addEndpointToGroup("Other", "compress-pdf");
addEndpointToGroup("Other", "sign"); addEndpointToGroup("Other", "extract-images");
addEndpointToGroup("Other", "flatten"); addEndpointToGroup("Other", "change-metadata");
addEndpointToGroup("Other", "repair"); addEndpointToGroup("Other", "extract-image-scans");
addEndpointToGroup("Other", "remove-blanks"); addEndpointToGroup("Other", "sign");
addEndpointToGroup("Other", "compare"); addEndpointToGroup("Other", "flatten");
addEndpointToGroup("Other", "repair");
addEndpointToGroup("Other", "remove-blanks");
addEndpointToGroup("Other", "compare");
addEndpointToGroup("Other", "add-page-numbers");
addEndpointToGroup("Other", "auto-rename");
//CLI
addEndpointToGroup("CLI", "compress-pdf");
addEndpointToGroup("CLI", "extract-image-scans"); //CLI
addEndpointToGroup("CLI", "remove-blanks"); addEndpointToGroup("CLI", "compress-pdf");
addEndpointToGroup("CLI", "repair"); addEndpointToGroup("CLI", "extract-image-scans");
addEndpointToGroup("CLI", "pdf-to-pdfa"); addEndpointToGroup("CLI", "remove-blanks");
addEndpointToGroup("CLI", "file-to-pdf"); addEndpointToGroup("CLI", "repair");
addEndpointToGroup("CLI", "xlsx-to-pdf"); addEndpointToGroup("CLI", "pdf-to-pdfa");
addEndpointToGroup("CLI", "pdf-to-word"); addEndpointToGroup("CLI", "file-to-pdf");
addEndpointToGroup("CLI", "pdf-to-presentation"); addEndpointToGroup("CLI", "xlsx-to-pdf");
addEndpointToGroup("CLI", "pdf-to-text"); addEndpointToGroup("CLI", "pdf-to-word");
addEndpointToGroup("CLI", "pdf-to-html"); addEndpointToGroup("CLI", "pdf-to-presentation");
addEndpointToGroup("CLI", "pdf-to-xml"); addEndpointToGroup("CLI", "pdf-to-text");
addEndpointToGroup("CLI", "ocr-pdf"); addEndpointToGroup("CLI", "pdf-to-html");
addEndpointToGroup("CLI", "pdf-to-xml");
//python addEndpointToGroup("CLI", "ocr-pdf");
addEndpointToGroup("Python", "extract-image-scans");
addEndpointToGroup("Python", "remove-blanks"); //python
addEndpointToGroup("Python", "extract-image-scans");
addEndpointToGroup("Python", "remove-blanks");
//openCV
addEndpointToGroup("OpenCV", "extract-image-scans");
addEndpointToGroup("OpenCV", "remove-blanks"); //openCV
addEndpointToGroup("OpenCV", "extract-image-scans");
//LibreOffice addEndpointToGroup("OpenCV", "remove-blanks");
addEndpointToGroup("LibreOffice", "repair");
addEndpointToGroup("LibreOffice", "file-to-pdf"); //LibreOffice
addEndpointToGroup("LibreOffice", "xlsx-to-pdf"); addEndpointToGroup("LibreOffice", "repair");
addEndpointToGroup("LibreOffice", "pdf-to-word"); addEndpointToGroup("LibreOffice", "file-to-pdf");
addEndpointToGroup("LibreOffice", "pdf-to-presentation"); addEndpointToGroup("LibreOffice", "xlsx-to-pdf");
addEndpointToGroup("LibreOffice", "pdf-to-text"); addEndpointToGroup("LibreOffice", "pdf-to-word");
addEndpointToGroup("LibreOffice", "pdf-to-html"); addEndpointToGroup("LibreOffice", "pdf-to-presentation");
addEndpointToGroup("LibreOffice", "pdf-to-xml"); addEndpointToGroup("LibreOffice", "pdf-to-text");
addEndpointToGroup("LibreOffice", "pdf-to-html");
addEndpointToGroup("LibreOffice", "pdf-to-xml");
//OCRmyPDF
addEndpointToGroup("OCRmyPDF", "compress-pdf");
addEndpointToGroup("OCRmyPDF", "pdf-to-pdfa"); //OCRmyPDF
addEndpointToGroup("OCRmyPDF", "ocr-pdf"); addEndpointToGroup("OCRmyPDF", "compress-pdf");
addEndpointToGroup("OCRmyPDF", "pdf-to-pdfa");
//Java addEndpointToGroup("OCRmyPDF", "ocr-pdf");
addEndpointToGroup("Java", "merge-pdfs");
addEndpointToGroup("Java", "remove-pages"); //Java
addEndpointToGroup("Java", "split-pdfs"); addEndpointToGroup("Java", "merge-pdfs");
addEndpointToGroup("Java", "pdf-organizer"); addEndpointToGroup("Java", "remove-pages");
addEndpointToGroup("Java", "rotate-pdf"); addEndpointToGroup("Java", "split-pdfs");
addEndpointToGroup("Java", "pdf-to-img"); addEndpointToGroup("Java", "pdf-organizer");
addEndpointToGroup("Java", "img-to-pdf"); addEndpointToGroup("Java", "rotate-pdf");
addEndpointToGroup("Java", "add-password"); addEndpointToGroup("Java", "pdf-to-img");
addEndpointToGroup("Java", "remove-password"); addEndpointToGroup("Java", "img-to-pdf");
addEndpointToGroup("Java", "change-permissions"); addEndpointToGroup("Java", "add-password");
addEndpointToGroup("Java", "add-watermark"); addEndpointToGroup("Java", "remove-password");
addEndpointToGroup("Java", "add-image"); addEndpointToGroup("Java", "change-permissions");
addEndpointToGroup("Java", "extract-images"); addEndpointToGroup("Java", "add-watermark");
addEndpointToGroup("Java", "change-metadata"); addEndpointToGroup("Java", "add-image");
addEndpointToGroup("Java", "cert-sign"); addEndpointToGroup("Java", "extract-images");
addEndpointToGroup("Java", "multi-page-layout"); addEndpointToGroup("Java", "change-metadata");
addEndpointToGroup("Java", "scale-pages"); addEndpointToGroup("Java", "cert-sign");
addEndpointToGroup("Java", "multi-page-layout");
addEndpointToGroup("Java", "scale-pages");
//Javascript addEndpointToGroup("Java", "add-page-numbers");
addEndpointToGroup("Javascript", "pdf-organizer"); addEndpointToGroup("Java", "auto-rename");
addEndpointToGroup("Javascript", "sign"); addEndpointToGroup("Java", "auto-split-pdf");
addEndpointToGroup("Javascript", "compare"); addEndpointToGroup("Java", "sanitize-pdf");
addEndpointToGroup("Java", "crop");
}
//Javascript
private void processEnvironmentConfigs() { addEndpointToGroup("Javascript", "pdf-organizer");
String endpointsToRemove = System.getenv("ENDPOINTS_TO_REMOVE"); addEndpointToGroup("Javascript", "sign");
String groupsToRemove = System.getenv("GROUPS_TO_REMOVE"); addEndpointToGroup("Javascript", "compare");
addEndpointToGroup("Javascript", "adjust-contrast");
if (endpointsToRemove != null) {
String[] endpoints = endpointsToRemove.split(",");
for (String endpoint : endpoints) { }
disableEndpoint(endpoint.trim());
} private void processEnvironmentConfigs() {
} String endpointsToRemove = System.getenv("ENDPOINTS_TO_REMOVE");
String groupsToRemove = System.getenv("GROUPS_TO_REMOVE");
if (groupsToRemove != null) {
String[] groups = groupsToRemove.split(","); if (endpointsToRemove != null) {
for (String group : groups) { String[] endpoints = endpointsToRemove.split(",");
disableGroup(group.trim()); for (String endpoint : endpoints) {
} disableEndpoint(endpoint.trim());
} }
} }
} if (groupsToRemove != null) {
String[] groups = groupsToRemove.split(",");
for (String group : groups) {
disableGroup(group.trim());
}
}
}
}

View File

@ -1,176 +1,176 @@
package stirling.software.SPDF.controller.api.other; package stirling.software.SPDF.controller.api.other;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.utils.GeneralUtils; import stirling.software.SPDF.utils.GeneralUtils;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
import org.apache.pdfbox.pdmodel.*; import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.common.*; import org.apache.pdfbox.pdmodel.common.*;
import org.apache.pdfbox.pdmodel.PDPageContentStream.*; import org.apache.pdfbox.pdmodel.PDPageContentStream.*;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.http.*; import org.springframework.http.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.*; import io.swagger.v3.oas.annotations.*;
import io.swagger.v3.oas.annotations.media.*; import io.swagger.v3.oas.annotations.media.*;
import io.swagger.v3.oas.annotations.parameters.*; import io.swagger.v3.oas.annotations.parameters.*;
import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.tomcat.util.http.ResponseUtil; import org.apache.tomcat.util.http.ResponseUtil;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.List; import java.util.List;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.itextpdf.io.font.constants.StandardFonts; import com.itextpdf.io.font.constants.StandardFonts;
import com.itextpdf.kernel.font.PdfFont; import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory; import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.Rectangle; import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfReader; import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage; import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas; import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas; import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.element.Paragraph; import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.properties.TextAlignment; import com.itextpdf.layout.properties.TextAlignment;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import java.io.*; import java.io.*;
@RestController @RestController
@Tag(name = "Other", description = "Other APIs") @Tag(name = "Other", description = "Other APIs")
public class PageNumbersController { public class PageNumbersController {
private static final Logger logger = LoggerFactory.getLogger(PageNumbersController.class); private static final Logger logger = LoggerFactory.getLogger(PageNumbersController.class);
@PostMapping(value = "/add-page-numbers", consumes = "multipart/form-data") @PostMapping(value = "/add-page-numbers", consumes = "multipart/form-data")
@Operation(summary = "Add page numbers to a PDF document", description = "This operation takes an input PDF file and adds page numbers to it. Input:PDF Output:PDF Type:SISO") @Operation(summary = "Add page numbers to a PDF document", description = "This operation takes an input PDF file and adds page numbers to it. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<byte[]> addPageNumbers( public ResponseEntity<byte[]> addPageNumbers(
@Parameter(description = "The input PDF file", required = true) @RequestParam("fileInput") MultipartFile file, @Parameter(description = "The input PDF file", required = true) @RequestParam("fileInput") MultipartFile file,
@Parameter(description = "Custom margin: small/medium/large", required = true, schema = @Schema(type = "string", allowableValues = {"small", "medium", "large"})) @RequestParam("customMargin") String customMargin, @Parameter(description = "Custom margin: small/medium/large", required = true, schema = @Schema(type = "string", allowableValues = {"small", "medium", "large"})) @RequestParam("customMargin") String customMargin,
@Parameter(description = "Position: 1 of 9 positions", required = true, schema = @Schema(type = "integer", minimum = "1", maximum = "9")) @RequestParam("position") int position, @Parameter(description = "Position: 1 of 9 positions", required = true, schema = @Schema(type = "integer", minimum = "1", maximum = "9")) @RequestParam("position") int position,
@Parameter(description = "Starting number", required = true, schema = @Schema(type = "integer", minimum = "1")) @RequestParam("startingNumber") int startingNumber, @Parameter(description = "Starting number", required = true, schema = @Schema(type = "integer", minimum = "1")) @RequestParam("startingNumber") int startingNumber,
@Parameter(description = "Which pages to number, default all", required = false, schema = @Schema(type = "string")) @RequestParam(value = "pagesToNumber", required = false) String pagesToNumber, @Parameter(description = "Which pages to number, default all", required = false, schema = @Schema(type = "string")) @RequestParam(value = "pagesToNumber", required = false) String pagesToNumber,
@Parameter(description = "Custom text: defaults to just number but can have things like \"Page {n} of {p}\"", required = false, schema = @Schema(type = "string")) @RequestParam(value = "customText", required = false) String customText) @Parameter(description = "Custom text: defaults to just number but can have things like \"Page {n} of {p}\"", required = false, schema = @Schema(type = "string")) @RequestParam(value = "customText", required = false) String customText)
throws IOException { throws IOException {
byte[] fileBytes = file.getBytes(); byte[] fileBytes = file.getBytes();
ByteArrayInputStream bais = new ByteArrayInputStream(fileBytes); ByteArrayInputStream bais = new ByteArrayInputStream(fileBytes);
int pageNumber = startingNumber; int pageNumber = startingNumber;
float marginFactor; float marginFactor;
switch (customMargin.toLowerCase()) { switch (customMargin.toLowerCase()) {
case "small": case "small":
marginFactor = 0.01f; marginFactor = 0.02f;
break; break;
case "medium": case "medium":
marginFactor = 0.025f; marginFactor = 0.035f;
break; break;
case "large": case "large":
marginFactor = 0.05f; marginFactor = 0.05f;
break; break;
case "x-large": case "x-large":
marginFactor = 0.1f; marginFactor = 0.1f;
break; break;
default: default:
marginFactor = 0.01f; marginFactor = 0.035f;
break; break;
} }
float fontSize = 12.0f; float fontSize = 12.0f;
PdfReader reader = new PdfReader(bais); PdfReader reader = new PdfReader(bais);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter writer = new PdfWriter(baos); PdfWriter writer = new PdfWriter(baos);
PdfDocument pdfDoc = new PdfDocument(reader, writer); PdfDocument pdfDoc = new PdfDocument(reader, writer);
List<Integer> pagesToNumberList = GeneralUtils.parsePageList(pagesToNumber.split(","), pdfDoc.getNumberOfPages()); List<Integer> pagesToNumberList = GeneralUtils.parsePageList(pagesToNumber.split(","), pdfDoc.getNumberOfPages());
for (int i : pagesToNumberList) { for (int i : pagesToNumberList) {
PdfPage page = pdfDoc.getPage(i+1); PdfPage page = pdfDoc.getPage(i+1);
Rectangle pageSize = page.getPageSize(); Rectangle pageSize = page.getPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), pdfDoc); PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), pdfDoc);
String text = customText != null ? customText.replace("{n}", String.valueOf(pageNumber)).replace("{total}", String.valueOf(pdfDoc.getNumberOfPages())) : String.valueOf(pageNumber); String text = customText != null ? customText.replace("{n}", String.valueOf(pageNumber)).replace("{total}", String.valueOf(pdfDoc.getNumberOfPages())) : String.valueOf(pageNumber);
PdfFont font = PdfFontFactory.createFont(StandardFonts.HELVETICA); PdfFont font = PdfFontFactory.createFont(StandardFonts.HELVETICA);
float textWidth = font.getWidth(text, fontSize); float textWidth = font.getWidth(text, fontSize);
float textHeight = font.getAscent(text, fontSize) - font.getDescent(text, fontSize); float textHeight = font.getAscent(text, fontSize) - font.getDescent(text, fontSize);
float x, y; float x, y;
TextAlignment alignment; TextAlignment alignment;
int xGroup = (position - 1) % 3; int xGroup = (position - 1) % 3;
int yGroup = 2 - (position - 1) / 3; int yGroup = 2 - (position - 1) / 3;
switch (xGroup) { switch (xGroup) {
case 0: // left case 0: // left
x = pageSize.getLeft() + marginFactor * pageSize.getWidth(); x = pageSize.getLeft() + marginFactor * pageSize.getWidth();
alignment = TextAlignment.LEFT; alignment = TextAlignment.LEFT;
break; break;
case 1: // center case 1: // center
x = pageSize.getLeft() + (pageSize.getWidth()) / 2; x = pageSize.getLeft() + (pageSize.getWidth()) / 2;
alignment = TextAlignment.CENTER; alignment = TextAlignment.CENTER;
break; break;
default: // right default: // right
x = pageSize.getRight() - marginFactor * pageSize.getWidth(); x = pageSize.getRight() - marginFactor * pageSize.getWidth();
alignment = TextAlignment.RIGHT; alignment = TextAlignment.RIGHT;
break; break;
} }
switch (yGroup) { switch (yGroup) {
case 0: // bottom case 0: // bottom
y = pageSize.getBottom() + marginFactor * pageSize.getHeight(); y = pageSize.getBottom() + marginFactor * pageSize.getHeight();
break; break;
case 1: // middle case 1: // middle
y = pageSize.getBottom() + (pageSize.getHeight() ) / 2; y = pageSize.getBottom() + (pageSize.getHeight() ) / 2;
break; break;
default: // top default: // top
y = pageSize.getTop() - marginFactor * pageSize.getHeight(); y = pageSize.getTop() - marginFactor * pageSize.getHeight();
break; break;
} }
new Canvas(pdfCanvas, page.getPageSize()) new Canvas(pdfCanvas, page.getPageSize())
.showTextAligned(new Paragraph(text).setFont(font).setFontSize(fontSize), x, y, alignment); .showTextAligned(new Paragraph(text).setFont(font).setFontSize(fontSize), x, y, alignment);
pageNumber++; pageNumber++;
} }
pdfDoc.close(); pdfDoc.close();
byte[] resultBytes = baos.toByteArray(); byte[] resultBytes = baos.toByteArray();
return ResponseEntity.ok() return ResponseEntity.ok()
.header("Content-Type", "application/pdf; charset=UTF-8") .header("Content-Type", "application/pdf; charset=UTF-8")
.header("Content-Disposition", "inline; filename=" + URLEncoder.encode(file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_numbersAdded.pdf", "UTF-8")) .header("Content-Disposition", "inline; filename=" + URLEncoder.encode(file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_numbersAdded.pdf", "UTF-8"))
.body(resultBytes); .body(resultBytes);
} }
} }

View File

@ -0,0 +1,140 @@
package stirling.software.SPDF.controller.api.security;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.PDPageTree;
import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.interactive.action.*;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.interactive.form.PDNonTerminalField;
import org.apache.pdfbox.pdmodel.interactive.form.PDTerminalField;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import stirling.software.SPDF.utils.WebResponseUtils;
import java.io.IOException;
import java.io.InputStream;
@RestController
public class SanitizeController {
@PostMapping(consumes = "multipart/form-data", value = "/sanitize-pdf")
@Operation(summary = "Sanitize a PDF file",
description = "This endpoint processes a PDF file and removes specific elements based on the provided options. Input:PDF Output:PDF Type:SISO")
public ResponseEntity<byte[]> sanitizePDF(
@RequestPart(required = true, value = "fileInput")
@Parameter(description = "The input PDF file to be sanitized")
MultipartFile inputFile,
@RequestParam(name = "removeJavaScript", required = false, defaultValue = "true")
@Parameter(description = "Remove JavaScript actions from the PDF if set to true")
Boolean removeJavaScript,
@RequestParam(name = "removeEmbeddedFiles", required = false, defaultValue = "true")
@Parameter(description = "Remove embedded files from the PDF if set to true")
Boolean removeEmbeddedFiles,
@RequestParam(name = "removeMetadata", required = false, defaultValue = "true")
@Parameter(description = "Remove metadata from the PDF if set to true")
Boolean removeMetadata,
@RequestParam(name = "removeLinks", required = false, defaultValue = "true")
@Parameter(description = "Remove links from the PDF if set to true")
Boolean removeLinks,
@RequestParam(name = "removeFonts", required = false, defaultValue = "true")
@Parameter(description = "Remove fonts from the PDF if set to true")
Boolean removeFonts) throws IOException {
try (PDDocument document = PDDocument.load(inputFile.getInputStream())) {
if (removeJavaScript) {
sanitizeJavaScript(document);
}
if (removeEmbeddedFiles) {
sanitizeEmbeddedFiles(document);
}
if (removeMetadata) {
sanitizeMetadata(document);
}
if (removeLinks) {
sanitizeLinks(document);
}
if (removeFonts) {
sanitizeFonts(document);
}
return WebResponseUtils.pdfDocToWebResponse(document, inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_sanitized.pdf");
}
}
private void sanitizeJavaScript(PDDocument document) throws IOException {
for (PDPage page : document.getPages()) {
for (PDAnnotation annotation : page.getAnnotations()) {
if (annotation instanceof PDAnnotationWidget) {
PDAnnotationWidget widget = (PDAnnotationWidget) annotation;
PDAction action = widget.getAction();
if (action instanceof PDActionJavaScript) {
widget.setAction(null);
}
}
}
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
if (acroForm != null) {
for (PDField field : acroForm.getFields()) {
if (field.getActions().getF() instanceof PDActionJavaScript) {
field.getActions().setF(null);
}
}
}
}
}
private void sanitizeEmbeddedFiles(PDDocument document) {
PDPageTree allPages = document.getPages();
for (PDPage page : allPages) {
PDResources res = page.getResources();
// Remove embedded files from the PDF
res.getCOSObject().removeItem(COSName.getPDFName("EmbeddedFiles"));
}
}
private void sanitizeMetadata(PDDocument document) {
PDMetadata metadata = document.getDocumentCatalog().getMetadata();
if (metadata != null) {
document.getDocumentCatalog().setMetadata(null);
}
}
private void sanitizeLinks(PDDocument document) throws IOException {
for (PDPage page : document.getPages()) {
for (PDAnnotation annotation : page.getAnnotations()) {
if (annotation instanceof PDAnnotationLink) {
PDAction action = ((PDAnnotationLink) annotation).getAction();
if (action instanceof PDActionLaunch || action instanceof PDActionURI) {
((PDAnnotationLink) annotation).setAction(null);
}
}
}
}
}
private void sanitizeFonts(PDDocument document) {
for (PDPage page : document.getPages()) {
page.getResources().getCOSObject().removeItem(COSName.getPDFName("Font"));
}
}
}

View File

@ -1,46 +1,53 @@
package stirling.software.SPDF.controller.web; package stirling.software.SPDF.controller.web;
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 io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@Controller @Controller
@Tag(name = "Security", description = "Security APIs") @Tag(name = "Security", description = "Security APIs")
public class SecurityWebController { public class SecurityWebController {
@GetMapping("/add-password") @GetMapping("/add-password")
@Hidden @Hidden
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("/change-permissions") @GetMapping("/change-permissions")
@Hidden @Hidden
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";
} }
@GetMapping("/remove-password") @GetMapping("/remove-password")
@Hidden @Hidden
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("/add-watermark") @GetMapping("/add-watermark")
@Hidden @Hidden
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";
} }
@GetMapping("/cert-sign") @GetMapping("/cert-sign")
@Hidden @Hidden
public String certSignForm(Model model) { public String certSignForm(Model model) {
model.addAttribute("currentPage", "cert-sign"); model.addAttribute("currentPage", "cert-sign");
return "security/cert-sign"; return "security/cert-sign";
} }
}
@GetMapping("/sanitize-pdf")
@Hidden
public String sanitizeForm(Model model) {
model.addAttribute("currentPage", "");
return "security/sanitize-pdf";
}
}

View File

@ -152,6 +152,10 @@ home.crop.desc=Crop a PDF to reduce its size (maintains text!)
home.autoSplitPDF.title=Auto Split Pages home.autoSplitPDF.title=Auto Split Pages
home.autoSplitPDF.desc=Auto Split Scanned PDF with physical scanned page splitter QR Code home.autoSplitPDF.desc=Auto Split Scanned PDF with physical scanned page splitter QR Code
home.sanitizePdf.title=Sanitize
home.sanitizePdf.desc=Remove scripts and other elements from PDF files
error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect
downloadPdf=Download PDF downloadPdf=Download PDF
@ -159,6 +163,49 @@ text=Text
font=Font font=Font
selectFillter=-- Select -- selectFillter=-- Select --
pageNum=Page Number pageNum=Page Number
sizes.small=Small
sizes.medium=Medium
sizes.large=Large
sizes.x-large=X-Large
sanitizePDF.title=Sanitize PDF
sanitizePDF.header=Sanitize a PDF file
sanitizePDF.selectText.1=Remove JavaScript actions
sanitizePDF.selectText.2=Remove embedded files
sanitizePDF.selectText.3=Remove metadata
sanitizePDF.selectText.4=Remove links
sanitizePDF.selectText.5=Remove fonts
sanitizePDF.submit=Sanitize PDF
addPageNumbers.title=Add Page Numbers
addPageNumbers.header=Add Page Numbers
addPageNumbers.selectText.1=Select PDF file:
addPageNumbers.selectText.2=Margin Size
addPageNumbers.selectText.3=Position
addPageNumbers.selectText.4=Starting Number
addPageNumbers.selectText.5=Pages to Number
addPageNumbers.selectText.6=Custom Text
addPageNumbers.submit=Add Page Numbers
auto-rename.title=Auto Rename
auto-rename.header=Auto Rename PDF
auto-rename.submit=Auto Rename
adjustContrast.title=Adjust Contrast
adjustContrast.header=Adjust Contrast
adjustContrast.contrast=Contrast:
adjustContrast.brightness=Brightness:
adjustContrast.saturation=Saturation:
adjustContrast.download=Download
crop.title=Crop
crop.header=Crop Image
crop.submit=Submit
autoSplitPDF.title=Auto Split PDF
autoSplitPDF.header=Auto Split PDF
autoSplitPDF.submit=Submit
pipeline.title=Pipeline pipeline.title=Pipeline

View File

@ -0,0 +1 @@
<svg height="48" viewBox="0 0 48 48" width="48" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h48v48h-48z" fill="none"/><path d="m8.091 21c0 1.089-.576 1.695-1.304 2.464-.755.797-1.696 1.788-1.696 3.394v11.142c0 2.757 2.243 5 5 5h12c2.757 0 5-2.243 5-5v-11.143c0-2.316-2.045-3.302-4.022-4.254-2.447-1.179-4.978-2.397-4.978-6.104v-.215l-.088-.195c-.081-.179-.287-.608-.6-1.09h1.969l2.032-1.242 5.949 5.949 1.414-1.414-5.608-5.608 1.841-1.123v-6.561h-14.186l-5.713 3.428-.12 6.572h3.46c-.219.456-.351.961-.351 1.5v4.5zm-1.01-11.428 4.287-2.572h11.632v3.439l-4.19 2.561h-4.219-3-4.572zm3.01 11.428v-4.5c0-.827.673-1.5 1.5-1.5h3c.341 0 1.054.832 1.502 1.731.108 4.784 3.569 6.451 6.107 7.674 1.846.89 2.89 1.441 2.89 2.452v11.143c0 1.654-1.346 3-3 3h-12c-1.654 0-3-1.346-3-3v-11.143c0-.771.415-1.244 1.147-2.017.826-.87 1.854-1.953 1.854-3.84z"/><path d="m15.091 38h2v-5h5v-2h-5v-5h-2v5h-5v2h5z"/><circle cx="30.091" cy="8" r="2"/><circle cx="36.091" cy="8" r="2"/><circle cx="42.091" cy="8" r="2"/><circle cx="33.091" cy="13" r="2"/><circle cx="37.091" cy="17" r="2"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,105 +1,103 @@
<!DOCTYPE html> <!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org"> <html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title=#{crop.title})}"></th:block> <th:block th:insert="~{fragments/common :: head(title=#{crop.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> <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="#{crop.header}"></h2> <h2 th:text="#{crop.header}"></h2>
<form id="cropForm" action="/crop" method="post" enctype="multipart/form-data"> <form id="cropForm" action="/crop" method="post" enctype="multipart/form-data">
<input id="fileInput" type="file" name="file" required> <div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<input id="x" type="hidden" name="x"> <input id="x" type="hidden" name="x">
<input id="y" type="hidden" name="y"> <input id="y" type="hidden" name="y">
<input id="width" type="hidden" name="width"> <input id="width" type="hidden" name="width">
<input id="height" type="hidden" name="height"> <input id="height" type="hidden" name="height">
<button type="submit">Submit</button> <button type="submit" class="btn btn-primary" th:text="#{crop.submit}"></button>
</form> </form>
<canvas id="pdf-canvas"></canvas> <canvas id="pdf-canvas"></canvas>
<script> <script>
let canvas = document.getElementById('pdf-canvas'); let canvas = document.getElementById('pdf-canvas');
let context = canvas.getContext('2d'); let context = canvas.getContext('2d');
let cropForm = document.getElementById('cropForm'); let cropForm = document.getElementById('cropForm');
let fileInput = document.getElementById('fileInput'); let fileInput = document.getElementById('fileInput-input');
let xInput = document.getElementById('x'); let xInput = document.getElementById('x');
let yInput = document.getElementById('y'); let yInput = document.getElementById('y');
let widthInput = document.getElementById('width'); let widthInput = document.getElementById('width');
let heightInput = document.getElementById('height'); let heightInput = document.getElementById('height');
let pdfDoc = null; let pdfDoc = null;
let currentPage = 1; let currentPage = 1;
let totalPages = 0; let totalPages = 0;
let startX = 0; let startX = 0;
let startY = 0; let startY = 0;
let rectWidth = 0; let rectWidth = 0;
let rectHeight = 0; let rectHeight = 0;
fileInput.addEventListener('change', function(e) { fileInput.addEventListener('change', function(e) {
let file = e.target.files[0]; let file = e.target.files[0];
if (file.type === 'application/pdf') { if (file.type === 'application/pdf') {
let reader = new FileReader(); let reader = new FileReader();
reader.onload = function(ev) { reader.onload = function(ev) {
let typedArray = new Uint8Array(reader.result); let typedArray = new Uint8Array(reader.result);
pdfjsLib.getDocument(typedArray).promise.then(function(pdf) { pdfjsLib.getDocument(typedArray).promise.then(function(pdf) {
pdfDoc = pdf; pdfDoc = pdf;
totalPages = pdf.numPages; totalPages = pdf.numPages;
renderPage(currentPage); renderPage(currentPage);
}); });
}; };
reader.readAsArrayBuffer(file); reader.readAsArrayBuffer(file);
} }
}); });
canvas.addEventListener('mousedown', function(e) { canvas.addEventListener('mousedown', function(e) {
startX = e.offsetX; startX = e.offsetX;
startY = e.offsetY; startY = e.offsetY;
}); });
canvas.addEventListener('mouseup', function(e) { canvas.addEventListener('mouseup', function(e) {
rectWidth = e.offsetX - startX; rectWidth = e.offsetX - startX;
rectHeight = e.offsetY - startY; rectHeight = e.offsetY - startY;
// Flip the y-coordinate // Flip the y-coordinate
let flippedY = canvas.height - e.offsetY; let flippedY = canvas.height - e.offsetY;
xInput.value = startX; xInput.value = startX;
yInput.value = flippedY; yInput.value = flippedY;
widthInput.value = rectWidth; widthInput.value = rectWidth;
heightInput.value = rectHeight; heightInput.value = rectHeight;
context.strokeStyle = 'red'; context.strokeStyle = 'red';
context.strokeRect(startX, startY, rectWidth, rectHeight); context.strokeRect(startX, startY, rectWidth, rectHeight);
}); });
function renderPage(pageNumber) { function renderPage(pageNumber) {
pdfDoc.getPage(pageNumber).then(function(page) { pdfDoc.getPage(pageNumber).then(function(page) {
let viewport = page.getViewport({ scale: 1.0 }); let viewport = page.getViewport({ scale: 1.0 });
canvas.width = viewport.width; canvas.width = viewport.width;
canvas.height = viewport.height; canvas.height = viewport.height;
let renderContext = { canvasContext: context, viewport: viewport }; let renderContext = { canvasContext: context, viewport: viewport };
page.render(renderContext); page.render(renderContext);
}); });
} }
</script>
</script> </div>
</div>
</div> </div>
</div> </div>
</div> <div th:insert="~{fragments/footer.html :: footer}"></div>
</div> </div>
<div th:insert="~{fragments/footer.html :: footer}"></div> </body>
</div> </html>
</body>
</html>

View File

@ -40,7 +40,7 @@
</li>--> </li>-->
<li class="nav-item nav-item-separator"></li> <li class="nav-item nav-item-separator"></li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='remove-pages' OR ${currentPage}=='merge-pdfs' OR ${currentPage}=='split-pdfs' OR ${currentPage}=='pdf-organizer' OR ${currentPage}=='rotate-pdf' OR ${currentPage}=='multi-page-layout' OR ${currentPage}=='scale-pages' ? 'active' : ''"> <li class="nav-item dropdown" th:classappend="${currentPage}=='remove-pages' OR ${currentPage}=='merge-pdfs' OR ${currentPage}=='split-pdfs' OR ${currentPage}=='crop' OR ${currentPage}=='adjust-contrast' OR ${currentPage}=='pdf-organizer' OR ${currentPage}=='rotate-pdf' OR ${currentPage}=='multi-page-layout' OR ${currentPage}=='scale-pages' ? 'active' : ''">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img class="icon" src="images/file-earmark-pdf.svg" alt="icon"> <img class="icon" src="images/file-earmark-pdf.svg" alt="icon">
<span class="icon-text" th:text="#{navbar.pageOps}"></span> <span class="icon-text" th:text="#{navbar.pageOps}"></span>
@ -55,6 +55,8 @@
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'multi-page-layout', 'images/page-layout.svg', 'home.pageLayout.title', 'home.pageLayout.desc')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'multi-page-layout', 'images/page-layout.svg', 'home.pageLayout.title', 'home.pageLayout.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'scale-pages', 'images/scale-pages.svg', 'home.scalePages.title', 'home.scalePages.desc')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'scale-pages', 'images/scale-pages.svg', 'home.scalePages.title', 'home.scalePages.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'auto-split-pdf', 'images/layout-split.svg', 'home.autoSplitPDF.title', 'home.autoSplitPDF.desc')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'auto-split-pdf', 'images/layout-split.svg', 'home.autoSplitPDF.title', 'home.autoSplitPDF.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('adjust-contrast', 'images/adjust-contrast.svg', 'home.adjust-contrast.title', 'home.adjust-contrast.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('crop', 'images/crop.svg', 'home.crop.title', 'home.crop.desc')}"></div>
</div> </div>
@ -96,11 +98,12 @@
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('change-permissions', 'images/shield-lock.svg', 'home.permissions.title', 'home.permissions.desc')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('change-permissions', 'images/shield-lock.svg', 'home.permissions.title', 'home.permissions.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('add-watermark', 'images/droplet.svg', 'home.watermark.title', 'home.watermark.desc')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('add-watermark', 'images/droplet.svg', 'home.watermark.title', 'home.watermark.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('cert-sign', 'images/award.svg', 'home.certSign.title', 'home.certSign.desc')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('cert-sign', 'images/award.svg', 'home.certSign.title', 'home.certSign.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('sanitize-pdf', 'images/sanitize.svg', 'home.sanitizePdf.title', 'home.sanitizePdf.desc')}"></div>
</div> </div>
</li> </li>
<li class="nav-item nav-item-separator"></li> <li class="nav-item nav-item-separator"></li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='sign' OR ${currentPage}=='repair' OR ${currentPage}=='compare' OR ${currentPage}=='flatten' OR ${currentPage}=='remove-blanks' OR ${currentPage}=='extract-image-scans' OR ${currentPage}=='change-metadata' OR ${currentPage}=='add-image' OR ${currentPage}=='ocr-pdf' OR ${currentPage}=='change-permissions' OR ${currentPage}=='extract-images' OR ${currentPage}=='compress-pdf' OR ${currentPage}=='add-page-numbers' OR ${currentPage}=='auto-rename' OR ${currentPage}=='adjust-contrast' OR ${currentPage}=='crop' ? 'active' : ''"> <li class="nav-item dropdown" th:classappend="${currentPage}=='sign' OR ${currentPage}=='repair' OR ${currentPage}=='compare' OR ${currentPage}=='flatten' OR ${currentPage}=='remove-blanks' OR ${currentPage}=='extract-image-scans' OR ${currentPage}=='change-metadata' OR ${currentPage}=='add-image' OR ${currentPage}=='ocr-pdf' OR ${currentPage}=='change-permissions' OR ${currentPage}=='extract-images' OR ${currentPage}=='compress-pdf' OR ${currentPage}=='add-page-numbers' OR ${currentPage}=='auto-rename' ? 'active' : ''">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img class="icon" src="images/card-list.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <img class="icon" src="images/card-list.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;">
<span class="icon-text" th:text="#{navbar.other}"></span> <span class="icon-text" th:text="#{navbar.other}"></span>
@ -121,8 +124,6 @@
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('compare', 'images/scales.svg', 'home.compare.title', 'home.compare.desc')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('compare', 'images/scales.svg', 'home.compare.title', 'home.compare.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('add-page-numbers', 'images/add-page-numbers.svg', 'home.add-page-numbers.title', 'home.add-page-numbers.desc')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('add-page-numbers', 'images/add-page-numbers.svg', 'home.add-page-numbers.title', 'home.add-page-numbers.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('auto-rename', 'images/fonts.svg', 'home.auto-rename.title', 'home.auto-rename.desc')}"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('auto-rename', 'images/fonts.svg', 'home.auto-rename.title', 'home.auto-rename.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('adjust-contrast', 'images/adjust-contrast.svg', 'home.adjust-contrast.title', 'home.adjust-contrast.desc')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('crop', 'images/crop.svg', 'home.crop.title', 'home.crop.desc')}"></div>
</div> </div>
</li> </li>

View File

@ -78,7 +78,7 @@
<div th:replace="~{fragments/card :: card(id='adjust-contrast', cardTitle=#{home.adjust-contrast.title}, cardText=#{home.adjust-contrast.desc}, cardLink='adjust-contrast', svgPath='images/adjust-contrast.svg')}"></div> <div th:replace="~{fragments/card :: card(id='adjust-contrast', cardTitle=#{home.adjust-contrast.title}, cardText=#{home.adjust-contrast.desc}, cardLink='adjust-contrast', svgPath='images/adjust-contrast.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='crop', cardTitle=#{home.crop.title}, cardText=#{home.crop.desc}, cardLink='crop', svgPath='images/crop.svg')}"></div> <div th:replace="~{fragments/card :: card(id='crop', cardTitle=#{home.crop.title}, cardText=#{home.crop.desc}, cardLink='crop', svgPath='images/crop.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='auto-split-pdf', cardTitle=#{home.autoSplitPDF.title}, cardText=#{home.autoSplitPDF.desc}, cardLink='auto-split-pdf', svgPath='images/layout-split.svg')}"></div> <div th:replace="~{fragments/card :: card(id='auto-split-pdf', cardTitle=#{home.autoSplitPDF.title}, cardText=#{home.autoSplitPDF.desc}, cardLink='auto-split-pdf', svgPath='images/layout-split.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='sanitize-pdf', cardTitle=#{home.sanitizePdf.title}, cardText=#{home.sanitizePdf.desc}, cardLink='sanitize-pdf', svgPath='images/sanitize.svg')}"></div>
</div> </div>
</div> </div> </div> </div>

View File

@ -23,15 +23,13 @@
th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div> th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<br> <br>
<div class="form-group"> <div class="form-group">
<label for="customMargin">Margin Size</label> <select <label for="customMargin" th:text="#{addPageNumbers.selectText.2}"></label> <select
class="form-control" id="customMargin" name="customMargin" class="form-control" id="customMargin" name="customMargin"
required> required>
<option value="" disabled selected>Select a margin <option value="small" th:text="#{sizes.small}"></option>
size</option> <option value="medium" selected th:text="#{sizes.medium}"></option>
<option value="small">Small</option> <option value="large" th:text="#{sizes.large}"></option>
<option value="medium">Medium</option> <option value="x-large" th:text="#{sizes.x-large}"></option>
<option value="large">Large</option>
<option value="x-large">X-Large</option>
</select> </select>
</div> </div>
<style> <style>
@ -80,7 +78,7 @@
<div class="form-group"> <div class="form-group">
<label for="position">Position</label> <label for="position" th:text="#{addPageNumbers.selectText.3}"></label>
<div class="a4container"> <div class="a4container">
<div class="pageNumber" id="1" style="top: 10%; left: 10%;">1</div> <div class="pageNumber" id="1" style="top: 10%; left: 10%;">1</div>
<div class="pageNumber" id="2" style="top: 10%; left: 50%;">2</div> <div class="pageNumber" id="2" style="top: 10%; left: 50%;">2</div>
@ -92,27 +90,23 @@
<div class="pageNumber" id="8" style="top: 90%; left: 50%;">8</div> <div class="pageNumber" id="8" style="top: 90%; left: 50%;">8</div>
<div class="pageNumber" id="9" style="top: 90%; left: 90%;">9</div> <div class="pageNumber" id="9" style="top: 90%; left: 90%;">9</div>
</div> </div>
</div> </div>
<input type="hidden" id="numberInput" name="position" min="1" <input type="hidden" id="numberInput" name="position" min="1"
max="9" required> max="9" required>
<div class="form-group"> <div class="form-group">
<label for="startingNumber">Starting Number</label> <input <label for="startingNumber" th:text="#{addPageNumbers.selectText.4}"></label> <input
type="number" class="form-control" id="startingNumber" type="number" class="form-control" id="startingNumber"
name="startingNumber" min="1" required value="1" /> name="startingNumber" min="1" required value="1" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="pagesToNumber">Pages to Number</label> <input <label for="pagesToNumber" th:text="#{addPageNumbers.selectText.5}"></label> <input
type="text" class="form-control" id="pagesToNumber" type="text" class="form-control" id="pagesToNumber"
name="pagesToNumber" name="pagesToNumber"
placeholder="Which pages to number, default 'all', also accepts 1-5 or 2,5,9 etc" /> placeholder="Which pages to number, default 'all', also accepts 1-5 or 2,5,9 etc" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="customText">Custom Text</label> <input type="text" <label for="customText" th:text="#{addPageNumbers.selectText.6}"></label> <input type="text"
class="form-control" id="customText" name="customText" class="form-control" id="customText" name="customText"
placeholder="Default just number, also accepts 'Page {n} of {total}', 'Tag-{n}' etc" /> placeholder="Default just number, also accepts 'Page {n} of {total}', 'Tag-{n}' etc" />
</div> </div>
@ -157,4 +151,4 @@
<div th:insert="~{fragments/footer.html :: footer}"></div> <div th:insert="~{fragments/footer.html :: footer}"></div>
</div> </div>
</body> </body>
</html> </html>

View File

@ -1,292 +1,293 @@
<!DOCTYPE html> <!DOCTYPE html>
<html th:lang="${#locale.toString()}" <html th:lang="${#locale.toString()}"
th:lang-direction="#{language.direction}" th:lang-direction="#{language.direction}"
xmlns:th="http://www.thymeleaf.org"> xmlns:th="http://www.thymeleaf.org">
<th:block <th:block
th:insert="~{fragments/common :: head(title=#{extractImages.title})}"></th:block> th:insert="~{fragments/common :: head(title=#{adjustContrast.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> <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="#{extractImages.header}"></h2> <h2 th:text="#{adjustContrast.header}"></h2>
<input type="file" id="pdf-file" accept="application/pdf" /> <div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
<canvas id="pdf-canvas"></canvas> <h4>
<span th:text="#{adjustContrast.contrast}"></span> <span id="contrast-val">100</span>%
<h4> </h4>
Contrast: <span id="contrast-val">100</span>% <input type="range" min="0" max="200" value="100"
</h4> id="contrast-slider" />
<input type="range" min="0" max="200" value="100"
id="contrast-slider" /> <h4>
<span th:text="#{adjustContrast.brightness}"></span> <span id="brightness-val">100</span>%
<h4> </h4>
Brightness: <span id="brightness-val">100</span>% <input type="range" min="0" max="200" value="100"
</h4> id="brightness-slider" />
<input type="range" min="0" max="200" value="100"
id="brightness-slider" /> <h4>
<span th:text="#{adjustContrast.saturation}"></span> <span id="saturation-val">100</span>%
<h4> </h4>
Saturation: <span id="saturation-val">100</span>% <input type="range" min="0" max="200" value="100"
</h4> id="saturation-slider" />
<input type="range" min="0" max="200" value="100"
id="saturation-slider" /> </br>
<canvas id="pdf-canvas"></canvas>
<button id="download-button">Download</button>
<script src="pdfjs/pdf.js"></script>
<script> <button id="download-button" class="btn btn-primary" th:text="#{adjustContrast.download}"></button>
var canvas = document.getElementById('pdf-canvas');
var context = canvas.getContext('2d'); <script src="pdfjs/pdf.js"></script>
var originalImageData = null; <script>
var allPages = []; var canvas = document.getElementById('pdf-canvas');
var pdfDoc = null; var context = canvas.getContext('2d');
var pdf = null; // This is the current PDF document var originalImageData = null;
var allPages = [];
async function renderPDFAndSaveOriginalImageData(file) { var pdfDoc = null;
var fileReader = new FileReader(); var pdf = null; // This is the current PDF document
fileReader.onload = async function() {
var data = new Uint8Array(this.result); async function renderPDFAndSaveOriginalImageData(file) {
pdf = await pdfjsLib.getDocument({data: data}).promise; var fileReader = new FileReader();
fileReader.onload = async function() {
// Get the number of pages in the PDF var data = new Uint8Array(this.result);
var numPages = pdf.numPages; pdf = await pdfjsLib.getDocument({data: data}).promise;
allPages = Array.from({length: numPages}, (_, i) => i + 1);
// Get the number of pages in the PDF
// Create a new PDF document var numPages = pdf.numPages;
pdfDoc = await PDFLib.PDFDocument.create(); allPages = Array.from({length: numPages}, (_, i) => i + 1);
// Render the first page in the viewer
await renderPageAndAdjustImageProperties(1); // Create a new PDF document
}; pdfDoc = await PDFLib.PDFDocument.create();
fileReader.readAsArrayBuffer(file); // Render the first page in the viewer
} await renderPageAndAdjustImageProperties(1);
};
// This function is now async and returns a promise fileReader.readAsArrayBuffer(file);
function renderPageAndAdjustImageProperties(pageNum) { }
return new Promise(async function(resolve, reject) {
var page = await pdf.getPage(pageNum); // This function is now async and returns a promise
var scale = 1.5; function renderPageAndAdjustImageProperties(pageNum) {
var viewport = page.getViewport({ scale: scale }); return new Promise(async function(resolve, reject) {
var page = await pdf.getPage(pageNum);
canvas.height = viewport.height; var scale = 1.5;
canvas.width = viewport.width; var viewport = page.getViewport({ scale: scale });
var renderContext = { canvas.height = viewport.height;
canvasContext: context, canvas.width = viewport.width;
viewport: viewport
}; var renderContext = {
canvasContext: context,
var renderTask = page.render(renderContext); viewport: viewport
renderTask.promise.then(function () { };
originalImageData = context.getImageData(0, 0, canvas.width, canvas.height);
adjustImageProperties(); var renderTask = page.render(renderContext);
resolve(); renderTask.promise.then(function () {
}); originalImageData = context.getImageData(0, 0, canvas.width, canvas.height);
}); adjustImageProperties();
} resolve();
});
});
function adjustImageProperties() { }
var contrast = parseFloat(document.getElementById('contrast-slider').value);
var brightness = parseFloat(document.getElementById('brightness-slider').value);
var saturation = parseFloat(document.getElementById('saturation-slider').value); function adjustImageProperties() {
var contrast = parseFloat(document.getElementById('contrast-slider').value);
contrast /= 100; // normalize to range [0, 2] var brightness = parseFloat(document.getElementById('brightness-slider').value);
brightness /= 100; // normalize to range [0, 2] var saturation = parseFloat(document.getElementById('saturation-slider').value);
saturation /= 100; // normalize to range [0, 2]
contrast /= 100; // normalize to range [0, 2]
if (originalImageData) { brightness /= 100; // normalize to range [0, 2]
var newImageData = context.createImageData(originalImageData.width, originalImageData.height); saturation /= 100; // normalize to range [0, 2]
newImageData.data.set(originalImageData.data);
if (originalImageData) {
for(var i=0; i<newImageData.data.length; i+=4) var newImageData = context.createImageData(originalImageData.width, originalImageData.height);
{ newImageData.data.set(originalImageData.data);
var r = newImageData.data[i];
var g = newImageData.data[i+1]; for(var i=0; i<newImageData.data.length; i+=4)
var b = newImageData.data[i+2]; {
// Adjust contrast var r = newImageData.data[i];
r = adjustContrastForPixel(r, contrast); var g = newImageData.data[i+1];
g = adjustContrastForPixel(g, contrast); var b = newImageData.data[i+2];
b = adjustContrastForPixel(b, contrast); // Adjust contrast
// Adjust brightness r = adjustContrastForPixel(r, contrast);
r = adjustBrightnessForPixel(r, brightness); g = adjustContrastForPixel(g, contrast);
g = adjustBrightnessForPixel(g, brightness); b = adjustContrastForPixel(b, contrast);
b = adjustBrightnessForPixel(b, brightness); // Adjust brightness
// Adjust saturation r = adjustBrightnessForPixel(r, brightness);
var rgb = adjustSaturationForPixel(r, g, b, saturation); g = adjustBrightnessForPixel(g, brightness);
newImageData.data[i] = rgb[0]; b = adjustBrightnessForPixel(b, brightness);
newImageData.data[i+1] = rgb[1]; // Adjust saturation
newImageData.data[i+2] = rgb[2]; var rgb = adjustSaturationForPixel(r, g, b, saturation);
} newImageData.data[i] = rgb[0];
newImageData.data[i+1] = rgb[1];
context.putImageData(newImageData, 0, 0); newImageData.data[i+2] = rgb[2];
} }
}
context.putImageData(newImageData, 0, 0);
function rgbToHsl(r, g, b) { }
r /= 255, g /= 255, b /= 255; }
var max = Math.max(r, g, b), min = Math.min(r, g, b); function rgbToHsl(r, g, b) {
var h, s, l = (max + min) / 2; r /= 255, g /= 255, b /= 255;
if (max === min) { var max = Math.max(r, g, b), min = Math.min(r, g, b);
h = s = 0; // achromatic var h, s, l = (max + min) / 2;
} else {
var d = max - min; if (max === min) {
s = l > 0.5 ? d / (2 - max - min) : d / (max + min); h = s = 0; // achromatic
} else {
switch (max) { var d = max - min;
case r: h = (g - b) / d + (g < b ? 6 : 0); break; s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break; switch (max) {
} case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
h /= 6; case b: h = (r - g) / d + 4; break;
} }
return [h, s, l]; h /= 6;
} }
function hslToRgb(h, s, l) { return [h, s, l];
var r, g, b; }
if (s === 0) { function hslToRgb(h, s, l) {
r = g = b = l; // achromatic var r, g, b;
} else {
var hue2rgb = function hue2rgb(p, q, t) { if (s === 0) {
if (t < 0) t += 1; r = g = b = l; // achromatic
if (t > 1) t -= 1; } else {
if (t < 1 / 6) return p + (q - p) * 6 * t; var hue2rgb = function hue2rgb(p, q, t) {
if (t < 1 / 2) return q; if (t < 0) t += 1;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; if (t > 1) t -= 1;
return p; if (t < 1 / 6) return p + (q - p) * 6 * t;
}; if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
var q = l < 0.5 ? l * (1 + s) : l + s - l * s; return p;
var p = 2 * l - q; };
r = hue2rgb(p, q, h + 1 / 3); var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
g = hue2rgb(p, q, h); var p = 2 * l - q;
b = hue2rgb(p, q, h - 1 / 3);
} r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
return [r * 255, g * 255, b * 255]; b = hue2rgb(p, q, h - 1 / 3);
} }
function adjustContrastForPixel(pixel, contrast) { return [r * 255, g * 255, b * 255];
// Normalize to range [-0.5, 0.5] }
var normalized = pixel / 255 - 0.5;
function adjustContrastForPixel(pixel, contrast) {
// Apply contrast // Normalize to range [-0.5, 0.5]
normalized *= contrast; var normalized = pixel / 255 - 0.5;
// Denormalize back to [0, 255] // Apply contrast
return (normalized + 0.5) * 255; normalized *= contrast;
}
function clamp(value, min, max) { // Denormalize back to [0, 255]
return Math.min(Math.max(value, min), max); return (normalized + 0.5) * 255;
} }
function clamp(value, min, max) {
function adjustSaturationForPixel(r, g, b, saturation) { return Math.min(Math.max(value, min), max);
var hsl = rgbToHsl(r, g, b); }
// Adjust saturation function adjustSaturationForPixel(r, g, b, saturation) {
hsl[1] = clamp(hsl[1] * saturation, 0, 1); var hsl = rgbToHsl(r, g, b);
// Convert back to RGB // Adjust saturation
var rgb = hslToRgb(hsl[0], hsl[1], hsl[2]); hsl[1] = clamp(hsl[1] * saturation, 0, 1);
// Return adjusted RGB values // Convert back to RGB
return rgb; var rgb = hslToRgb(hsl[0], hsl[1], hsl[2]);
}
// Return adjusted RGB values
function adjustBrightnessForPixel(pixel, brightness) { return rgb;
return Math.max(0, Math.min(255, pixel * brightness)); }
}
function adjustBrightnessForPixel(pixel, brightness) {
async function downloadPDF() { return Math.max(0, Math.min(255, pixel * brightness));
for (var i = 0; i < allPages.length; i++) { }
await renderPageAndAdjustImageProperties(allPages[i]);
const pngImageBytes = canvas.toDataURL('image/png'); async function downloadPDF() {
const pngImage = await pdfDoc.embedPng(pngImageBytes); for (var i = 0; i < allPages.length; i++) {
const pngDims = pngImage.scale(1); await renderPageAndAdjustImageProperties(allPages[i]);
const pngImageBytes = canvas.toDataURL('image/png');
// Create a blank page matching the dimensions of the image const pngImage = await pdfDoc.embedPng(pngImageBytes);
const page = pdfDoc.addPage([pngDims.width, pngDims.height]); const pngDims = pngImage.scale(1);
// Draw the PNG image // Create a blank page matching the dimensions of the image
page.drawImage(pngImage, { const page = pdfDoc.addPage([pngDims.width, pngDims.height]);
x: 0,
y: 0, // Draw the PNG image
width: pngDims.width, page.drawImage(pngImage, {
height: pngDims.height x: 0,
}); y: 0,
} width: pngDims.width,
height: pngDims.height
// Serialize the PDFDocument to bytes (a Uint8Array) });
const pdfBytes = await pdfDoc.save(); }
// Create a Blob // Serialize the PDFDocument to bytes (a Uint8Array)
const blob = new Blob([pdfBytes.buffer], {type: "application/pdf"}); const pdfBytes = await pdfDoc.save();
// Create download link // Create a Blob
const downloadLink = document.createElement('a'); const blob = new Blob([pdfBytes.buffer], {type: "application/pdf"});
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = "download.pdf"; // Create download link
downloadLink.click(); const downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(blob);
// After download, reset the viewer and clear stored data downloadLink.download = "download.pdf";
allPages = []; // Clear the pages downloadLink.click();
originalImageData = null; // Clear the image data
// After download, reset the viewer and clear stored data
// Go back to page 1 and render it in the viewer allPages = []; // Clear the pages
if (pdf !== null) { originalImageData = null; // Clear the image data
renderPageAndAdjustImageProperties(1);
} // Go back to page 1 and render it in the viewer
if (pdf !== null) {
} renderPageAndAdjustImageProperties(1);
}
}
// Event listeners
document.getElementById('pdf-file').addEventListener('change', function(e) {
if (e.target.files.length > 0) {
renderPDFAndSaveOriginalImageData(e.target.files[0]); // Event listeners
} document.getElementById('fileInput-input').addEventListener('change', function(e) {
}); if (e.target.files.length > 0) {
renderPDFAndSaveOriginalImageData(e.target.files[0]);
document.getElementById('contrast-slider').addEventListener('input', function() { }
document.getElementById('contrast-val').textContent = this.value; });
adjustImageProperties();
}); document.getElementById('contrast-slider').addEventListener('input', function() {
document.getElementById('contrast-val').textContent = this.value;
document.getElementById('brightness-slider').addEventListener('input', function() { adjustImageProperties();
document.getElementById('brightness-val').textContent = this.value; });
adjustImageProperties();
}); document.getElementById('brightness-slider').addEventListener('input', function() {
document.getElementById('brightness-val').textContent = this.value;
document.getElementById('saturation-slider').addEventListener('input', function() { adjustImageProperties();
document.getElementById('saturation-val').textContent = this.value; });
adjustImageProperties();
}); document.getElementById('saturation-slider').addEventListener('input', function() {
document.getElementById('saturation-val').textContent = this.value;
document.getElementById('download-button').addEventListener('click', function() { adjustImageProperties();
downloadPDF(); });
});
</script> document.getElementById('download-button').addEventListener('click', function() {
downloadPDF();
});
</div> </script>
</div> </div>
</div> </div>
</div> </div>
<div th:insert="~{fragments/footer.html :: footer}"></div> </div>
</div> <div th:insert="~{fragments/footer.html :: footer}"></div>
</body> </div>
</html> </body>
</html>

View File

@ -1,31 +1,30 @@
<!DOCTYPE html> <!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org"> <html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title=#{auto-rename.title})}"></th:block> <th:block th:insert="~{fragments/common :: head(title=#{auto-rename.title})}"></th:block>
<body> <body>
<th:block th:insert="~{fragments/common :: game}"></th:block> <th:block th:insert="~{fragments/common :: game}"></th:block>
<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="#{auto-rename.header}"></h2> <h2 th:text="#{auto-rename.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{auto-rename}"> <form method="post" enctype="multipart/form-data" th:action="@{auto-rename}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div> <div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<br> <br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{auto-rename.submit}"></button> <button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{auto-rename.submit}"></button>
</form> </form>
<p class="mt-3" th:text="#{auto-rename.credit}"></p> </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>

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title=#{sanitizePDF.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="#{sanitizePDF.header}"></h2>
<form action="sanitize-pdf" method="post" enctype="multipart/form-data">
<div class="form-group">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="removeJavaScript" name="removeJavaScript" checked>
<label class="form-check-label" for="removeJavaScript" th:text="#{sanitizePDF.selectText.1}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="removeEmbeddedFiles" name="removeEmbeddedFiles" checked>
<label class="form-check-label" for="removeEmbeddedFiles" th:text="#{sanitizePDF.selectText.2}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="removeMetadata" name="removeMetadata" checked>
<label class="form-check-label" for="removeMetadata" th:text="#{sanitizePDF.selectText.3}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="removeLinks" name="removeLinks" checked>
<label class="form-check-label" for="removeLinks" th:text="#{sanitizePDF.selectText.4}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="removeFonts" name="removeFonts" checked>
<label class="form-check-label" for="removeFonts" th:text="#{sanitizePDF.selectText.5}"></label>
</div>
<br />
<div class="form-group text-center">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{sanitizePDF.submit}"></button>
</div>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>