mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2024-11-16 12:20:12 +01:00
pdfjs worker changes and crop fix
This commit is contained in:
parent
e83a027023
commit
749461334d
@ -90,7 +90,7 @@ public class CropController {
|
|||||||
@PostMapping(value = "/crop", consumes = "multipart/form-data")
|
@PostMapping(value = "/crop", consumes = "multipart/form-data")
|
||||||
@Operation(summary = "Crops a PDF document", description = "This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO")
|
@Operation(summary = "Crops a PDF document", description = "This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO")
|
||||||
public ResponseEntity<byte[]> cropPdf(
|
public ResponseEntity<byte[]> cropPdf(
|
||||||
@Parameter(description = "The input PDF file", required = true) @RequestParam("file") MultipartFile file,
|
@Parameter(description = "The input PDF file", required = true) @RequestParam("fileInput") MultipartFile file,
|
||||||
@Parameter(description = "The x-coordinate of the top-left corner of the crop area", required = true, schema = @Schema(type = "number")) @RequestParam("x") float x,
|
@Parameter(description = "The x-coordinate of the top-left corner of the crop area", required = true, schema = @Schema(type = "number")) @RequestParam("x") float x,
|
||||||
@Parameter(description = "The y-coordinate of the top-left corner of the crop area", required = true, schema = @Schema(type = "number")) @RequestParam("y") float y,
|
@Parameter(description = "The y-coordinate of the top-left corner of the crop area", required = true, schema = @Schema(type = "number")) @RequestParam("y") float y,
|
||||||
@Parameter(description = "The width of the crop area", required = true, schema = @Schema(type = "number")) @RequestParam("width") float width,
|
@Parameter(description = "The width of the crop area", required = true, schema = @Schema(type = "number")) @RequestParam("width") float width,
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
package stirling.software.SPDF.controller.api.converters;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
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.tags.Tag;
|
||||||
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@Tag(name = "Convert", description = "Convert APIs")
|
||||||
|
public class ConvertHtmlToPDF {
|
||||||
|
|
||||||
|
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-pdfa")
|
||||||
|
@Operation(
|
||||||
|
summary = "Convert a PDF to a PDF/A",
|
||||||
|
description = "This endpoint converts a PDF file to a PDF/A file. PDF/A is a format designed for long-term archiving of digital documents. Input:PDF Output:PDF Type:SISO"
|
||||||
|
)
|
||||||
|
public ResponseEntity<byte[]> pdfToPdfA(
|
||||||
|
@RequestPart(required = true, value = "fileInput")
|
||||||
|
@Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true)
|
||||||
|
MultipartFile inputFile) throws IOException, InterruptedException {
|
||||||
|
|
||||||
|
// Save the uploaded file to a temporary location
|
||||||
|
Path tempInputFile = Files.createTempFile("input_", ".pdf");
|
||||||
|
inputFile.transferTo(tempInputFile.toFile());
|
||||||
|
|
||||||
|
// Prepare the output file path
|
||||||
|
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
|
||||||
|
|
||||||
|
// Prepare the OCRmyPDF command
|
||||||
|
List<String> command = new ArrayList<>();
|
||||||
|
command.add("ocrmypdf");
|
||||||
|
command.add("--skip-text");
|
||||||
|
command.add("--tesseract-timeout=0");
|
||||||
|
command.add("--output-type");
|
||||||
|
command.add("pdfa");
|
||||||
|
command.add(tempInputFile.toString());
|
||||||
|
command.add(tempOutputFile.toString());
|
||||||
|
|
||||||
|
int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF).runCommandWithOutputHandling(command);
|
||||||
|
|
||||||
|
// Read the optimized PDF file
|
||||||
|
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||||
|
|
||||||
|
// Clean up the temporary files
|
||||||
|
Files.delete(tempInputFile);
|
||||||
|
Files.delete(tempOutputFile);
|
||||||
|
|
||||||
|
// Return the optimized PDF as a response
|
||||||
|
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_PDFA.pdf";
|
||||||
|
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -126,7 +126,7 @@ home.PDFToWord.desc=Convert PDF to Word formats (DOC, DOCX and ODT)
|
|||||||
home.PDFToPresentation.title=PDF to Presentation
|
home.PDFToPresentation.title=PDF to Presentation
|
||||||
home.PDFToPresentation.desc=Convert PDF to Presentation formats (PPT, PPTX and ODP)
|
home.PDFToPresentation.desc=Convert PDF to Presentation formats (PPT, PPTX and ODP)
|
||||||
|
|
||||||
home.PDFToText.title=PDF to Text/RTF
|
home.PDFToText.title=PDF to RTF (Text)
|
||||||
home.PDFToText.desc=Convert PDF to Text or RTF format
|
home.PDFToText.desc=Convert PDF to Text or RTF format
|
||||||
|
|
||||||
home.PDFToHTML.title=PDF to HTML
|
home.PDFToHTML.title=PDF to HTML
|
||||||
@ -582,8 +582,8 @@ PDFToPresentation.submit=Convert
|
|||||||
|
|
||||||
|
|
||||||
#PDFToText
|
#PDFToText
|
||||||
PDFToText.title=PDF to Text/RTF
|
PDFToText.title=PDF to RTF (Text)
|
||||||
PDFToText.header=PDF to Text/RTF
|
PDFToText.header=PDF to RTF (Text)
|
||||||
PDFToText.selectText.1=Output file format
|
PDFToText.selectText.1=Output file format
|
||||||
PDFToText.credit=This service uses LibreOffice for file conversion.
|
PDFToText.credit=This service uses LibreOffice for file conversion.
|
||||||
PDFToText.submit=Convert
|
PDFToText.submit=Convert
|
||||||
|
@ -594,7 +594,7 @@ PDFToPresentation.submit=변환
|
|||||||
|
|
||||||
|
|
||||||
#PDFToText
|
#PDFToText
|
||||||
PDFToText.title=PDF to Text/RTF
|
PDFToText.title=PDF to RTF (Text)
|
||||||
PDFToText.header=PDF를 텍스트/RTF로 변환
|
PDFToText.header=PDF를 텍스트/RTF로 변환
|
||||||
PDFToText.selectText.1=출력 파일 형식
|
PDFToText.selectText.1=출력 파일 형식
|
||||||
PDFToText.credit=이 서비스는 파일 변환을 위해 LibreOffice를 사용합니다.
|
PDFToText.credit=이 서비스는 파일 변환을 위해 LibreOffice를 사용합니다.
|
||||||
|
@ -135,7 +135,7 @@ home.PDFToWord.desc=将PDF转换为Word格式(DOC、DOCX和ODT)。
|
|||||||
home.PDFToPresentation.title=PDF To Presentation
|
home.PDFToPresentation.title=PDF To Presentation
|
||||||
home.PDFToPresentation.desc=将PDF转换成演示文稿格式(PPT、PPTX和ODP)。
|
home.PDFToPresentation.desc=将PDF转换成演示文稿格式(PPT、PPTX和ODP)。
|
||||||
|
|
||||||
home.PDFToText.title=PDF To Text/RTF
|
home.PDFToText.title=PDF to RTF (Text)
|
||||||
home.PDFToText.desc=将PDF转换为文本或RTF格式
|
home.PDFToText.desc=将PDF转换为文本或RTF格式
|
||||||
|
|
||||||
home.PDFToHTML.title=PDF To HTML
|
home.PDFToHTML.title=PDF To HTML
|
||||||
@ -594,7 +594,7 @@ PDFToPresentation.submit=转换
|
|||||||
|
|
||||||
|
|
||||||
#PDFToText
|
#PDFToText
|
||||||
PDFToText.title=PDF To Text/RTF
|
PDFToText.title=PDF to RTF (Text)
|
||||||
PDFToText.header=将PDF转换成文本/RTF
|
PDFToText.header=将PDF转换成文本/RTF
|
||||||
PDFToText.selectText.1=输出文件格式
|
PDFToText.selectText.1=输出文件格式
|
||||||
PDFToText.credit=该服务使用LibreOffice进行文件转换。
|
PDFToText.credit=该服务使用LibreOffice进行文件转换。
|
||||||
|
@ -69,4 +69,14 @@ html[lang-direction="rtl"] label.form-check-label {
|
|||||||
#pdf-canvas {
|
#pdf-canvas {
|
||||||
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384);
|
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
}
|
||||||
|
.fixed-shadow-canvas {
|
||||||
|
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.shadow-canvas {
|
||||||
|
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384);
|
||||||
|
}
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
}
|
}
|
@ -1,206 +1,207 @@
|
|||||||
class PdfContainer {
|
class PdfContainer {
|
||||||
fileName;
|
fileName;
|
||||||
pagesContainer;
|
pagesContainer;
|
||||||
pagesContainerWrapper;
|
pagesContainerWrapper;
|
||||||
pdfAdapters;
|
pdfAdapters;
|
||||||
|
|
||||||
constructor(id, wrapperId, pdfAdapters) {
|
constructor(id, wrapperId, pdfAdapters) {
|
||||||
this.fileName = null;
|
this.fileName = null;
|
||||||
this.pagesContainer = document.getElementById(id)
|
this.pagesContainer = document.getElementById(id)
|
||||||
this.pagesContainerWrapper = document.getElementById(wrapperId);
|
this.pagesContainerWrapper = document.getElementById(wrapperId);
|
||||||
this.movePageTo = this.movePageTo.bind(this);
|
this.movePageTo = this.movePageTo.bind(this);
|
||||||
this.addPdfs = this.addPdfs.bind(this);
|
this.addPdfs = this.addPdfs.bind(this);
|
||||||
this.rotateElement = this.rotateElement.bind(this);
|
this.rotateElement = this.rotateElement.bind(this);
|
||||||
this.rotateAll = this.rotateAll.bind(this);
|
this.rotateAll = this.rotateAll.bind(this);
|
||||||
this.exportPdf = this.exportPdf.bind(this);
|
this.exportPdf = this.exportPdf.bind(this);
|
||||||
|
|
||||||
this.pdfAdapters = pdfAdapters;
|
this.pdfAdapters = pdfAdapters;
|
||||||
|
|
||||||
this.pdfAdapters.forEach(adapter => {
|
this.pdfAdapters.forEach(adapter => {
|
||||||
adapter.setActions({
|
adapter.setActions({
|
||||||
movePageTo: this.movePageTo,
|
movePageTo: this.movePageTo,
|
||||||
addPdfs: this.addPdfs,
|
addPdfs: this.addPdfs,
|
||||||
rotateElement: this.rotateElement,
|
rotateElement: this.rotateElement,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
window.addPdfs = this.addPdfs;
|
window.addPdfs = this.addPdfs;
|
||||||
window.exportPdf = this.exportPdf;
|
window.exportPdf = this.exportPdf;
|
||||||
window.rotateAll = this.rotateAll;
|
window.rotateAll = this.rotateAll;
|
||||||
}
|
}
|
||||||
|
|
||||||
movePageTo(startElement, endElement, scrollTo = false) {
|
movePageTo(startElement, endElement, scrollTo = false) {
|
||||||
const childArray = Array.from(this.pagesContainer.childNodes);
|
const childArray = Array.from(this.pagesContainer.childNodes);
|
||||||
const startIndex = childArray.indexOf(startElement);
|
const startIndex = childArray.indexOf(startElement);
|
||||||
const endIndex = childArray.indexOf(endElement);
|
const endIndex = childArray.indexOf(endElement);
|
||||||
this.pagesContainer.removeChild(startElement);
|
this.pagesContainer.removeChild(startElement);
|
||||||
if(!endElement) {
|
if(!endElement) {
|
||||||
this.pagesContainer.append(startElement);
|
this.pagesContainer.append(startElement);
|
||||||
} else {
|
} else {
|
||||||
this.pagesContainer.insertBefore(startElement, endElement);
|
this.pagesContainer.insertBefore(startElement, endElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(scrollTo) {
|
if(scrollTo) {
|
||||||
const { width } = startElement.getBoundingClientRect();
|
const { width } = startElement.getBoundingClientRect();
|
||||||
const vector = (endIndex !== -1 && startIndex > endIndex)
|
const vector = (endIndex !== -1 && startIndex > endIndex)
|
||||||
? 0-width
|
? 0-width
|
||||||
: width;
|
: width;
|
||||||
|
|
||||||
this.pagesContainerWrapper.scroll({
|
this.pagesContainerWrapper.scroll({
|
||||||
left: this.pagesContainerWrapper.scrollLeft + vector,
|
left: this.pagesContainerWrapper.scrollLeft + vector,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addPdfs(nextSiblingElement) {
|
addPdfs(nextSiblingElement) {
|
||||||
var input = document.createElement('input');
|
var input = document.createElement('input');
|
||||||
input.type = 'file';
|
input.type = 'file';
|
||||||
input.multiple = true;
|
input.multiple = true;
|
||||||
input.setAttribute("accept", "application/pdf");
|
input.setAttribute("accept", "application/pdf");
|
||||||
|
|
||||||
input.onchange = async(e) => {
|
input.onchange = async(e) => {
|
||||||
const files = e.target.files;
|
const files = e.target.files;
|
||||||
this.fileName = files[0].name;
|
this.fileName = files[0].name;
|
||||||
for (var i=0; i < files.length; i++) {
|
for (var i=0; i < files.length; i++) {
|
||||||
await this.addPdfFile(files[i], nextSiblingElement);
|
await this.addPdfFile(files[i], nextSiblingElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.querySelectorAll(".enable-on-file").forEach(element => {
|
document.querySelectorAll(".enable-on-file").forEach(element => {
|
||||||
element.disabled = false;
|
element.disabled = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
rotateElement(element, deg) {
|
rotateElement(element, deg) {
|
||||||
var lastTransform = element.style.rotate;
|
var lastTransform = element.style.rotate;
|
||||||
if (!lastTransform) {
|
if (!lastTransform) {
|
||||||
lastTransform = "0";
|
lastTransform = "0";
|
||||||
}
|
}
|
||||||
const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ''));
|
const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ''));
|
||||||
const newAngle = lastAngle + deg;
|
const newAngle = lastAngle + deg;
|
||||||
|
|
||||||
element.style.rotate = newAngle + "deg";
|
element.style.rotate = newAngle + "deg";
|
||||||
}
|
}
|
||||||
|
|
||||||
async addPdfFile(file, nextSiblingElement) {
|
async addPdfFile(file, nextSiblingElement) {
|
||||||
const { renderer, pdfDocument } = await this.loadFile(file);
|
const { renderer, pdfDocument } = await this.loadFile(file);
|
||||||
|
|
||||||
for (var i=0; i < renderer.pageCount; i++) {
|
for (var i=0; i < renderer.pageCount; i++) {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
|
|
||||||
div.classList.add("page-container");
|
div.classList.add("page-container");
|
||||||
|
|
||||||
var img = document.createElement('img');
|
var img = document.createElement('img');
|
||||||
img.classList.add('page-image')
|
img.classList.add('page-image')
|
||||||
const imageSrc = await renderer.renderPage(i)
|
const imageSrc = await renderer.renderPage(i)
|
||||||
img.src = imageSrc;
|
img.src = imageSrc;
|
||||||
img.pageIdx = i;
|
img.pageIdx = i;
|
||||||
img.rend = renderer;
|
img.rend = renderer;
|
||||||
img.doc = pdfDocument;
|
img.doc = pdfDocument;
|
||||||
div.appendChild(img);
|
div.appendChild(img);
|
||||||
|
|
||||||
this.pdfAdapters.forEach((adapter) => {
|
this.pdfAdapters.forEach((adapter) => {
|
||||||
adapter.adapt?.(div)
|
adapter.adapt?.(div)
|
||||||
})
|
})
|
||||||
if (nextSiblingElement) {
|
if (nextSiblingElement) {
|
||||||
this.pagesContainer.insertBefore(div, nextSiblingElement);
|
this.pagesContainer.insertBefore(div, nextSiblingElement);
|
||||||
} else {
|
} else {
|
||||||
this.pagesContainer.appendChild(div);
|
this.pagesContainer.appendChild(div);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadFile(file) {
|
async loadFile(file) {
|
||||||
var objectUrl = URL.createObjectURL(file);
|
var objectUrl = URL.createObjectURL(file);
|
||||||
var pdfDocument = await this.toPdfLib(objectUrl);
|
var pdfDocument = await this.toPdfLib(objectUrl);
|
||||||
var renderer = await this.toRenderer(objectUrl);
|
var renderer = await this.toRenderer(objectUrl);
|
||||||
return { renderer, pdfDocument };
|
return { renderer, pdfDocument };
|
||||||
}
|
}
|
||||||
|
|
||||||
async toRenderer(objectUrl) {
|
async toRenderer(objectUrl) {
|
||||||
const pdf = await pdfjsLib.getDocument(objectUrl).promise;
|
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js'
|
||||||
return {
|
const pdf = await pdfjsLib.getDocument(objectUrl).promise;
|
||||||
document: pdf,
|
return {
|
||||||
pageCount: pdf.numPages,
|
document: pdf,
|
||||||
renderPage: async function(pageIdx) {
|
pageCount: pdf.numPages,
|
||||||
const page = await this.document.getPage(pageIdx+1);
|
renderPage: async function(pageIdx) {
|
||||||
|
const page = await this.document.getPage(pageIdx+1);
|
||||||
const canvas = document.createElement("canvas");
|
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
// set the canvas size to the size of the page
|
|
||||||
if (page.rotate == 90 || page.rotate == 270) {
|
// set the canvas size to the size of the page
|
||||||
canvas.width = page.view[3];
|
if (page.rotate == 90 || page.rotate == 270) {
|
||||||
canvas.height = page.view[2];
|
canvas.width = page.view[3];
|
||||||
} else {
|
canvas.height = page.view[2];
|
||||||
canvas.width = page.view[2];
|
} else {
|
||||||
canvas.height = page.view[3];
|
canvas.width = page.view[2];
|
||||||
}
|
canvas.height = page.view[3];
|
||||||
|
}
|
||||||
// render the page onto the canvas
|
|
||||||
var renderContext = {
|
// render the page onto the canvas
|
||||||
canvasContext: canvas.getContext("2d"),
|
var renderContext = {
|
||||||
viewport: page.getViewport({ scale: 1 })
|
canvasContext: canvas.getContext("2d"),
|
||||||
};
|
viewport: page.getViewport({ scale: 1 })
|
||||||
|
};
|
||||||
await page.render(renderContext).promise;
|
|
||||||
return canvas.toDataURL();
|
await page.render(renderContext).promise;
|
||||||
}
|
return canvas.toDataURL();
|
||||||
};
|
}
|
||||||
}
|
};
|
||||||
|
}
|
||||||
async toPdfLib(objectUrl) {
|
|
||||||
const existingPdfBytes = await fetch(objectUrl).then(res => res.arrayBuffer());
|
async toPdfLib(objectUrl) {
|
||||||
const pdfDoc = await PDFLib.PDFDocument.load(existingPdfBytes, { ignoreEncryption: true });
|
const existingPdfBytes = await fetch(objectUrl).then(res => res.arrayBuffer());
|
||||||
return pdfDoc;
|
const pdfDoc = await PDFLib.PDFDocument.load(existingPdfBytes, { ignoreEncryption: true });
|
||||||
}
|
return pdfDoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
rotateAll(deg) {
|
|
||||||
for (var i=0; i<this.pagesContainer.childNodes.length; i++) {
|
rotateAll(deg) {
|
||||||
const img = this.pagesContainer.childNodes[i].querySelector("img");
|
for (var i=0; i<this.pagesContainer.childNodes.length; i++) {
|
||||||
if (!img) continue;
|
const img = this.pagesContainer.childNodes[i].querySelector("img");
|
||||||
this.rotateElement(img, deg)
|
if (!img) continue;
|
||||||
}
|
this.rotateElement(img, deg)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
async exportPdf() {
|
|
||||||
const pdfDoc = await PDFLib.PDFDocument.create();
|
async exportPdf() {
|
||||||
for (var i=0; i<this.pagesContainer.childNodes.length; i++) {
|
const pdfDoc = await PDFLib.PDFDocument.create();
|
||||||
const img = this.pagesContainer.childNodes[i].querySelector("img");
|
for (var i=0; i<this.pagesContainer.childNodes.length; i++) {
|
||||||
if (!img) continue;
|
const img = this.pagesContainer.childNodes[i].querySelector("img");
|
||||||
const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx])
|
if (!img) continue;
|
||||||
const page = pages[0];
|
const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx])
|
||||||
|
const page = pages[0];
|
||||||
const rotation = img.style.rotate;
|
|
||||||
if (rotation) {
|
const rotation = img.style.rotate;
|
||||||
const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, ''));
|
if (rotation) {
|
||||||
page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle))
|
const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, ''));
|
||||||
}
|
page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle))
|
||||||
|
}
|
||||||
pdfDoc.addPage(page);
|
|
||||||
}
|
pdfDoc.addPage(page);
|
||||||
const pdfBytes = await pdfDoc.save();
|
}
|
||||||
const pdfBlob = new Blob([pdfBytes], { type: 'application/pdf' });
|
const pdfBytes = await pdfDoc.save();
|
||||||
const url = URL.createObjectURL(pdfBlob);
|
const pdfBlob = new Blob([pdfBytes], { type: 'application/pdf' });
|
||||||
const downloadOption = localStorage.getItem('downloadOption');
|
const url = URL.createObjectURL(pdfBlob);
|
||||||
|
const downloadOption = localStorage.getItem('downloadOption');
|
||||||
if (downloadOption === 'sameWindow') {
|
|
||||||
// Open the file in the same window
|
if (downloadOption === 'sameWindow') {
|
||||||
window.location.href = url;
|
// Open the file in the same window
|
||||||
} else if (downloadOption === 'newWindow') {
|
window.location.href = url;
|
||||||
// Open the file in a new window
|
} else if (downloadOption === 'newWindow') {
|
||||||
window.open(url, '_blank');
|
// Open the file in a new window
|
||||||
} else {
|
window.open(url, '_blank');
|
||||||
// Download the file
|
} else {
|
||||||
const downloadLink = document.createElement('a');
|
// Download the file
|
||||||
downloadLink.href = url;
|
const downloadLink = document.createElement('a');
|
||||||
downloadLink.download = this.fileName ? this.fileName : 'managed.pdf';
|
downloadLink.href = url;
|
||||||
downloadLink.click();
|
downloadLink.download = this.fileName ? this.fileName : 'managed.pdf';
|
||||||
}
|
downloadLink.click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
export default PdfContainer;
|
|
||||||
|
export default PdfContainer;
|
||||||
|
@ -1,35 +1,34 @@
|
|||||||
<!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=#{PDFToText.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{PDFToText.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="#{PDFToText.header}"></h2>
|
<h2 th:text="#{PDFToText.header}"></h2>
|
||||||
<form method="post" enctype="multipart/form-data" th:action="@{pdf-to-text}">
|
<form method="post" enctype="multipart/form-data" th:action="@{pdf-to-text}">
|
||||||
<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>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label th:text="#{PDFToText.selectText.1}"></label>
|
<label th:text="#{PDFToText.selectText.1}"></label>
|
||||||
<select class="form-control" name="outputFormat">
|
<select class="form-control" name="outputFormat">
|
||||||
<option value="rtf">RTF</option>
|
<option value="rtf">RTF</option>
|
||||||
<option value="txt:Text">TXT</option>
|
</select>
|
||||||
</select>
|
</div>
|
||||||
</div>
|
<br>
|
||||||
<br>
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToText.submit}"></button>
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToText.submit}"></button>
|
|
||||||
|
</form>
|
||||||
</form>
|
<p class="mt-3" th:text="#{PDFToText.credit}"></p>
|
||||||
<p class="mt-3" th:text="#{PDFToText.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>
|
@ -22,13 +22,13 @@
|
|||||||
<button type="submit" class="btn btn-primary" th:text="#{crop.submit}"></button>
|
<button type="submit" class="btn btn-primary" th:text="#{crop.submit}"></button>
|
||||||
</form>
|
</form>
|
||||||
<div style="position: relative; display: inline-block;">
|
<div style="position: relative; display: inline-block;">
|
||||||
<canvas id="pdf-canvas" style="position: absolute; top: 0; left: 0; z-index: 1;"></canvas>
|
<canvas id="crop-pdf-canvas" style="position: absolute; top: 0; left: 0; z-index: 1;"></canvas>
|
||||||
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2;"></canvas>
|
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2;"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
let pdfCanvas = document.getElementById('pdf-canvas');
|
let pdfCanvas = document.getElementById('crop-pdf-canvas');
|
||||||
let overlayCanvas = document.getElementById('overlayCanvas');
|
let overlayCanvas = document.getElementById('overlayCanvas');
|
||||||
|
|
||||||
let context = pdfCanvas.getContext('2d');
|
let context = pdfCanvas.getContext('2d');
|
||||||
@ -61,6 +61,7 @@
|
|||||||
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.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js'
|
||||||
pdfjsLib.getDocument(typedArray).promise.then(function(pdf) {
|
pdfjsLib.getDocument(typedArray).promise.then(function(pdf) {
|
||||||
pdfDoc = pdf;
|
pdfDoc = pdf;
|
||||||
totalPages = pdf.numPages;
|
totalPages = pdf.numPages;
|
||||||
@ -126,6 +127,7 @@
|
|||||||
|
|
||||||
let renderContext = { canvasContext: context, viewport: viewport };
|
let renderContext = { canvasContext: context, viewport: viewport };
|
||||||
page.render(renderContext);
|
page.render(renderContext);
|
||||||
|
pdfCanvas.classList.add("shadow-canvas");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,140 +1,141 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
|
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{addImage.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{addImage.title})}"></th:block>
|
||||||
<script src="js/thirdParty/interact.min.js"></script>
|
<script src="js/thirdParty/interact.min.js"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="page-container">
|
<div id="page-container">
|
||||||
<div id="content-wrap">
|
<div id="content-wrap">
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
<br> <br>
|
<br> <br>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2 th:text="#{addImage.header}"></h2>
|
<h2 th:text="#{addImage.header}"></h2>
|
||||||
|
|
||||||
<!-- pdf selector -->
|
<!-- pdf selector -->
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='pdf-upload', multiple=false, accept='application/pdf')}"></div>
|
<div th:replace="~{fragments/common :: fileSelector(name='pdf-upload', multiple=false, accept='application/pdf')}"></div>
|
||||||
<script>
|
<script>
|
||||||
let originalFileName = '';
|
let originalFileName = '';
|
||||||
document.querySelector('input[name=pdf-upload]').addEventListener('change', async (event) => {
|
document.querySelector('input[name=pdf-upload]').addEventListener('change', async (event) => {
|
||||||
const file = event.target.files[0];
|
const file = event.target.files[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
originalFileName = file.name.replace(/\.[^/.]+$/, "");
|
originalFileName = file.name.replace(/\.[^/.]+$/, "");
|
||||||
const pdfData = await file.arrayBuffer();
|
const pdfData = await file.arrayBuffer();
|
||||||
const pdfDoc = await pdfjsLib.getDocument({ data: pdfData }).promise;
|
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js'
|
||||||
await DraggableUtils.renderPage(pdfDoc, 0);
|
const pdfDoc = await pdfjsLib.getDocument({ data: pdfData }).promise;
|
||||||
|
await DraggableUtils.renderPage(pdfDoc, 0);
|
||||||
document.querySelectorAll(".show-on-file-selected").forEach(el => {
|
|
||||||
el.style.cssText = '';
|
document.querySelectorAll(".show-on-file-selected").forEach(el => {
|
||||||
})
|
el.style.cssText = '';
|
||||||
}
|
})
|
||||||
});
|
}
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
});
|
||||||
document.querySelectorAll(".show-on-file-selected").forEach(el => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
el.style.cssText = "display:none !important";
|
document.querySelectorAll(".show-on-file-selected").forEach(el => {
|
||||||
})
|
el.style.cssText = "display:none !important";
|
||||||
});
|
})
|
||||||
</script>
|
});
|
||||||
|
</script>
|
||||||
<div class="tab-group show-on-file-selected">
|
|
||||||
<div class="tab-container" th:title="#{addImage.upload}">
|
<div class="tab-group show-on-file-selected">
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='image-upload', multiple=true, accept='image/*', inputText=#{imgPrompt})}"></div>
|
<div class="tab-container" th:title="#{addImage.upload}">
|
||||||
<script>
|
<div th:replace="~{fragments/common :: fileSelector(name='image-upload', multiple=true, accept='image/*', inputText=#{imgPrompt})}"></div>
|
||||||
const imageUpload = document.querySelector('input[name=image-upload]');
|
<script>
|
||||||
imageUpload.addEventListener('change', e => {
|
const imageUpload = document.querySelector('input[name=image-upload]');
|
||||||
if(!e.target.files) {
|
imageUpload.addEventListener('change', e => {
|
||||||
return;
|
if(!e.target.files) {
|
||||||
}
|
return;
|
||||||
for (const imageFile of e.target.files) {
|
}
|
||||||
var reader = new FileReader();
|
for (const imageFile of e.target.files) {
|
||||||
reader.readAsDataURL(imageFile);
|
var reader = new FileReader();
|
||||||
reader.onloadend = function (e) {
|
reader.readAsDataURL(imageFile);
|
||||||
DraggableUtils.createDraggableCanvasFromUrl(e.target.result);
|
reader.onloadend = function (e) {
|
||||||
};
|
DraggableUtils.createDraggableCanvasFromUrl(e.target.result);
|
||||||
}
|
};
|
||||||
});
|
}
|
||||||
</script>
|
});
|
||||||
</div>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<!-- draggables box -->
|
|
||||||
<div id="box-drag-container" class="show-on-file-selected">
|
<!-- draggables box -->
|
||||||
<canvas id="pdf-canvas"></canvas>
|
<div id="box-drag-container" class="show-on-file-selected">
|
||||||
<script src="js/draggable-utils.js"></script>
|
<canvas id="pdf-canvas"></canvas>
|
||||||
<div class="draggable-buttons-box ignore-rtl">
|
<script src="js/draggable-utils.js"></script>
|
||||||
<button class="btn btn-outline-secondary" onclick="DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted())">
|
<div class="draggable-buttons-box ignore-rtl">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">
|
<button class="btn btn-outline-secondary" onclick="DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted())">
|
||||||
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z"/>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">
|
||||||
<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1ZM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118ZM2.5 3h11V2h-11v1Z"/>
|
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z"/>
|
||||||
</svg>
|
<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1ZM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118ZM2.5 3h11V2h-11v1Z"/>
|
||||||
</button>
|
</svg>
|
||||||
<button class="btn btn-outline-secondary" onclick="document.documentElement.getAttribute('lang-direction')==='rtl' ? DraggableUtils.incrementPage() : DraggableUtils.decrementPage()" style="margin-left:auto">
|
</button>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-left" viewBox="0 0 16 16">
|
<button class="btn btn-outline-secondary" onclick="document.documentElement.getAttribute('lang-direction')==='rtl' ? DraggableUtils.incrementPage() : DraggableUtils.decrementPage()" style="margin-left:auto">
|
||||||
<path fill-rule="evenodd" d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z"/>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-left" viewBox="0 0 16 16">
|
||||||
</svg>
|
<path fill-rule="evenodd" d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z"/>
|
||||||
</button>
|
</svg>
|
||||||
<button class="btn btn-outline-secondary" onclick="document.documentElement.getAttribute('lang-direction')==='rtl' ? DraggableUtils.decrementPage() : DraggableUtils.incrementPage()">
|
</button>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-right" viewBox="0 0 16 16">
|
<button class="btn btn-outline-secondary" onclick="document.documentElement.getAttribute('lang-direction')==='rtl' ? DraggableUtils.decrementPage() : DraggableUtils.incrementPage()">
|
||||||
<path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"/>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-right" viewBox="0 0 16 16">
|
||||||
</svg>
|
<path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"/>
|
||||||
</button>
|
</svg>
|
||||||
</div>
|
</button>
|
||||||
<style>
|
</div>
|
||||||
#box-drag-container {
|
<style>
|
||||||
position: relative;
|
#box-drag-container {
|
||||||
margin: 20px 0;
|
position: relative;
|
||||||
}
|
margin: 20px 0;
|
||||||
#pdf-canvas {
|
}
|
||||||
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384);
|
#pdf-canvas {
|
||||||
width: 100%;
|
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384);
|
||||||
}
|
width: 100%;
|
||||||
.draggable-buttons-box {
|
}
|
||||||
position: absolute;
|
.draggable-buttons-box {
|
||||||
top: 0;
|
position: absolute;
|
||||||
padding: 10px;
|
top: 0;
|
||||||
width: 100%;
|
padding: 10px;
|
||||||
display: flex;
|
width: 100%;
|
||||||
gap: 5px;
|
display: flex;
|
||||||
}
|
gap: 5px;
|
||||||
.draggable-buttons-box > button {
|
}
|
||||||
z-index: 10;
|
.draggable-buttons-box > button {
|
||||||
background-color: rgba(13, 110, 253, 0.1);
|
z-index: 10;
|
||||||
}
|
background-color: rgba(13, 110, 253, 0.1);
|
||||||
.draggable-canvas {
|
}
|
||||||
border: 1px solid red;
|
.draggable-canvas {
|
||||||
position: absolute;
|
border: 1px solid red;
|
||||||
touch-action: none;
|
position: absolute;
|
||||||
user-select: none;
|
touch-action: none;
|
||||||
top: 0px;
|
user-select: none;
|
||||||
left: 0;
|
top: 0px;
|
||||||
}
|
left: 0;
|
||||||
</style>
|
}
|
||||||
</div>
|
</style>
|
||||||
|
</div>
|
||||||
<!-- download button -->
|
|
||||||
<div class="margin-auto-parent">
|
<!-- download button -->
|
||||||
<button id="download-pdf" class="btn btn-primary mb-2 show-on-file-selected margin-center">Download PDF</button>
|
<div class="margin-auto-parent">
|
||||||
</div>
|
<button id="download-pdf" class="btn btn-primary mb-2 show-on-file-selected margin-center">Download PDF</button>
|
||||||
<script>
|
</div>
|
||||||
document.getElementById("download-pdf").addEventListener('click', async() => {
|
<script>
|
||||||
const modifiedPdf = await DraggableUtils.getOverlayedPdfDocument();
|
document.getElementById("download-pdf").addEventListener('click', async() => {
|
||||||
const modifiedPdfBytes = await modifiedPdf.save();
|
const modifiedPdf = await DraggableUtils.getOverlayedPdfDocument();
|
||||||
const blob = new Blob([modifiedPdfBytes], { type: 'application/pdf' });
|
const modifiedPdfBytes = await modifiedPdf.save();
|
||||||
const link = document.createElement('a');
|
const blob = new Blob([modifiedPdfBytes], { type: 'application/pdf' });
|
||||||
link.href = URL.createObjectURL(blob);
|
const link = document.createElement('a');
|
||||||
link.download = originalFileName + '_addedImage.pdf';
|
link.href = URL.createObjectURL(blob);
|
||||||
link.click();
|
link.download = originalFileName + '_addedImage.pdf';
|
||||||
});
|
link.click();
|
||||||
</script>
|
});
|
||||||
</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>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
@ -14,37 +14,50 @@
|
|||||||
<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-12">
|
||||||
<h2 th:text="#{adjustContrast.header}"></h2>
|
<div class="row justify-content-center">
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
|
<div class="col-md-3">
|
||||||
<h4>
|
<div id="sliders-container" style="display:none;">
|
||||||
<span th:text="#{adjustContrast.contrast}"></span> <span id="contrast-val">100</span>%
|
<h4>
|
||||||
</h4>
|
<span th:text="#{adjustContrast.contrast}"></span> <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>
|
<h4>
|
||||||
<span th:text="#{adjustContrast.brightness}"></span> <span id="brightness-val">100</span>%
|
<span th:text="#{adjustContrast.brightness}"></span> <span id="brightness-val">100</span>%
|
||||||
</h4>
|
</h4>
|
||||||
<input type="range" min="0" max="200" value="100"
|
<input type="range" min="0" max="200" value="100" id="brightness-slider" />
|
||||||
id="brightness-slider" />
|
|
||||||
|
|
||||||
<h4>
|
<h4>
|
||||||
<span th:text="#{adjustContrast.saturation}"></span> <span id="saturation-val">100</span>%
|
<span th:text="#{adjustContrast.saturation}"></span> <span id="saturation-val">100</span>%
|
||||||
</h4>
|
</h4>
|
||||||
<input type="range" min="0" max="200" value="100"
|
<input type="range" min="0" max="200" value="100" id="saturation-slider" />
|
||||||
id="saturation-slider" />
|
</div>
|
||||||
|
</div>
|
||||||
</br>
|
<div class="col-md-7">
|
||||||
<canvas id="pdf-canvas"></canvas>
|
<h2 th:text="#{adjustContrast.header}"></h2>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<canvas id="contrast-pdf-canvas"></canvas>
|
||||||
|
<button id="download-button" class="btn btn-primary" th:text="#{adjustContrast.download}"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
#flex-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
#sliders-container {
|
||||||
|
padding: 0 20px; /* Add some padding to separate sliders from canvas */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<button id="download-button" class="btn btn-primary" th:text="#{adjustContrast.download}"></button>
|
|
||||||
|
|
||||||
<script src="pdfjs/pdf.js"></script>
|
<script src="pdfjs/pdf.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var canvas = document.getElementById('pdf-canvas');
|
var canvas = document.getElementById('contrast-pdf-canvas');
|
||||||
var context = canvas.getContext('2d');
|
var context = canvas.getContext('2d');
|
||||||
var originalImageData = null;
|
var originalImageData = null;
|
||||||
var allPages = [];
|
var allPages = [];
|
||||||
@ -55,6 +68,7 @@
|
|||||||
var fileReader = new FileReader();
|
var fileReader = new FileReader();
|
||||||
fileReader.onload = async function() {
|
fileReader.onload = async function() {
|
||||||
var data = new Uint8Array(this.result);
|
var data = new Uint8Array(this.result);
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js'
|
||||||
pdf = await pdfjsLib.getDocument({data: data}).promise;
|
pdf = await pdfjsLib.getDocument({data: data}).promise;
|
||||||
|
|
||||||
// Get the number of pages in the PDF
|
// Get the number of pages in the PDF
|
||||||
@ -65,6 +79,8 @@
|
|||||||
pdfDoc = await PDFLib.PDFDocument.create();
|
pdfDoc = await PDFLib.PDFDocument.create();
|
||||||
// Render the first page in the viewer
|
// Render the first page in the viewer
|
||||||
await renderPageAndAdjustImageProperties(1);
|
await renderPageAndAdjustImageProperties(1);
|
||||||
|
document.getElementById("sliders-container").style.display = "block";
|
||||||
|
|
||||||
};
|
};
|
||||||
fileReader.readAsArrayBuffer(file);
|
fileReader.readAsArrayBuffer(file);
|
||||||
}
|
}
|
||||||
@ -90,6 +106,7 @@
|
|||||||
adjustImageProperties();
|
adjustImageProperties();
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
canvas.classList.add("fixed-shadow-canvas");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,263 +1,263 @@
|
|||||||
<!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=#{changeMetadata.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{changeMetadata.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="#{changeMetadata.header}"></h2>
|
<h2 th:text="#{changeMetadata.header}"></h2>
|
||||||
|
|
||||||
<form method="post" id="form1" 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, accept='application/pdf')}"></div>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
|
||||||
<p class="text-muted" th:text="#{changeMetadata.selectText.1}"></p>
|
<p class="text-muted" th:text="#{changeMetadata.selectText.1}"></p>
|
||||||
|
|
||||||
<div class="form-group-inline form-check">
|
<div class="form-group-inline form-check">
|
||||||
<input type="checkbox" class="form-check-input" id="deleteAll" name="deleteAll">
|
<input type="checkbox" class="form-check-input" id="deleteAll" name="deleteAll">
|
||||||
<label class="ml-3" for="deleteAll" th:text="#{changeMetadata.selectText.2}" ></label>
|
<label class="ml-3" for="deleteAll" th:text="#{changeMetadata.selectText.2}" ></label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group-inline form-check">
|
<div class="form-group-inline form-check">
|
||||||
<input type="checkbox" class="form-check-input" id="customModeCheckbox">
|
<input type="checkbox" class="form-check-input" id="customModeCheckbox">
|
||||||
<label class="ml-3" for="customModeCheckbox" th:text="#{changeMetadata.selectText.3}"></label>
|
<label class="ml-3" for="customModeCheckbox" th:text="#{changeMetadata.selectText.3}"></label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-check-label" for="author" th:text="#{changeMetadata.author}"></label>
|
<label class="form-check-label" for="author" th:text="#{changeMetadata.author}"></label>
|
||||||
<input type="text" class="form-control" id="author" name="author">
|
<input type="text" class="form-control" id="author" name="author">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-check-label" for="creationDate" th:text="#{changeMetadata.creationDate}"></label>
|
<label class="form-check-label" for="creationDate" th:text="#{changeMetadata.creationDate}"></label>
|
||||||
<input type="text" class="form-control" id="creationDate" name="creationDate" placeholder="2020/12/25 18:30:59">
|
<input type="text" class="form-control" id="creationDate" name="creationDate" placeholder="2020/12/25 18:30:59">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-check-label" for="creator" th:text="#{changeMetadata.creator}"></label>
|
<label class="form-check-label" for="creator" th:text="#{changeMetadata.creator}"></label>
|
||||||
<input type="text" class="form-control" id="creator" name="creator">
|
<input type="text" class="form-control" id="creator" name="creator">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-check-label" for="keywords" th:text="#{changeMetadata.keywords}"></label>
|
<label class="form-check-label" for="keywords" th:text="#{changeMetadata.keywords}"></label>
|
||||||
<input type="text" class="form-control" id="keywords" name="keywords">
|
<input type="text" class="form-control" id="keywords" name="keywords">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-check-label" for="modificationDate" th:text="#{changeMetadata.modDate}"></label>
|
<label class="form-check-label" for="modificationDate" th:text="#{changeMetadata.modDate}"></label>
|
||||||
<input type="text" class="form-control" id="modificationDate" name="modificationDate" placeholder="2020/12/25 18:30:59">
|
<input type="text" class="form-control" id="modificationDate" name="modificationDate" placeholder="2020/12/25 18:30:59">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-check-label" for="producer" th:text="#{changeMetadata.producer}"></label>
|
<label class="form-check-label" for="producer" th:text="#{changeMetadata.producer}"></label>
|
||||||
<input type="text" class="form-control" id="producer" name="producer">
|
<input type="text" class="form-control" id="producer" name="producer">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-check-label" for="subject" th:text="#{changeMetadata.subject}"></label>
|
<label class="form-check-label" for="subject" th:text="#{changeMetadata.subject}"></label>
|
||||||
<input type="text" class="form-control" id="subject" name="subject">
|
<input type="text" class="form-control" id="subject" name="subject">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-check-label" for="title" th:text="#{changeMetadata.title}"></label>
|
<label class="form-check-label" for="title" th:text="#{changeMetadata.title}"></label>
|
||||||
<input type="text" class="form-control" id="title" name="title">
|
<input type="text" class="form-control" id="title" name="title">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-check-label" for="trapped" th:text="#{changeMetadata.trapped}"></label>
|
<label class="form-check-label" for="trapped" th:text="#{changeMetadata.trapped}"></label>
|
||||||
<select class="form-control" id="trapped" name="trapped">
|
<select class="form-control" id="trapped" name="trapped">
|
||||||
<option value="True" th:text="#{true}"></option>
|
<option value="True" th:text="#{true}"></option>
|
||||||
<option value="False" th:text="#{false}" selected></option>
|
<option value="False" th:text="#{false}" selected></option>
|
||||||
<option value="Unknown" th:text="#{unknown}"></option>
|
<option value="Unknown" th:text="#{unknown}"></option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div id="customMetadata" style="display: none;">
|
<div id="customMetadata" style="display: none;">
|
||||||
<h3 th:text="#{changeMetadata.selectText.4}"></h3>
|
<h3 th:text="#{changeMetadata.selectText.4}"></h3>
|
||||||
<div class="form-group" id="otherMetadataEntries"></div>
|
<div class="form-group" id="otherMetadataEntries"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="customMetadataEntries"></div>
|
<div id="customMetadataEntries"></div>
|
||||||
<button type="button" class="btn btn-secondary" id="addMetadataBtn" th:text="#{changeMetadata.selectText.5}"></button>
|
<button type="button" class="btn btn-secondary" id="addMetadataBtn" th:text="#{changeMetadata.selectText.5}"></button>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<button class="btn btn-primary" type="submit" id="submitBtn" th:text="#{changeMetadata.submit}"></button>
|
<button class="btn btn-primary" type="submit" id="submitBtn" th:text="#{changeMetadata.submit}"></button>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
const deleteAllCheckbox = document.querySelector("#deleteAll");
|
const deleteAllCheckbox = document.querySelector("#deleteAll");
|
||||||
const inputs = document.querySelectorAll(".form-control");
|
const inputs = document.querySelectorAll(".form-control");
|
||||||
const customMetadataDiv = document.getElementById('customMetadata');
|
const customMetadataDiv = document.getElementById('customMetadata');
|
||||||
const otherMetadataEntriesDiv = document.getElementById('otherMetadataEntries');
|
const otherMetadataEntriesDiv = document.getElementById('otherMetadataEntries');
|
||||||
|
|
||||||
deleteAllCheckbox.addEventListener("change", function(event) {
|
deleteAllCheckbox.addEventListener("change", function(event) {
|
||||||
if (event.target !== deleteAllCheckbox) {
|
if (event.target !== deleteAllCheckbox) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs.forEach(input => {
|
inputs.forEach(input => {
|
||||||
if (input === deleteAllCheckbox) {
|
if (input === deleteAllCheckbox) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
input.disabled = deleteAllCheckbox.checked;
|
input.disabled = deleteAllCheckbox.checked;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const customModeCheckbox = document.getElementById('customModeCheckbox');
|
const customModeCheckbox = document.getElementById('customModeCheckbox');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const addMetadataBtn = document.getElementById("addMetadataBtn");
|
const addMetadataBtn = document.getElementById("addMetadataBtn");
|
||||||
const customMetadataFormContainer = document.getElementById("customMetadataEntries");
|
const customMetadataFormContainer = document.getElementById("customMetadataEntries");
|
||||||
var count = 1;
|
var count = 1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const fileInput = document.querySelector("#fileInput-input");
|
const fileInput = document.querySelector("#fileInput-input");
|
||||||
const authorInput = document.querySelector("#author");
|
const authorInput = document.querySelector("#author");
|
||||||
const creationDateInput = document.querySelector("#creationDate");
|
const creationDateInput = document.querySelector("#creationDate");
|
||||||
const creatorInput = document.querySelector("#creator");
|
const creatorInput = document.querySelector("#creator");
|
||||||
const keywordsInput = document.querySelector("#keywords");
|
const keywordsInput = document.querySelector("#keywords");
|
||||||
const modificationDateInput = document.querySelector("#modificationDate");
|
const modificationDateInput = document.querySelector("#modificationDate");
|
||||||
const producerInput = document.querySelector("#producer");
|
const producerInput = document.querySelector("#producer");
|
||||||
const subjectInput = document.querySelector("#subject");
|
const subjectInput = document.querySelector("#subject");
|
||||||
const titleInput = document.querySelector("#title");
|
const titleInput = document.querySelector("#title");
|
||||||
const trappedInput = document.querySelector("#trapped");
|
const trappedInput = document.querySelector("#trapped");
|
||||||
|
|
||||||
var lastPDFFileMeta = null;
|
var lastPDFFileMeta = null;
|
||||||
fileInput.addEventListener("change", async function() {
|
fileInput.addEventListener("change", async function() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
while (otherMetadataEntriesDiv.firstChild) {
|
while (otherMetadataEntriesDiv.firstChild) {
|
||||||
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
|
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
|
||||||
}
|
}
|
||||||
while (customMetadataFormContainer.firstChild) {
|
while (customMetadataFormContainer.firstChild) {
|
||||||
customMetadataFormContainer.removeChild(customMetadataFormContainer.firstChild);
|
customMetadataFormContainer.removeChild(customMetadataFormContainer.firstChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const file = this.files[0];
|
const file = this.files[0];
|
||||||
var url = URL.createObjectURL(file)
|
var url = URL.createObjectURL(file)
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js'
|
||||||
const pdf = await pdfjsLib.getDocument(url).promise;
|
const pdf = await pdfjsLib.getDocument(url).promise;
|
||||||
const pdfMetadata = await pdf.getMetadata();
|
const pdfMetadata = await pdf.getMetadata();
|
||||||
lastPDFFile = pdfMetadata?.info
|
lastPDFFile = pdfMetadata?.info
|
||||||
console.log(pdfMetadata);
|
console.log(pdfMetadata);
|
||||||
if(!pdfMetadata?.info?.Custom || pdfMetadata?.info?.Custom.size == 0) {
|
if(!pdfMetadata?.info?.Custom || pdfMetadata?.info?.Custom.size == 0) {
|
||||||
customModeCheckbox.disabled = true;
|
customModeCheckbox.disabled = true;
|
||||||
customModeCheckbox.checked = false;
|
customModeCheckbox.checked = false;
|
||||||
} else {
|
} else {
|
||||||
customModeCheckbox.disabled = false;
|
customModeCheckbox.disabled = false;
|
||||||
}
|
}
|
||||||
authorInput.value = pdfMetadata?.info?.Author;
|
authorInput.value = pdfMetadata?.info?.Author;
|
||||||
creationDateInput.value = convertDateFormat(pdfMetadata?.info?.CreationDate);
|
creationDateInput.value = convertDateFormat(pdfMetadata?.info?.CreationDate);
|
||||||
creatorInput.value = pdfMetadata?.info?.Creator;
|
creatorInput.value = pdfMetadata?.info?.Creator;
|
||||||
keywordsInput.value = pdfMetadata?.info?.Keywords;
|
keywordsInput.value = pdfMetadata?.info?.Keywords;
|
||||||
modificationDateInput.value = convertDateFormat(pdfMetadata?.info?.ModDate);
|
modificationDateInput.value = convertDateFormat(pdfMetadata?.info?.ModDate);
|
||||||
producerInput.value = pdfMetadata?.info?.Producer;
|
producerInput.value = pdfMetadata?.info?.Producer;
|
||||||
subjectInput.value = pdfMetadata?.info?.Subject;
|
subjectInput.value = pdfMetadata?.info?.Subject;
|
||||||
titleInput.value = pdfMetadata?.info?.Title;
|
titleInput.value = pdfMetadata?.info?.Title;
|
||||||
console.log(pdfMetadata?.info);
|
console.log(pdfMetadata?.info);
|
||||||
const trappedValue = pdfMetadata?.info?.Trapped;
|
const trappedValue = pdfMetadata?.info?.Trapped;
|
||||||
// Get all options in the select element
|
// Get all options in the select element
|
||||||
const options = trappedInput.options;
|
const options = trappedInput.options;
|
||||||
// Loop through all options to find the one with a matching value
|
// Loop through all options to find the one with a matching value
|
||||||
for (let i = 0; i < options.length; i++) {
|
for (let i = 0; i < options.length; i++) {
|
||||||
if (options[i].value === trappedValue) {
|
if (options[i].value === trappedValue) {
|
||||||
options[i].selected = true;
|
options[i].selected = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addExtra();
|
addExtra();
|
||||||
});
|
});
|
||||||
|
|
||||||
addMetadataBtn.addEventListener("click", () => {
|
addMetadataBtn.addEventListener("click", () => {
|
||||||
|
|
||||||
const keyInput = document.createElement("input");
|
const keyInput = document.createElement("input");
|
||||||
keyInput.type = "text";
|
keyInput.type = "text";
|
||||||
keyInput.placeholder = 'Key';
|
keyInput.placeholder = 'Key';
|
||||||
keyInput.className = "form-control";
|
keyInput.className = "form-control";
|
||||||
keyInput.name = "customKey" + count;
|
keyInput.name = "customKey" + count;
|
||||||
|
|
||||||
const valueInput = document.createElement("input");
|
const valueInput = document.createElement("input");
|
||||||
valueInput.type = "text";
|
valueInput.type = "text";
|
||||||
valueInput.placeholder = 'Value';
|
valueInput.placeholder = 'Value';
|
||||||
valueInput.className = "form-control";
|
valueInput.className = "form-control";
|
||||||
valueInput.name = "customValue" + count;
|
valueInput.name = "customValue" + count;
|
||||||
count = count + 1;
|
count = count + 1;
|
||||||
|
|
||||||
const formGroup = document.createElement("div");
|
const formGroup = document.createElement("div");
|
||||||
formGroup.className = "form-group";
|
formGroup.className = "form-group";
|
||||||
formGroup.appendChild(keyInput);
|
formGroup.appendChild(keyInput);
|
||||||
formGroup.appendChild(valueInput);
|
formGroup.appendChild(valueInput);
|
||||||
|
|
||||||
|
|
||||||
customMetadataFormContainer.appendChild(formGroup);
|
customMetadataFormContainer.appendChild(formGroup);
|
||||||
});
|
});
|
||||||
function convertDateFormat(dateTimeString) {
|
function convertDateFormat(dateTimeString) {
|
||||||
if (!dateTimeString || dateTimeString.length < 17) {
|
if (!dateTimeString || dateTimeString.length < 17) {
|
||||||
return dateTimeString;
|
return dateTimeString;
|
||||||
}
|
}
|
||||||
|
|
||||||
const year = dateTimeString.substring(2, 6);
|
const year = dateTimeString.substring(2, 6);
|
||||||
const month = dateTimeString.substring(6, 8);
|
const month = dateTimeString.substring(6, 8);
|
||||||
const day = dateTimeString.substring(8, 10);
|
const day = dateTimeString.substring(8, 10);
|
||||||
const hour = dateTimeString.substring(10, 12);
|
const hour = dateTimeString.substring(10, 12);
|
||||||
const minute = dateTimeString.substring(12, 14);
|
const minute = dateTimeString.substring(12, 14);
|
||||||
const second = dateTimeString.substring(14, 16);
|
const second = dateTimeString.substring(14, 16);
|
||||||
|
|
||||||
return year + "/" + month + "/" + day + " " + hour + ":" + minute + ":" + second;
|
return year + "/" + month + "/" + day + " " + hour + ":" + minute + ":" + second;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addExtra() {
|
function addExtra() {
|
||||||
const event = document.getElementById("customModeCheckbox");
|
const event = document.getElementById("customModeCheckbox");
|
||||||
|
|
||||||
|
|
||||||
if (event.checked && lastPDFFile.Custom != null) {
|
if (event.checked && lastPDFFile.Custom != null) {
|
||||||
customMetadataDiv.style.display = 'block';
|
customMetadataDiv.style.display = 'block';
|
||||||
for (const [key, value] of Object.entries(lastPDFFile.Custom)) {
|
for (const [key, value] of Object.entries(lastPDFFile.Custom)) {
|
||||||
if (key === 'Author' || key === 'CreationDate' || key === 'Creator' || key === 'Keywords' || key === 'ModDate' || key === 'Producer' || key === 'Subject' || key === 'Title' || key === 'Trapped') {
|
if (key === 'Author' || key === 'CreationDate' || key === 'Creator' || key === 'Keywords' || key === 'ModDate' || key === 'Producer' || key === 'Subject' || key === 'Title' || key === 'Trapped') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const entryDiv = document.createElement('div');
|
const entryDiv = document.createElement('div');
|
||||||
entryDiv.className = 'form-group';
|
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>`;
|
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);
|
otherMetadataEntriesDiv.appendChild(entryDiv);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
customMetadataDiv.style.display = 'none';
|
customMetadataDiv.style.display = 'none';
|
||||||
while (otherMetadataEntriesDiv.firstChild) {
|
while (otherMetadataEntriesDiv.firstChild) {
|
||||||
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
|
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
customModeCheckbox.addEventListener('change', (event) => {
|
customModeCheckbox.addEventListener('change', (event) => {
|
||||||
|
|
||||||
addExtra();
|
addExtra();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,190 +1,190 @@
|
|||||||
<!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=#{compare.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{compare.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-9">
|
<div class="col-md-9">
|
||||||
<h2 th:text="#{compare.header}"></h2>
|
<h2 th:text="#{compare.header}"></h2>
|
||||||
|
|
||||||
<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>
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput2', multiple=false, accept='application/pdf')}"></div>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput2', multiple=false, accept='application/pdf')}"></div>
|
||||||
<button class="btn btn-primary" onclick="comparePDFs()" th:text="#{compare.submit}"></button>
|
<button class="btn btn-primary" onclick="comparePDFs()" th:text="#{compare.submit}"></button>
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h3 th:text="#{compare.document.1}"></h3>
|
<h3 th:text="#{compare.document.1}"></h3>
|
||||||
<div id="result1" class="result-column"></div>
|
<div id="result1" class="result-column"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h3 th:text="#{compare.document.2}"></h3>
|
<h3 th:text="#{compare.document.2}"></h3>
|
||||||
<div id="result2" class="result-column"></div>
|
<div id="result2" class="result-column"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<style>
|
<style>
|
||||||
.result-column {
|
.result-column {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
height: calc(100vh - 400px);
|
height: calc(100vh - 400px);
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
// get the elements
|
// get the elements
|
||||||
var result1 = document.getElementById('result1');
|
var result1 = document.getElementById('result1');
|
||||||
var result2 = document.getElementById('result2');
|
var result2 = document.getElementById('result2');
|
||||||
|
|
||||||
// add event listeners
|
// add event listeners
|
||||||
result1.addEventListener('scroll', function() {
|
result1.addEventListener('scroll', function() {
|
||||||
result2.scrollTop = result1.scrollTop;
|
result2.scrollTop = result1.scrollTop;
|
||||||
});
|
});
|
||||||
|
|
||||||
result2.addEventListener('scroll', function() {
|
result2.addEventListener('scroll', function() {
|
||||||
result1.scrollTop = result2.scrollTop;
|
result1.scrollTop = result2.scrollTop;
|
||||||
});
|
});
|
||||||
|
|
||||||
async function comparePDFs() {
|
async function comparePDFs() {
|
||||||
const file1 = document.getElementById("fileInput-input").files[0];
|
const file1 = document.getElementById("fileInput-input").files[0];
|
||||||
const file2 = document.getElementById("fileInput2-input").files[0];
|
const file2 = document.getElementById("fileInput2-input").files[0];
|
||||||
|
|
||||||
if (!file1 || !file2) {
|
if (!file1 || !file2) {
|
||||||
console.error("Please select two PDF files to compare");
|
console.error("Please select two PDF files to compare");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js'
|
||||||
const [pdf1, pdf2] = await Promise.all([
|
const [pdf1, pdf2] = await Promise.all([
|
||||||
pdfjsLib.getDocument(URL.createObjectURL(file1)).promise,
|
pdfjsLib.getDocument(URL.createObjectURL(file1)).promise,
|
||||||
pdfjsLib.getDocument(URL.createObjectURL(file2)).promise
|
pdfjsLib.getDocument(URL.createObjectURL(file2)).promise
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const extractText = async (pdf) => {
|
const extractText = async (pdf) => {
|
||||||
const pages = [];
|
const pages = [];
|
||||||
for (let i = 1; i <= pdf.numPages; i++) {
|
for (let i = 1; i <= pdf.numPages; i++) {
|
||||||
const page = await pdf.getPage(i);
|
const page = await pdf.getPage(i);
|
||||||
const content = await page.getTextContent();
|
const content = await page.getTextContent();
|
||||||
const strings = content.items.map(item => item.str);
|
const strings = content.items.map(item => item.str);
|
||||||
pages.push(strings.join(" "));
|
pages.push(strings.join(" "));
|
||||||
}
|
}
|
||||||
return pages.join(" ");
|
return pages.join(" ");
|
||||||
};
|
};
|
||||||
|
|
||||||
const [text1, text2] = await Promise.all([
|
const [text1, text2] = await Promise.all([
|
||||||
extractText(pdf1),
|
extractText(pdf1),
|
||||||
extractText(pdf2)
|
extractText(pdf2)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (text1.trim() === "" || text2.trim() === "") {
|
if (text1.trim() === "" || text2.trim() === "") {
|
||||||
alert("One or both of the selected PDFs have no text content. Please choose PDFs with text for comparison.");
|
alert("One or both of the selected PDFs have no text content. Please choose PDFs with text for comparison.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const diff = (text1, text2) => {
|
const diff = (text1, text2) => {
|
||||||
const words1 = text1.split(' ');
|
const words1 = text1.split(' ');
|
||||||
const words2 = text2.split(' ');
|
const words2 = text2.split(' ');
|
||||||
|
|
||||||
// Create a 2D array to hold our "matrix"
|
// Create a 2D array to hold our "matrix"
|
||||||
const matrix = Array(words1.length + 1).fill(null).map(() => Array(words2.length + 1).fill(0));
|
const matrix = Array(words1.length + 1).fill(null).map(() => Array(words2.length + 1).fill(0));
|
||||||
|
|
||||||
// Perform standard LCS algorithm
|
// Perform standard LCS algorithm
|
||||||
for (let i = 1; i <= words1.length; i++) {
|
for (let i = 1; i <= words1.length; i++) {
|
||||||
for (let j = 1; j <= words2.length; j++) {
|
for (let j = 1; j <= words2.length; j++) {
|
||||||
if (words1[i - 1] === words2[j - 1]) {
|
if (words1[i - 1] === words2[j - 1]) {
|
||||||
matrix[i][j] = matrix[i - 1][j - 1] + 1;
|
matrix[i][j] = matrix[i - 1][j - 1] + 1;
|
||||||
} else {
|
} else {
|
||||||
matrix[i][j] = Math.max(matrix[i][j - 1], matrix[i - 1][j]);
|
matrix[i][j] = Math.max(matrix[i][j - 1], matrix[i - 1][j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let i = words1.length;
|
let i = words1.length;
|
||||||
let j = words2.length;
|
let j = words2.length;
|
||||||
const differences = [];
|
const differences = [];
|
||||||
|
|
||||||
// Backtrack through the matrix to create the diff
|
// Backtrack through the matrix to create the diff
|
||||||
while (i > 0 || j > 0) {
|
while (i > 0 || j > 0) {
|
||||||
if (i > 0 && j > 0 && words1[i - 1] === words2[j - 1]) {
|
if (i > 0 && j > 0 && words1[i - 1] === words2[j - 1]) {
|
||||||
differences.unshift(['black', words1[i - 1]]);
|
differences.unshift(['black', words1[i - 1]]);
|
||||||
i--;
|
i--;
|
||||||
j--;
|
j--;
|
||||||
} else if (j > 0 && (i === 0 || matrix[i][j - 1] >= matrix[i - 1][j])) {
|
} else if (j > 0 && (i === 0 || matrix[i][j - 1] >= matrix[i - 1][j])) {
|
||||||
differences.unshift(['green', words2[j - 1]]);
|
differences.unshift(['green', words2[j - 1]]);
|
||||||
j--;
|
j--;
|
||||||
} else if (i > 0 && (j === 0 || matrix[i][j - 1] < matrix[i - 1][j])) {
|
} else if (i > 0 && (j === 0 || matrix[i][j - 1] < matrix[i - 1][j])) {
|
||||||
differences.unshift(['red', words1[i - 1]]);
|
differences.unshift(['red', words1[i - 1]]);
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return differences;
|
return differences;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const differences = diff(text1, text2);
|
const differences = diff(text1, text2);
|
||||||
|
|
||||||
const displayDifferences = (differences) => {
|
const displayDifferences = (differences) => {
|
||||||
const resultDiv1 = document.getElementById("result1");
|
const resultDiv1 = document.getElementById("result1");
|
||||||
const resultDiv2 = document.getElementById("result2");
|
const resultDiv2 = document.getElementById("result2");
|
||||||
resultDiv1.innerHTML = "";
|
resultDiv1.innerHTML = "";
|
||||||
resultDiv2.innerHTML = "";
|
resultDiv2.innerHTML = "";
|
||||||
|
|
||||||
differences.forEach(([color, word]) => {
|
differences.forEach(([color, word]) => {
|
||||||
const span1 = document.createElement("span");
|
const span1 = document.createElement("span");
|
||||||
const span2 = document.createElement("span");
|
const span2 = document.createElement("span");
|
||||||
|
|
||||||
// If it's an addition, show it in green in the second document and transparent in the first
|
// If it's an addition, show it in green in the second document and transparent in the first
|
||||||
if (color === "green") {
|
if (color === "green") {
|
||||||
span1.style.color = "transparent";
|
span1.style.color = "transparent";
|
||||||
span1.style.userSelect = "none";
|
span1.style.userSelect = "none";
|
||||||
span2.style.color = color;
|
span2.style.color = color;
|
||||||
}
|
}
|
||||||
// If it's a deletion, show it in red in the first document and transparent in the second
|
// If it's a deletion, show it in red in the first document and transparent in the second
|
||||||
else if (color === "red") {
|
else if (color === "red") {
|
||||||
span1.style.color = color;
|
span1.style.color = color;
|
||||||
span2.style.color = "transparent";
|
span2.style.color = "transparent";
|
||||||
span2.style.userSelect = "none";
|
span2.style.userSelect = "none";
|
||||||
}
|
}
|
||||||
// If it's unchanged, show it in black in both
|
// If it's unchanged, show it in black in both
|
||||||
else {
|
else {
|
||||||
span1.style.color = color;
|
span1.style.color = color;
|
||||||
span2.style.color = color;
|
span2.style.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
span1.textContent = word;
|
span1.textContent = word;
|
||||||
span2.textContent = word;
|
span2.textContent = word;
|
||||||
resultDiv1.appendChild(span1);
|
resultDiv1.appendChild(span1);
|
||||||
resultDiv2.appendChild(span2);
|
resultDiv2.appendChild(span2);
|
||||||
|
|
||||||
// Add space after each word, or a new line if the word ends with a full stop
|
// Add space after each word, or a new line if the word ends with a full stop
|
||||||
const spaceOrNewline1 = document.createElement("span");
|
const spaceOrNewline1 = document.createElement("span");
|
||||||
const spaceOrNewline2 = document.createElement("span");
|
const spaceOrNewline2 = document.createElement("span");
|
||||||
if (word.endsWith(".")) {
|
if (word.endsWith(".")) {
|
||||||
spaceOrNewline1.innerHTML = "<br>";
|
spaceOrNewline1.innerHTML = "<br>";
|
||||||
spaceOrNewline2.innerHTML = "<br>";
|
spaceOrNewline2.innerHTML = "<br>";
|
||||||
} else {
|
} else {
|
||||||
spaceOrNewline1.textContent = " ";
|
spaceOrNewline1.textContent = " ";
|
||||||
spaceOrNewline2.textContent = " ";
|
spaceOrNewline2.textContent = " ";
|
||||||
}
|
}
|
||||||
resultDiv1.appendChild(spaceOrNewline1);
|
resultDiv1.appendChild(spaceOrNewline1);
|
||||||
resultDiv2.appendChild(spaceOrNewline2);
|
resultDiv2.appendChild(spaceOrNewline2);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('Differences:', differences);
|
console.log('Differences:', differences);
|
||||||
displayDifferences(differences);
|
displayDifferences(differences);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</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>
|
@ -1,132 +1,132 @@
|
|||||||
<!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=#{rotate.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{rotate.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="#{rotate.header}"></h2>
|
<h2 th:text="#{rotate.header}"></h2>
|
||||||
|
|
||||||
<form action="#" th:action="@{rotate-pdf}" th:object="${rotateForm}" method="post" enctype="multipart/form-data">
|
<form action="#" th:action="@{rotate-pdf}" th:object="${rotateForm}" method="post" enctype="multipart/form-data">
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
|
||||||
<input type="hidden" id="angleInput" name="angle" value="0">
|
<input type="hidden" id="angleInput" name="angle" value="0">
|
||||||
|
|
||||||
<div id="editSection" style="display: none">
|
<div id="editSection" style="display: none">
|
||||||
<div class="previewContainer">
|
<div class="previewContainer">
|
||||||
<img id="pdf-preview" />
|
<img id="pdf-preview" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="buttonContainer">
|
<div class="buttonContainer">
|
||||||
<button type="button" class="btn btn-secondary" onclick="rotate(-90)">
|
<button type="button" class="btn btn-secondary" onclick="rotate(-90)">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
|
||||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z" />
|
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z" />
|
||||||
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z" />
|
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{rotate.submit}"></button>
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{rotate.submit}"></button>
|
||||||
<button type="button" class="btn btn-secondary" onclick="rotate(90)">
|
<button type="button" class="btn btn-secondary" onclick="rotate(90)">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
|
||||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z" />
|
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z" />
|
||||||
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" />
|
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const angleInput = document.getElementById("angleInput");
|
const angleInput = document.getElementById("angleInput");
|
||||||
const fileInput = document.getElementById("fileInput-input");
|
const fileInput = document.getElementById("fileInput-input");
|
||||||
const preview = document.getElementById("pdf-preview");
|
const preview = document.getElementById("pdf-preview");
|
||||||
fileInput.addEventListener("change", async function() {
|
fileInput.addEventListener("change", async function() {
|
||||||
console.log("loading pdf");
|
console.log("loading pdf");
|
||||||
|
|
||||||
document.querySelector("#editSection").style.display = "";
|
document.querySelector("#editSection").style.display = "";
|
||||||
|
|
||||||
var url = URL.createObjectURL(fileInput.files[0])
|
var url = URL.createObjectURL(fileInput.files[0])
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js'
|
||||||
const pdf = await pdfjsLib.getDocument(url).promise;
|
const pdf = await pdfjsLib.getDocument(url).promise;
|
||||||
const page = await pdf.getPage(1);
|
const page = await pdf.getPage(1);
|
||||||
|
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
|
|
||||||
// set the canvas size to the size of the page
|
// set the canvas size to the size of the page
|
||||||
if (page.rotate == 90 || page.rotate == 270) {
|
if (page.rotate == 90 || page.rotate == 270) {
|
||||||
canvas.width = page.view[3];
|
canvas.width = page.view[3];
|
||||||
canvas.height = page.view[2];
|
canvas.height = page.view[2];
|
||||||
} else {
|
} else {
|
||||||
canvas.width = page.view[2];
|
canvas.width = page.view[2];
|
||||||
canvas.height = page.view[3];
|
canvas.height = page.view[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
// render the page onto the canvas
|
// render the page onto the canvas
|
||||||
var renderContext = {
|
var renderContext = {
|
||||||
canvasContext: canvas.getContext("2d"),
|
canvasContext: canvas.getContext("2d"),
|
||||||
viewport: page.getViewport({ scale: 1 })
|
viewport: page.getViewport({ scale: 1 })
|
||||||
};
|
};
|
||||||
|
|
||||||
await page.render(renderContext).promise;
|
await page.render(renderContext).promise;
|
||||||
preview.src = canvas.toDataURL();
|
preview.src = canvas.toDataURL();
|
||||||
});
|
});
|
||||||
|
|
||||||
function rotate(deg) {
|
function rotate(deg) {
|
||||||
var lastTransform = preview.style.rotate;
|
var lastTransform = preview.style.rotate;
|
||||||
if (!lastTransform) {
|
if (!lastTransform) {
|
||||||
lastTransform = "0";
|
lastTransform = "0";
|
||||||
}
|
}
|
||||||
const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ''));
|
const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ''));
|
||||||
const newAngle = lastAngle + deg;
|
const newAngle = lastAngle + deg;
|
||||||
|
|
||||||
preview.style.rotate = newAngle + "deg";
|
preview.style.rotate = newAngle + "deg";
|
||||||
angleInput.value = newAngle;
|
angleInput.value = newAngle;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
#pdf-preview {
|
#pdf-preview {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
display: block;
|
display: block;
|
||||||
max-width: calc(100% - 30px);
|
max-width: calc(100% - 30px);
|
||||||
max-height: calc(100% - 30px);
|
max-height: calc(100% - 30px);
|
||||||
box-shadow: 0 0 4px rgba(100, 100, 100, .25);
|
box-shadow: 0 0 4px rgba(100, 100, 100, .25);
|
||||||
transition: rotate .3s;
|
transition: rotate .3s;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
translate: -50% -50%;
|
translate: -50% -50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.previewContainer {
|
.previewContainer {
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid rgba(0, 0, 0, .125);
|
border: 1px solid rgba(0, 0, 0, .125);
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
display: block;
|
display: block;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttonContainer {
|
.buttonContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -47,6 +47,7 @@ select#font-select, select#font-select option {
|
|||||||
if (file) {
|
if (file) {
|
||||||
originalFileName = file.name.replace(/\.[^/.]+$/, "");
|
originalFileName = file.name.replace(/\.[^/.]+$/, "");
|
||||||
const pdfData = await file.arrayBuffer();
|
const pdfData = await file.arrayBuffer();
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js'
|
||||||
const pdfDoc = await pdfjsLib.getDocument({ data: pdfData }).promise;
|
const pdfDoc = await pdfjsLib.getDocument({ data: pdfData }).promise;
|
||||||
await DraggableUtils.renderPage(pdfDoc, 0);
|
await DraggableUtils.renderPage(pdfDoc, 0);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user