I am creating a service to stamp a pdf with an image using PDF.js and jQuery. I managed to create a draggable object using PDF.js but the the object leaves a trail of past objects when it is dragged.
I used context.clearRect(0, 0, canvas.width, canvas.height);
to clear the past objects but it also clears the underlying PDF in the canvas.
How can I drag this object without affecting the underlying PDF?
Here is what I have done so far.
I am loading the PDF to the canvas using following code.
function loadPdfPreview(base64pdf){
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.0.943/pdf.worker.min.js';
var loadingTask = pdfjsLib.getDocument({data: base64pdf});
loadingTask.promise.then(function (pdf) {
// Fetch the first page
pdf.getPage(1).then(function (page) {
var scale = 1.0;
var viewport = page.getViewport(scale);
// Prepare canvas using PDF page dimensions
canvas = document.getElementById('pdf-canvas');
context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
canvasOffset = $("#pdf-canvas").offset();
offsetX = canvasOffset.left;
offsetY = canvasOffset.top;
// Render PDF page into canvas context
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext).then(function () {
// creating the dummy object on success
drawObjectFromBlueprint(blueprint);
}, function (e) {
console.log(e);
});
});
});
}
I use following function to draw the object on top of the canvas after the pdf is loaded to the canvas.
function drawObjectFromBlueprint(blueprint) {
// drawing a draggable element inside the canvas
context.strokeStyle = "lightgray";
// Clearing the previous dummy objects
context.clearRect(0, 0, canvas.width, canvas.height);
// drawing the dummy object
context.beginPath();
context.moveTo(blueprint.x, blueprint.y);
context.lineTo(blueprint.right, blueprint.y);
context.lineTo(blueprint.right, blueprint.bottom);
context.lineTo(blueprint.x, blueprint.bottom);
context.closePath();
context.fillStyle = blueprint.fill;
context.fill();
context.stroke();
}
I handle the mouse move event using the following code.
function handleMouseMove(e) {
var mouseX = parseInt(e.clientX - offsetX1);
var mouseY = parseInt(e.clientY - offsetY1);
// always update the global blueprint
blueprint.x = (mouseX - lastX);
blueprint.y = (mouseY - lastY);
blueprint.right = blueprint.x blueprint.width;
blueprint.bottom = blueprint.y blueprint.height;
lastX = mouseX;
lastY = mouseY;
drawObjectFromBlueprint(blueprint);
}
And I listen to the mouse move event using following code.
$("#drawable-canvas").mousemove(function (e) {
handleMouseMove(e);
});
I want to re-draw the object without affecting the underlying PDF. Tried to load the PDF and re-draw the object by clearing the whole canvas, and it does not work as intended.
CodePudding user response:
In the callback of the page.render
method, the pdf page will be drawn on the canvas. You have to save the drawn image separately so that the original image does not disappear by dragging.
// maybe globalScope...?
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
// your code
page.render(renderContext).then(function () {
// Save the original page image.
tempCanvas.width = canvas.width;
tempCanvas.height = canvas.height;
tempCtx.drawImage(canvas, 0, 0);
// creating the dummy object on success
drawObjectFromBlueprint(blueprint);
}, ...
Next, please modify it to draw with the original page image in drawObjectFromBlueprint
.
function drawObjectFromBlueprint(blueprint) {
// draw original page image
context.drawImage(tempCanvas, 0, 0);
// drawing a draggable element inside the canvas
context.strokeStyle = "lightgray";
...
}
CodePudding user response:
After two hours of trying figures out the way to do this. I used two canvases as @Nikolaus suggested on top of each other and used the bottom canvas to load the PDF and the top canvas for the drawing.
Following is my HTML:
<div class="col-md-12" id="pdfDisplay" style="display: none;">
<div id="pageContainer" class="pdfViewer nopadding" style="background-color:transparent">
<canvas id="pdf-canvas" style="border:1px solid black"></canvas>
<canvas id="drawable-canvas" style="border:1px solid black"></canvas>
</div>
</div>
Following is my CSS to place both canvases on top of each other.
#pageContainer { position: relative; }
#drawable-canvas { position: absolute; top: 0; left: 0; }
Following is my global Javascript variables:
var isUploading = false;
var base64pdf = "";
var initX = 50;
var initY = 50;
var initWidth = 200;
var initHeight = 150;
// blueprint options are in pixels
// this blueprint holds the latest values for the draggable object
// always update this global blueprint when making a change so it holds the latest values
var blueprint = {
x: initX,
y: initY,
width: initWidth,
height: initHeight,
right: (initX initWidth), // x width
bottom: (initY initHeight), // y height
fill: "skyblue"
};
var context = null;
var canvas = null;
var drawableContext = null;
var drawableCanvas = null;
var canvasOffset = null;
var offsetX = 0;
var offsetY = 0;
var canvasOffset1 = null;
var offsetX1 = 0;
var offsetY1 = 0;
var lastX = 0;
var lastY = 0;
var mouseIsDown = false;
Javascript function to listen to mouse up, down and movement as I only need to track the mouse movements when the object is dragged using the mouse.
$("#drawable-canvas").mousedown(function (e) {
var mouseX = parseInt(e.clientX - offsetX);
var mouseY = parseInt(e.clientY - offsetY);
lastX = mouseX;
lastY = mouseY;
mouseIsDown = true;
});
$("#drawable-canvas").mousemove(function (e) {
handleMouseMove(e);
});
$("#drawable-canvas").mouseup(function (e) {
mouseIsDown = false;
});
Javascript function to load the PDF to the bottom canvas.
function loadPdfPreview(base64pdf){
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.0.943/pdf.worker.min.js';
var loadingTask = pdfjsLib.getDocument({data: base64pdf});
loadingTask.promise.then(function (pdf) {
// Fetch the first page
pdf.getPage(1).then(function (page) {
var scale = 1.0;
var viewport = page.getViewport(scale);
// Prepare canvas using PDF page dimensions
canvas = document.getElementById('pdf-canvas');
context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
canvasOffset = $("#pdf-canvas").offset();
offsetX = canvasOffset.left;
offsetY = canvasOffset.top;
canvasOffset1 = $("#drawable-canvas").offset();
offsetX1 = canvasOffset1.left;
offsetY1 = canvasOffset1.top;
// creating the drawable-canvas canvas for drawing purposes without affecting the pdf-canvas
// it has identical width and height and X and Y values
drawableCanvas = document.getElementById('drawable-canvas');;
drawableCanvas.height = viewport.height;
//bringing the drawable canvas up using z-index
drawableCanvas.style.zIndex = 1;
drawableCanvas.width = viewport.width;
drawableContext = drawableCanvas.getContext('2d');
// Render PDF page into canvas context
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext).then(function () {
// creating the dummy object on success
drawObjectFromBlueprint(blueprint);
}, function (e) {
console.log(e);
});
});
});
}
Handling Mouse movement(dragging the object):
function handleMouseMove(e) {
if (!mouseIsDown) {
return;
}
var mouseX = parseInt(e.clientX - offsetX1);
var mouseY = parseInt(e.clientY - offsetY1);
// always update the global blueprint
blueprint.x = (mouseX - lastX);
blueprint.y = (mouseY - lastY);
blueprint.right = blueprint.x blueprint.width;
blueprint.bottom = blueprint.y blueprint.height;
lastX = mouseX;
lastY = mouseY;
drawObjectFromBlueprint(blueprint);
console.log(blueprint);
}
Javascript function to draw the object on the drawable canvas, totally independent from the pdf-canvas.
function drawObjectFromBlueprint(blueprint) {
// drawing a draggable element inside the canvas
drawableContext.strokeStyle = "lightgray";
// Clearing the previous dummy objects
drawableContext.clearRect(0, 0, drawableCanvas.width, drawableCanvas.height);
// drawing the dummy object
drawableContext.beginPath();
drawableContext.moveTo(blueprint.x, blueprint.y);
drawableContext.lineTo(blueprint.right, blueprint.y);
drawableContext.lineTo(blueprint.right, blueprint.bottom);
drawableContext.lineTo(blueprint.x, blueprint.bottom);
drawableContext.closePath();
drawableContext.fillStyle = blueprint.fill;
drawableContext.fill();
drawableContext.stroke();
}