This commit is contained in:
Anthony Stirling 2023-02-11 00:37:53 +00:00
parent e09abb8bb4
commit d44b9666ae
10 changed files with 245 additions and 112 deletions

View File

@ -0,0 +1,29 @@
package stirling.software.SPDF.controller;
import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
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 ContrastController {
@GetMapping("/adjust-contrast")
public String adjustContrastMain(Model model) {
model.addAttribute("currentPage", "adjustContrast");
return "adjust-contrast";
}
}

View File

@ -48,8 +48,7 @@ public class ConvertImgPDFController {
@PostMapping("/pdf-to-img")
public ResponseEntity<Resource> convertToImage(@RequestParam("fileInput") MultipartFile file, @RequestParam("imageFormat") String imageFormat,
@RequestParam("singleOrMultiple") String singleOrMultiple, @RequestParam("colorType") String colorType, @RequestParam("dpi") String dpi,
@RequestParam("contrast") String contrast, @RequestParam("brightness") String brightness) throws IOException {
@RequestParam("singleOrMultiple") String singleOrMultiple, @RequestParam("colorType") String colorType, @RequestParam("dpi") String dpi) throws IOException {
byte[] pdfBytes = file.getBytes();
ImageType colorTypeResult = ImageType.RGB;
@ -62,9 +61,7 @@ public class ConvertImgPDFController {
boolean singleImage = singleOrMultiple.equals("single");
byte[] result = null;
try {
result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toLowerCase(), colorTypeResult, singleImage, Integer.valueOf(dpi), Integer.valueOf(contrast),
Integer.valueOf(brightness)); // DPI, contrast,
// brightness
result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toLowerCase(), colorTypeResult, singleImage, Integer.valueOf(dpi));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();

View File

@ -1,15 +1,23 @@
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 {
@ -21,14 +29,82 @@ public class MetadataController {
}
@PostMapping("/update-metadata")
public ResponseEntity<byte[]> metadata(@RequestParam Map<String, String> allRequestParams) throws IOException {
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 {
System.out.println("1 allRequestParams.size() = " + allRequestParams.size());
for (Entry entry : allRequestParams.entrySet()) {
System.out.println("1 key=" + entry.getKey() + ", value=" + entry.getValue());
}
return null;
}
PDDocument document = PDDocument.load(pdfFile.getBytes());
// Remove all metadata based on flag
PDDocumentInformation info = document.getDocumentInformation();
if(deleteAll) {
for (String key : info.getMetadataKeys()) {
info.setCustomMetadataValue(key, null);
}
} else {
if(author != null && author.length() > 0) {
info.setAuthor(author);
}
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);
}
if(creator != null && creator.length() > 0) {
info.setCreator(creator);
}
if(keywords != null && keywords.length() > 0) {
info.setKeywords(keywords);
}
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);
}
if(producer != null && producer.length() > 0) {
info.setProducer(producer);
}
if(subject != null && subject.length() > 0) {
info.setSubject(subject);
}
if(title != null && title.length() > 0) {
info.setTitle(title);
}
if(trapped != null && trapped.length() > 0) {
info.setTrapped(trapped);
}
}
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_metadata.pdf");
}
// @PostMapping("/update-metadata")
// public ResponseEntity<byte[]> addWatermark(@RequestParam("fileInput") MultipartFile pdfFile,
@ -51,20 +127,7 @@ public class MetadataController {
// System.out.println("3 key=" + entry.getKey() + ", value=" + entry.getValue());
// }
//
// PDDocument document = PDDocument.load(pdfFile.getBytes());
//
//
// // Remove all metadata based on flag
// PDDocumentInformation info = document.getDocumentInformation();
// for (String key : info.getMetadataKeys()) {
// info.setCustomMetadataValue(key, null);
// }
//
//
//
//
// return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_metadata.pdf");
// }
// // Loop over all pages and remove annotations
// for (PDPage page : document.getPages()) {

View File

@ -31,7 +31,7 @@ import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import com.spire.pdf.PdfDocument;
//Generate tests for the below class
public class PdfUtils {
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
@ -79,7 +79,7 @@ public class PdfUtils {
}
}
public static byte[] convertFromPdf(byte[] inputStream, String imageType, ImageType colorType, boolean singleImage, int DPI, int contrast, int brightness)
public static byte[] convertFromPdf(byte[] inputStream, String imageType, ImageType colorType, boolean singleImage, int DPI)
throws IOException, Exception {
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
PDFRenderer pdfRenderer = new PDFRenderer(document);
@ -88,14 +88,9 @@ public class PdfUtils {
// Create images of all pages
for (int i = 0; i < pageCount; i++) {
BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300, colorType);
float scale = contrast + 1f;
float offset = brightness;
RescaleOp rescaleOp = new RescaleOp(scale, offset, null);
BufferedImage dest = rescaleOp.filter(image, null);
images.add(dest);
images.add(pdfRenderer.renderImageWithDPI(i, 300, colorType));
}
if (singleImage) {
// Combine all images into a single big image
BufferedImage combined = new BufferedImage(images.get(0).getWidth(), images.get(0).getHeight() * pageCount, BufferedImage.TYPE_INT_RGB);

View File

@ -131,8 +131,6 @@ pdfToImage.colorType=Colour type
pdfToImage.color=Colour
pdfToImage.grey=Greyscale
pdfToImage.blackwhite=Black and White (May lose data!)
pdfToImage.contrast=Contrast Value:
pdfToImage.brightness=Brightness Value:
pdfToImage.submit=Convert
#addPassword

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="60px" height="60px" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
<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>

Before

Width:  |  Height:  |  Size: 919 B

After

Width:  |  Height:  |  Size: 919 B

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="45px" height="45px" viewBox="0 -0.5 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<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>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,36 @@
<!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=#{contrast.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="#{contrast.header}"></h2>
<form action="#" th:action="@{adjust-contrast}" method="post" enctype="multipart/form-data">
<p th:text="#{processTimeWarning}"></p>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<div class="form-group">
<label for="imagecontrastionLevel" th:text="#{contrast.contrastLevel}"></label>
<input type="number" class="form-control" id="imagecontrastionLevel" name="imagecontrastionLevel" step="1" value="1" min="1" max="100" required>
</div>
<button type="submit" class="btn btn-primary" th:text="#{contrast.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>
</html>

View File

@ -39,19 +39,9 @@
</select>
</div>
<div class="form-group">
<label for="dpi" th:text="#{pdfToImage.dpi}"></label>
<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>
<div class="form-group">
<label for="contrast" th:text="#{pdfToImage.contrast}"></label>
<input type="number" name="contrast" class="form-control" id="contrast" min="-255" max="255" value="0" step="1" required>
</div>
<div class="form-group">
<label for="brightness" th:text="#{pdfToImage.brightness}"></label>
<input type="number" name="brightness" class="form-control" id="brightness" min="-255" max="255" step="1" value="1" required>
</div>
<button type="submit" class="btn btn-primary" th:text="#{pdfToImage.submit}"></button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>

View File

@ -13,66 +13,70 @@
<div class="col-md-6">
<h2 th:text="#{changeMetadata.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{/update-metadata}">
<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">Please select the variables you wish to change</p>
<div class="form-group form-check form-check-inline">
<input type="checkbox" class="form-check-input" id="deleteAll">
<div class="form-group-inline form-check">
<input type="checkbox" class="form-check-input" id="deleteAll" name="deleteAll">
<label class="ml-3" for="deleteAll">Delete all metadata</label>
</div>
<div class="form-group form-check form-check-inline">
<div class="form-group-inline form-check">
<input type="checkbox" class="form-check-input" id="advancedModeCheckbox">
<label class="ml-3" for="advancedModeCheckbox">Show Advanced Metadata:</label>
</div>
<div class="form-group form-check">
<div class="form-group">
<label class="form-check-label" for="author">Author:</label>
<input type="text" class="form-control" id="author">
<input type="text" class="form-control" id="author" name="author">
</div>
<div class="form-group form-check">
<div class="form-group">
<label class="form-check-label" for="creationDate">Creation Date:</label>
<input type="text" class="form-control" id="creationDate">
<input type="text" class="form-control" id="creationDate" name="creationDate" placeholder="2020/12/25 18:30:59">
</div>
<div class="form-group form-check">
<div class="form-group">
<label class="form-check-label" for="creator">Creator:</label>
<input type="text" class="form-control" id="creator">
<input type="text" class="form-control" id="creator" name="creator">
</div>
<div class="form-group form-check">
<div class="form-group">
<label class="form-check-label" for="keywords">Keywords:</label>
<input type="text" class="form-control" id="keywords">
<input type="text" class="form-control" id="keywords" name="keywords">
</div>
<div class="form-group form-check">
<div class="form-group">
<label class="form-check-label" for="modificationDate">Modification Date:</label>
<input type="text" class="form-control" id="modificationDate">
<input type="text" class="form-control" id="modificationDate" name="modificationDate" placeholder="2020/12/25 18:30:59">
</div>
<div class="form-group form-check">
<div class="form-group">
<label class="form-check-label" for="producer">Producer:</label>
<input type="text" class="form-control" id="producer">
<input type="text" class="form-control" id="producer" name="producer">
</div>
<div class="form-group form-check">
<div class="form-group">
<label class="form-check-label" for="subject">Subject:</label>
<input type="text" class="form-control" id="subject">
<input type="text" class="form-control" id="subject" name="subject">
</div>
<div class="form-group form-check">
<div class="form-group">
<label class="form-check-label" for="title">Title:</label>
<input type="text" class="form-control" id="title">
<input type="text" class="form-control" id="title" name="title">
</div>
<div class="form-group form-check">
<div class="form-group">
<label class="form-check-label" for="trapped">Trapped:</label>
<input type="text" class="form-control" id="trapped">
<select class="form-control" id="trapped" name="trapped">
<option value="True">True</option>
<option value="False">False</option>
<option value="Unknown">Unknown</option>
</select>
</div>
<div id="advancedMetadata" style="display: none;">
<p>Other Metadata:</p>
<h3>Other Metadata:</h3>
<div class="form-group" id="otherMetadataEntries"></div>
</div>
<div id="customMetadataEntries"></div>
@ -87,25 +91,32 @@
const advancedMetadataDiv = document.getElementById('advancedMetadata');
const otherMetadataEntriesDiv = document.getElementById('otherMetadataEntries');
const addMetadataBtn = document.getElementById("addMetadataBtn");
const customMetadataFormContainer = document.getElementById("customMetadataEntries");
var count = 1;
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";
customMetadataFormContainer.appendChild(keyInput);
customMetadataFormContainer.appendChild(valueInput);
valueInput.className = "form-control";
valueInput.name = "customValue1" + count;
count = count + 1;
const formGroup = document.createElement("div");
formGroup.className = "form-group";
formGroup.appendChild(keyInput);
formGroup.appendChild(valueInput);
customMetadataFormContainer.appendChild(formGroup);
});
@ -126,15 +137,15 @@
const file = this.files[0];
var url = URL.createObjectURL(file)
const pdf = await pdfjsLib.getDocument(url).promise;
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 = pdfMetadata?.info?.CreationDate;
creationDateInput.value = convertDateFormat(pdfMetadata?.info?.CreationDate);
creatorInput.value = pdfMetadata?.info?.Creator;
keywordsInput.value = pdfMetadata?.info?.Keywords;
modificationDateInput.value = pdfMetadata?.info?.ModDate;
modificationDateInput.value = convertDateFormat(pdfMetadata?.info?.ModDate);
producerInput.value = pdfMetadata?.info?.Producer;
subjectInput.value = pdfMetadata?.info?.Subject;
titleInput.value = pdfMetadata?.info?.Title;
@ -142,39 +153,53 @@
addExtra();
});
function convertDateFormat(dateTimeString) {
if (!dateTimeString || dateTimeString.length < 17) {
return dateTimeString;
}
function addExtra() {
const event = document.getElementById("advancedModeCheckbox");
if (event.checked) {
advancedMetadataDiv.style.display = 'block';
for (const [key, value] of Object.entries(lastPDFFile)) {
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 form-check"><label class="form-check-label" for="${key}">${key}:</label><input value="${value}" type="text" class="form-control" id="${key}"></div>`;
otherMetadataEntriesDiv.appendChild(entryDiv);
}
} else {
advancedMetadataDiv.style.display = 'none';
while (otherMetadataEntriesDiv.firstChild) {
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
}
}
}
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("advancedModeCheckbox");
if (event.checked) {
advancedMetadataDiv.style.display = 'block';
for (const [key, value] of Object.entries(lastPDFFile)) {
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 {
advancedMetadataDiv.style.display = 'none';
while (otherMetadataEntriesDiv.firstChild) {
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
}
}
}
advancedModeCheckbox.addEventListener('change', (event) => {
addExtra();
});
addExtra();
});
</script>