compare init, robots, sign lang and local js

This commit is contained in:
Anthony Stirling 2023-05-08 00:17:20 +01:00
parent 1d55ee7f93
commit acf4662d2f
9 changed files with 198 additions and 25 deletions

View File

@ -1,8 +1,10 @@
package stirling.software.SPDF.controller.web;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import io.swagger.v3.oas.annotations.Hidden;
@ -67,14 +69,26 @@ public class GeneralWebController {
return "split-pdfs";
}
@GetMapping("/sign")
@Hidden
public String signForm(Model model) {
model.addAttribute("currentPage", "sign");
return "sign";
}
@GetMapping(value = "/robots.txt", produces = MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
public String getRobotsTxt() {
String allowGoogleVisibility = System.getProperty("ALLOW_GOOGLE_VISABILITY");
if (allowGoogleVisibility == null)
allowGoogleVisibility = System.getenv("ALLOW_GOOGLE_VISABILITY");
if (allowGoogleVisibility == null)
allowGoogleVisibility = "true";
if (Boolean.parseBoolean(allowGoogleVisibility)) {
return "User-agent: Googlebot\nAllow: /\n\nUser-agent: *\nDisallow: /";
} else {
return "User-agent: Googlebot\nDisallow: /\n\nUser-agent: *\nDisallow: /";
}
}
}

View File

@ -53,6 +53,12 @@ public class OtherWebController {
return "other/change-metadata";
}
@GetMapping("/compare")
@Hidden
public String compareForm(Model model) {
model.addAttribute("currentPage", "compare");
return "other/compare";
}
public List<String> getAvailableTesseractLanguages() {
String tessdataDir = "/usr/share/tesseract-ocr/4.00/tessdata";

View File

@ -117,8 +117,17 @@ home.flatten.desc=Remove all interactive elements and forms from a PDF
home.repair.title=Repair
home.repair.desc=Tries to repair a corrupt/broken PDF
downloadPdf=Download PDF
text=Text
font=Font
sign.title=Sign
sign.header=Sign PDFs
sign.upload=Upload Image
sign.draw=Draw Signature
sign.text=Text Input
sign.clear=Clear
sign.add=Add
ScannerImageSplit.selectText.1=Angle Threshold:
ScannerImageSplit.selectText.2=Sets the minimum absolute angle required for the image to be rotated (default: 10).

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,135 @@
<!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=#{repair.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="#{repair.header}"></h2>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput2', multiple=false, accept='application/pdf')}"></div>
<button onclick="comparePDFs()">Compare</button>
<div id="result"></div>
<script>
async function comparePDFs() {
const file1 = document.getElementById("fileInput-input").files[0];
const file2 = document.getElementById("fileInput2-input").files[0];
if (!file1 || !file2) {
console.error("Please select two PDF files to compare");
return;
}
const [pdf1, pdf2] = await Promise.all([
pdfjsLib.getDocument(URL.createObjectURL(file1)).promise,
pdfjsLib.getDocument(URL.createObjectURL(file2)).promise
]);
const extractText = async (pdf) => {
const pages = [];
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const content = await page.getTextContent();
const strings = content.items.map(item => item.str);
pages.push(strings.join(""));
}
return pages.join("\n");
};
const [text1, text2] = await Promise.all([
extractText(pdf1),
extractText(pdf2)
]);
const diff = (text1, text2) => {
const lines1 = text1.split("\n");
const lines2 = text2.split("\n");
const result = [];
let i = 0, j = 0;
while (i < lines1.length || j < lines2.length) {
console.log(`lines1[${i}]='${lines1[i]}', lines2[${j}]='${lines2[j]}'`);
console.log(`i=${i}, j=${j}`);
if (lines1[i] === lines2[j]) {
result.push([i, j, lines1[i]]);
i++;
j++;
console.log(`i=${i}, j=${j}`);
} else {
let k = i, l = j;
while (k < lines1.length && l < lines2.length && lines1[k] !== lines2[l]) {
k++;
l++;
}
for (let x = i; x < k; x++) {
result.push([x, -1, lines1[x]]);
}
for (let y = j; y < l; y++) {
result.push([-1, y, lines2[y]]);
}
i = k;
j = l;
}
}
return result;
};
const differences = diff(text1, text2);
const highlightDifferences = async (pdf, differences) => {
for (const difference of differences) {
const [pageIndex, lineIndex, lineText] = difference;
if (lineIndex === -1) {
continue;
}
console.log(pageIndex);
const page = await pdf.getPage(pageIndex);
const viewport = page.getViewport({ scale: 1 });
const [left,top] = viewport.convertToViewportPoint(0, lineIndex * 20);
const [right, bottom] = viewport.convertToViewportPoint(500, (lineIndex + 1) * 20);
const annotation = {
type: "Highlight",
rect: [left, top, right - left, bottom - top],
color: [255, 255, 0],
opacity: 0.5,
quadPoints:
[
left, top, right, top, right, bottom, left, bottom
]
};
await page.addAnnotation(annotation);
const message = `Difference found in page ${pageIndex }, line ${lineIndex + 1}: ${lineText}`;
const p = document.createElement("p");
p.textContent = message;
document.getElementById("result").appendChild(p);
}
};
await highlightDifferences(pdf1, differences);
}
</script>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@ -3,20 +3,21 @@
<head>
<th:block th:insert="~{fragments/common :: head(title=#{sign.title})}"></th:block>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PDF Signature App</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.min.js"></script>
<script src="https://unpkg.com/pdf-lib@1.18.0/dist/umd/pdf-lib.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/signature_pad@4.1.5/dist/signature_pad.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/interact.js/1.10.11/interact.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Estonia&family=Tangerine&family=Windsong&display=swap" rel="stylesheet">
</head>
<script src="js/signature_pad.umd.min.js"></script>
<script src="js/interact.min.js"></script>
</head>
<style>
@font-face {
font-family: 'Estonia';
src: url(fonts/Estonia.woff2) format('woff2');
}
@font-face {
font-family: 'Tangerine';
src: url(fonts/Tangerine.woff2) format('woff2');
}
</style>
<body>
<th:block th:insert="~{fragments/common.html :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
@ -49,7 +50,7 @@
</script>
<div class="tab-group show-on-file-selected">
<div class="tab-container" title="Upload Image">
<div class="tab-container" th:title="#{sign.upload}">
<div th:replace="~{fragments/common :: fileSelector(name='image-upload', multiple=true, accept='image/*', inputText=#{imgPrompt})}"></div>
<script>
const imageUpload = document.querySelector('input[name=image-upload]');
@ -67,11 +68,11 @@
});
</script>
</div>
<div class="tab-container drawing-pad-container" title="Draw Signature">
<div class="tab-container drawing-pad-container" th:title="#{sign.draw}">
<canvas id="drawing-pad-canvas"></canvas>
<br>
<button id="clear-signature" class="btn btn-outline-danger mt-2" onclick="signaturePad.clear()">Clear</button>
<button id="save-signature" class="btn btn-outline-success mt-2" onclick="addDraggableFromPad()">Add</button>
<button id="clear-signature" class="btn btn-outline-danger mt-2" onclick="signaturePad.clear()" th:text="#{sign.clear}"></button>
<button id="save-signature" class="btn btn-outline-success mt-2" onclick="addDraggableFromPad()" th:text="#{sign.add}"></button>
<script>
const signaturePadCanvas = document.getElementById('drawing-pad-canvas');
const signaturePad = new SignaturePad(signaturePadCanvas, {
@ -95,17 +96,16 @@
}
</style>
</div>
<div class="tab-container" title="Text Input">
<label class="form-check-label" for="sigText">Text</label>
<div class="tab-container" th:title="#{sign.text}">
<label class="form-check-label" for="sigText" th:text="#{text}"></label>
<input type="text" class="form-control" id="sigText" name="sigText">
<label>Font:</label>
<label th:text="#{font}"></label>
<select class="form-control" name="font" id="font-select">
<option value="Estonia" class="estonia-font">Estonia</option>
<option value="Tangerine" class="tangerine-font">Tangerine</option>
<option value="Windsong" class="windsong-font">Windsong</option>
</select>
<div class="margin-auto-parent">
<button id="save-text-signature" class="btn btn-outline-success mt-2 margin-center" onclick="addDraggableFromText()">Add</button>
<button id="save-text-signature" class="btn btn-outline-success mt-2 margin-center" onclick="addDraggableFromText()" th:text="#{sign.add}"></button>
</div>
<script>
function addDraggableFromText() {
@ -148,7 +148,7 @@
<!-- draggables box -->
<div id="box-drag-container" class="show-on-file-selected">
<canvas id="pdf-canvas"></canvas>
<script src="/js/draggable-utils.js"></script>
<script src="js/draggable-utils.js"></script>
<div class="draggable-buttons-box ignore-rtl">
<button class="btn btn-outline-secondary" onclick="DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted())">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">