如何使用 PDF.js 和 jQuery 在 PDF 上创建可拖动元素

Posted

技术标签:

【中文标题】如何使用 PDF.js 和 jQuery 在 PDF 上创建可拖动元素【英文标题】:How to create a draggable element on top of a PDF using PDF.js and jQuery 【发布时间】:2021-11-15 13:51:34 【问题描述】:

我正在创建一项服务,以使用 PDF.js 和 jQuery 为 pdf 标记图像。我设法使用 PDF.js 创建了一个可拖动的对象,但该对象在被拖动时会留下过去对象的痕迹。

我使用context.clearRect(0, 0, canvas.width, canvas.height); 清除了过去的对象,但它也清除了画布中的底层 PDF。

如何在不影响底层 PDF 的情况下拖动该对象?

这是我到目前为止所做的。

我正在使用以下代码将 PDF 加载到画布上。

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);
                );

            );
        );
    

pdf 加载到画布后,我使用以下函数在画布顶部绘制对象。

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();
    

我使用以下代码处理鼠标移动事件。

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);
    

我使用以下代码监听鼠标移动事件。

$("#drawable-canvas").mousemove(function (e) 
        handleMouseMove(e);
    );

我想在不影响基础 PDF 的情况下重新绘制对象。试图通过清除整个画布来加载 PDF 并重新绘制对象,但它没有按预期工作。

【问题讨论】:

我认为您需要一个可拖动的新画布。防止与pdf一起使用。 【参考方案1】:

page.render方法的回调中,会在画布上绘制pdf页面。您必须单独保存绘制的图像,以免原始图像因拖动而消失。

// 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);
, ...

接下来,请将其修改为使用drawObjectFromBlueprint中的原始页面图像进行绘制。

function drawObjectFromBlueprint(blueprint) 
  // draw original page image
  context.drawImage(tempCanvas, 0, 0);

  // drawing a draggable element inside the canvas
  context.strokeStyle = "lightgray";
  ...


【讨论】:

【参考方案2】:

经过两个小时的尝试,找到了实现此目的的方法。我使用了@Nikolaus 建议的两个画布,并使用底部画布加载 PDF,顶部画布用于绘图。

以下是我的 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>

以下是我将两个画布放在彼此顶部的 CSS。

#pageContainer  position: relative; 
#drawable-canvas  position: absolute; top: 0; left: 0; 

以下是我的全局 javascript 变量:

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 函数可以监听鼠标的上移、下移和移动,因为我只需要在使用鼠标拖动对象时跟踪鼠标的移动。

$("#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 函数将 PDF 加载到底部画布。

    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);
                );

            );
        );
    

处理鼠标移动(拖动对象):

    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函数,完全独立于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();
    

【讨论】:

以上是关于如何使用 PDF.js 和 jQuery 在 PDF 上创建可拖动元素的主要内容,如果未能解决你的问题,请参考以下文章

如何将pdf.js嵌入fancybox

如何在 PyQt 中使用 pdf.js 查看器呈现 PDF?

如何使用 pdf.js [关闭]

如何创建pdf的buffer,让pdf.js实现预览pdf文件

PDF.js 如何取到PDF的目录页码

如何在 node.js 中生成 PDF