Compare commits

...

5 Commits

Author SHA1 Message Date
Dimitris Kaitantzidis
67de8a9460 Fix canvas pdf to csv (#2228)
* WIP: fixes canvas and rect to crop - small problem in smaller screens - neew to fix re render page on resize

* Closes #2209

* Closes #2227
2024-11-16 11:02:20 +00:00
Anthony Stirling
b26aa3417e Update build.gradle 2024-11-16 11:01:44 +00:00
Renan
8dfb5940ca Fixing bug: Add Image makes random changes to image (#2246) (#2256)
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2024-11-16 08:55:40 +00:00
Anthony Stirling
0ce479e1e3 Update push-docker.yml 2024-11-16 08:43:42 +00:00
Anthony Stirling
cca3b6b525 Update multi-tool.html 2024-11-16 08:40:34 +00:00
5 changed files with 146 additions and 77 deletions

View File

@@ -67,6 +67,7 @@ jobs:
images: | images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf
${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf ${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf
tags: | tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }} type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }}
@@ -95,6 +96,7 @@ jobs:
images: | images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf
${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf ${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf
tags: | tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }} type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
@@ -122,6 +124,7 @@ jobs:
images: | images: |
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf
${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf ${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf
tags: | tags: |
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat,enable=${{ github.ref == 'refs/heads/master' }} type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat,enable=${{ github.ref == 'refs/heads/master' }}

View File

@@ -22,7 +22,7 @@ ext {
} }
group = "stirling.software" group = "stirling.software"
version = "0.33.0" version = "0.33.1"
java { java {
// 17 is lowest but we support and recommend 21 // 17 is lowest but we support and recommend 21

View File

@@ -9,81 +9,81 @@ const DraggableUtils = {
init() { init() {
interact(".draggable-canvas") interact(".draggable-canvas")
.draggable({ .draggable({
listeners: { listeners: {
move: (event) => { move: (event) => {
const target = event.target; const target = event.target;
const x = (parseFloat(target.getAttribute("data-bs-x")) || 0) const x = (parseFloat(target.getAttribute("data-bs-x")) || 0)
+ event.dx; + event.dx;
const y = (parseFloat(target.getAttribute("data-bs-y")) || 0) const y = (parseFloat(target.getAttribute("data-bs-y")) || 0)
+ event.dy; + event.dy;
target.style.transform = `translate(${x}px, ${y}px)`; target.style.transform = `translate(${x}px, ${y}px)`;
target.setAttribute("data-bs-x", x); target.setAttribute("data-bs-x", x);
target.setAttribute("data-bs-y", y); target.setAttribute("data-bs-y", y);
this.onInteraction(target); this.onInteraction(target);
//update the last interacted element //update the last interacted element
this.lastInteracted = event.target; this.lastInteracted = event.target;
},
}, },
}, })
}) .resizable({
.resizable({ edges: { left: true, right: true, bottom: true, top: true },
edges: { left: true, right: true, bottom: true, top: true }, listeners: {
listeners: { move: (event) => {
move: (event) => { var target = event.target;
var target = event.target; var x = parseFloat(target.getAttribute("data-bs-x")) || 0;
var x = parseFloat(target.getAttribute("data-bs-x")) || 0; var y = parseFloat(target.getAttribute("data-bs-y")) || 0;
var y = parseFloat(target.getAttribute("data-bs-y")) || 0;
// check if control key is pressed // check if control key is pressed
if (event.ctrlKey) { if (event.ctrlKey) {
const aspectRatio = target.offsetWidth / target.offsetHeight; const aspectRatio = target.offsetWidth / target.offsetHeight;
// preserve aspect ratio // preserve aspect ratio
let width = event.rect.width; let width = event.rect.width;
let height = event.rect.height; let height = event.rect.height;
if (Math.abs(event.deltaRect.width) >= Math.abs( if (Math.abs(event.deltaRect.width) >= Math.abs(
event.deltaRect.height)) { event.deltaRect.height)) {
height = width / aspectRatio; height = width / aspectRatio;
} else { } else {
width = height * aspectRatio; width = height * aspectRatio;
}
event.rect.width = width;
event.rect.height = height;
} }
event.rect.width = width; target.style.width = event.rect.width + "px";
event.rect.height = height; target.style.height = event.rect.height + "px";
}
target.style.width = event.rect.width + "px"; // translate when resizing from top or left edges
target.style.height = event.rect.height + "px"; x += event.deltaRect.left;
y += event.deltaRect.top;
// translate when resizing from top or left edges target.style.transform = "translate(" + x + "px," + y + "px)";
x += event.deltaRect.left;
y += event.deltaRect.top;
target.style.transform = "translate(" + x + "px," + y + "px)"; target.setAttribute("data-bs-x", x);
target.setAttribute("data-bs-y", y);
target.setAttribute("data-bs-x", x); target.textContent = Math.round(event.rect.width) + "\u00D7"
target.setAttribute("data-bs-y", y);
target.textContent = Math.round(event.rect.width) + "\u00D7"
+ Math.round(event.rect.height); + Math.round(event.rect.height);
this.onInteraction(target); this.onInteraction(target);
},
}, },
},
modifiers: [ modifiers: [
interact.modifiers.restrictSize({ interact.modifiers.restrictSize({
min: {width: 5, height: 5}, min: { width: 5, height: 5 },
}), }),
], ],
inertia: true, inertia: true,
}); });
//Arrow key Support for Add-Image and Sign pages //Arrow key Support for Add-Image and Sign pages
if(window.location.pathname.endsWith('sign') || window.location.pathname.endsWith('add-image')) { if (window.location.pathname.endsWith('sign') || window.location.pathname.endsWith('add-image')) {
window.addEventListener('keydown', (event) => { window.addEventListener('keydown', (event) => {
//Check for last interacted element //Check for last interacted element
if (!this.lastInteracted){ if (!this.lastInteracted) {
return; return;
} }
// Get the currently selected element // Get the currently selected element
@@ -288,7 +288,7 @@ const DraggableUtils = {
} }
}, },
parseTransform(element) {}, parseTransform(element) { },
async getOverlayedPdfDocument() { async getOverlayedPdfDocument() {
const pdfBytes = await this.pdfDoc.getData(); const pdfBytes = await this.pdfDoc.getData();
const pdfDocModified = await PDFLib.PDFDocument.load(pdfBytes, { const pdfDocModified = await PDFLib.PDFDocument.load(pdfBytes, {
@@ -308,6 +308,7 @@ const DraggableUtils = {
const offsetWidth = pagesMap[pageIdx + "-offsetWidth"]; const offsetWidth = pagesMap[pageIdx + "-offsetWidth"];
const offsetHeight = pagesMap[pageIdx + "-offsetHeight"]; const offsetHeight = pagesMap[pageIdx + "-offsetHeight"];
for (const draggableData of draggablesData) { for (const draggableData of draggablesData) {
// embed the draggable canvas // embed the draggable canvas
const draggableElement = draggableData.element; const draggableElement = draggableData.element;
@@ -324,6 +325,24 @@ const DraggableUtils = {
width: draggableData.offsetWidth, width: draggableData.offsetWidth,
height: draggableData.offsetHeight, height: draggableData.offsetHeight,
}; };
//Auxiliary variables
let widthAdjusted = page.getWidth();
let heightAdjusted = page.getHeight();
const rotation = page.getRotation();
//Normalizing angle
let normalizedAngle = rotation.angle % 360;
if (normalizedAngle < 0) {
normalizedAngle += 360;
}
//Changing the page dimension if the angle is 90 or 270
if (normalizedAngle === 90 || normalizedAngle === 270) {
let widthTemp = widthAdjusted;
widthAdjusted = heightAdjusted;
heightAdjusted = widthTemp;
}
const draggablePositionRelative = { const draggablePositionRelative = {
x: draggablePositionPixels.x / offsetWidth, x: draggablePositionPixels.x / offsetWidth,
y: draggablePositionPixels.y / offsetHeight, y: draggablePositionPixels.y / offsetHeight,
@@ -331,18 +350,36 @@ const DraggableUtils = {
height: draggablePositionPixels.height / offsetHeight, height: draggablePositionPixels.height / offsetHeight,
}; };
const draggablePositionPdf = { const draggablePositionPdf = {
x: draggablePositionRelative.x * page.getWidth(), x: draggablePositionRelative.x * widthAdjusted,
y: draggablePositionRelative.y * page.getHeight(), y: draggablePositionRelative.y * heightAdjusted,
width: draggablePositionRelative.width * page.getWidth(), width: draggablePositionRelative.width * widthAdjusted,
height: draggablePositionRelative.height * page.getHeight(), height: draggablePositionRelative.height * heightAdjusted,
}; };
//Defining the image if the page has a 0-degree angle
let x = draggablePositionPdf.x
let y = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height
//Defining the image position if it is at other angles
if (normalizedAngle === 90) {
x = draggablePositionPdf.y + draggablePositionPdf.height;
y = draggablePositionPdf.x;
} else if (normalizedAngle === 180) {
x = widthAdjusted - draggablePositionPdf.x;
y = draggablePositionPdf.y + draggablePositionPdf.height;
} else if (normalizedAngle === 270) {
x = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height;
y = widthAdjusted - draggablePositionPdf.x;
}
// draw the image // draw the image
page.drawImage(pdfImageObject, { page.drawImage(pdfImageObject, {
x: draggablePositionPdf.x, x: x,
y: page.getHeight() - draggablePositionPdf.y - draggablePositionPdf.height, y: y,
width: draggablePositionPdf.width, width: draggablePositionPdf.width,
height: draggablePositionPdf.height, height: draggablePositionPdf.height,
rotate: rotation
}); });
} }
} }

View File

@@ -23,23 +23,26 @@
</form> </form>
<p id="instruction-text" style="margin: 0; display: none" th:text="#{PDFToCSV.prompt}"></p> <p id="instruction-text" style="margin: 0; display: none" th:text="#{PDFToCSV.prompt}"></p>
<div style="position: relative; display: inline-block;"> <div style="position: relative; width: auto;" id="canvasesContainer">
<div> <div>
<div style="display:none ;margin: 3px;position: absolute;top: 0;width: 120px;justify-content:space-between;z-index: 10" id="pagination-button-container"> <div style="display:none ;margin: 3px;position: absolute;top: 0;width: 120px;justify-content:space-between;z-index: 10" id="pagination-button-container">
<button id='previous-page-btn' style='opacity: 80% ; width: 50px; height: 30px; display: flex;align-items: center;justify-content: center; background: grey; color: #ffffff; ;border: none;outline: none; border-radius: 4px;'> &lt; </button> <button id='previous-page-btn' style='opacity: 80% ; width: 50px; height: 30px; display: flex;align-items: center;justify-content: center; background: grey; color: #ffffff; ;border: none;outline: none; border-radius: 4px;'> &lt; </button>
<button id='next-page-btn' style='opacity: 80% ; width: 50px; height: 30px; display: flex;align-items: center;justify-content: center; background: grey; color: #ffffff; ;border: none;outline: none; border-radius: 4px;'> &gt; </button> <button id='next-page-btn' style='opacity: 80% ; width: 50px; height: 30px; display: flex;align-items: center;justify-content: center; background: grey; color: #ffffff; ;border: none;outline: none; border-radius: 4px;'> &gt; </button>
</div> </div>
<canvas id="crop-pdf-canvas" style="position: absolute; top: 0; left: 0; z-index: 1;"></canvas> <canvas id="cropPdfCanvas" style="width: 100%"></canvas>
</div> </div>
<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; width: 100%"></canvas>
</div> </div>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script> <script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script> <script>
let pdfCanvas = document.getElementById('crop-pdf-canvas'); let pdfCanvas = document.getElementById('cropPdfCanvas');
let overlayCanvas = document.getElementById('overlayCanvas'); let overlayCanvas = document.getElementById('overlayCanvas');
let canvasesContainer = document.getElementById('canvasesContainer');
canvasesContainer.style.display = "none";
// let paginationBtnContainer = ; // let paginationBtnContainer = ;
let context = pdfCanvas.getContext('2d'); let context = pdfCanvas.getContext('2d');
let overlayContext = overlayCanvas.getContext('2d');
let btn1Object = document.getElementById('previous-page-btn'); let btn1Object = document.getElementById('previous-page-btn');
let btn2Object = document.getElementById('next-page-btn'); let btn2Object = document.getElementById('next-page-btn');
@@ -60,6 +63,8 @@
let rectWidth = 0; let rectWidth = 0;
let rectHeight = 0; let rectHeight = 0;
let timeId = null; // timeout id for resizing canvases event
btn1Object.addEventListener('click',function (e){ btn1Object.addEventListener('click',function (e){
if (currentPage !== 1) { if (currentPage !== 1) {
currentPage = currentPage - 1; currentPage = currentPage - 1;
@@ -102,14 +107,13 @@
} }
}); });
fileInput.addEventListener('change', function(e) { function renderPageFromFile(file) {
file = e.target.files[0];
if (file.type === 'application/pdf') { if (file.type === 'application/pdf') {
let reader = new FileReader(); let reader = new FileReader();
reader.onload = function(ev) { reader.onload = function (ev) {
let typedArray = new Uint8Array(reader.result); let typedArray = new Uint8Array(reader.result);
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs'; pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
pdfjsLib.getDocument(typedArray).promise.then(function(pdf) { pdfjsLib.getDocument(typedArray).promise.then(function (pdf) {
pdfDoc = pdf; pdfDoc = pdf;
totalPages = pdf.numPages; totalPages = pdf.numPages;
renderPage(currentPage); renderPage(currentPage);
@@ -117,9 +121,37 @@
pageId.value = currentPage; pageId.value = currentPage;
}; };
reader.readAsArrayBuffer(file); reader.readAsArrayBuffer(file);
document.getElementById("pagination-button-container").style.display="flex"; document.getElementById("pagination-button-container").style.display = "flex";
document.getElementById("instruction-text").style.display="block"; document.getElementById("instruction-text").style.display = "block";
} }
}
window.addEventListener("resize", function() {
clearTimeout(timeId);
timeId = setTimeout(function () {
if (fileInput.files.length == 0) return;
let canvasesContainer = document.getElementById('canvasesContainer');
let containerRect = canvasesContainer.getBoundingClientRect();
context.clearRect(0, 0, pdfCanvas.width, pdfCanvas.height);
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
pdfCanvas.width = containerRect.width;
pdfCanvas.height = containerRect.height;
overlayCanvas.width = containerRect.width;
overlayCanvas.height = containerRect.height;
let file = fileInput.files[0];
renderPageFromFile(file);
}, 1000);
});
fileInput.addEventListener('change', function(e) {
canvasesContainer.style.display = "block"; // set for visual purposes
file = e.target.files[0];
renderPageFromFile(file);
}); });
function renderPage(pageNumber) { function renderPage(pageNumber) {

View File

@@ -83,9 +83,6 @@
</span> </span>
</button> </button>
</div> </div>
<div class="mb-3">
<button type="button" id="resetFileInputBtn" class="btn btn-danger" onclick="removeAllElements()" th:text="#{reset}">Reset</button>
</div>
<div id="selected-pages-display" class="selected-pages-container hidden"> <div id="selected-pages-display" class="selected-pages-container hidden">
<div style="display:flex; height:3rem; margin-right:1rem"> <div style="display:flex; height:3rem; margin-right:1rem">
<h5 th:text="#{multiTool.selectedPages}" style="white-space: nowrap; margin-right: 1rem;">Selected <h5 th:text="#{multiTool.selectedPages}" style="white-space: nowrap; margin-right: 1rem;">Selected