how to fix this Error Cannot use the same canvas during multiple render() operations

Viewed 7530

I am using canvas in react and rendering pdf's as images using the canvas.

Now, when I get new data i.e another pdf get's added, then again have to use the canvases for that.

I am not sure how to fix this error or how to remove the canvas or even clear the canvas before using it again.

Here's the relevant code:

 pdfLoop = (item,index) => {
        var that = this;
        PDFJS.getDocument(item).then(function getPdfHelloWorld(pdf) {
             //
             // Fetch the first page
             console.log('url is : ',item);
             pdf.getPage(1).then(function getPageHelloWorld(page) {
               var scale = 0.5;
               var viewport = page.getViewport(scale);


                   let cref = 'canvas'+index;
                   let imgref ='img'+index;
                   console.log('cref no : ',cref);
                   console.log('img no : ',imgref);

                   // Prepare canvas using PDF page dimensions
                   //
                   var canvas = that.canvasRefs[cref];
                   //let imagez = that.imageRefs[imgref];
                   var context = canvas.getContext('2d');
                   context.globalcompositeoperation = 'source-over';
                  // context.fillStyle = "#fff";
                  //draw on entire canvas
                  //context.fillRect( 0, 0, canvas.width, canvas.height );
                   canvas.height = viewport.height;
                   canvas.width = viewport.width;

                    //imagez.src = canvas.toDataURL("image/png");
                   //
                   // Render PDF page into canvas context
                   //
                   //page.render({canvasContext: context, viewport: viewport});
                   var task = page.render({canvasContext: context, viewport: viewport})
              task.promise.then(function(){
                //console.log(canvas.toDataURL('image/png'));
                let imgItem = {imgref:canvas.toDataURL('image/png'),page:index+1,rotate:0}

                 let newState = that.state.imgsrc;
                 newState[index] = imgItem;
                 //let newState = that.state.imgsrc.concat(imgItem);
                that.setState({
                    imgsrc:newState
                });
                //imagez.src = canvas.toDataURL('image/png')
              });

             });
           });
       }
5 Answers

In case someone stumbles across this, the error message states the following Use different canvas or ensure previous operations were cancelled or completed.

When getting the document, if there already is a document, it has to be destroyed. I.e:

PDFJS.getDocument({ url: pdf_url }).promise
    .then((pdf_doc) => {

        if (this.pdf_doc) {
            this.pdf_doc.destroy();
        }
        this.pdf_doc = pdf_doc;
        this.total_pages = this.pdf_doc.numPages;
    })

I have no idea if this is a good solution, but at least it worked for me.

I've had the same exact problem as you, but my solution was a bit different than the answers previously posted.

For me the problem was the fact that I was unnecessarily re-rendering with a state change. Check if you aren't re-rendering the component without properly clearing the canvas, or if you even need to re-render at all (in my case I didn't).

Hopefully this could help

In order to avoid this situation, put your canvas object in a div object as is shown below:

<div id="div_canvas">
    <canvas id="cnv"></canvas>
</div>

Then, before to call pdf.js functions, remove the "div_canvas" content and recreate it:

$("#div_canvas").html("");
$("#div_canvas").html("<canvas id='cnv'></canvas>");

I had exactly the same issue when working with PDF.js, the solution to this is, You will have to clear your context after the render completes.

if (context) {
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.beginPath();
}

This will make sure that the context is cleared and ready for the next render and the error disappears.

this worked for me.

In some cases, this error will be displayed when the pdf has action buttons such as next/previous or scaling. In this cases, often you have a function for rendering pdf page such as:

renderPage(num) {
    // Using promise to fetch the page
    pdfDoc.getPage(num).then(function (page) {
        const viewport = page.getViewport({scale: scale});
        canvas.height = viewport.height;
        canvas.width = viewport.width;

        // Render PDF page into canvas context
        const renderContext = {
            canvasContext: ctx,
            viewport: viewport
        };
        page.render(renderContext);
    });
}

To fix the error just perform following changes:

  1. Add two variable for controlling conflict and cache waiting page number:

    pageRendering = false; // Check conflict
    pageNumPending = null; // Cache waiting page number
    
  2. Use these variables as follow:

    renderPage(num) {
      if (this.pageRendering) { // Check if other page is rendering
        this.pageNumPending = num; // Cache waited page number until previous page rendering completed
      } else {
        this.pageRendering = true;
        // Using promise to fetch the page
        pdfDoc.getPage(num).then(function (page) {
            const viewport = page.getViewport({scale: scale});
            canvas.height = viewport.height;
            canvas.width = viewport.width;
    
            // Render PDF page into canvas context
            const renderContext = {
                canvasContext: ctx,
                viewport: viewport
            };
            const renderTask = page.render(renderContext);
    
            // Wait for rendering to finish
            renderTask.promise.then(function () {
                this.pageRendering = false;
                if (pageNumPending !== null) {
                    // Waited page must be rendered
                    this.renderPage(pageNumPending);
                    // Must be set to null to prevent infinite loop
                    this.pageNumPending = null;
                }
            });
        });
    
Related