Nimesh khatri
Nimesh khatri

Reputation: 763

Error: Cannot use the same canvas during multiple render() operations. PDF.js issue

I'm working on web application(c#) with angularjs (client side) project. we've requirement to show pdf on webpage with next/previous button options. for that We'd used pdf.js (By Mozilla Foundation), and we've implemented code as per given example. we have created one angular directive to things works correctly. Everything works fine while it is loading page first time, now come to point:

After loading new source URL it is throwing below error:

pdf.js?v=1641729671:12170 Uncaught (in promise) Error: Cannot use the same canvas during multiple render() operations. Use different canvas or ensure previous operations were cancelled or completed.

at InternalRenderTask.initializeGraphics (pdf.js?v=1641729671:12170)
at pdf.js?v=1641729671:10666

I've tried below things from my side:

I did research on google but I didn't get any working solution :( now let me post my client side code for better understanding.

below is my directive code:

angular.module('angularPDFView', [])
.directive('pdfviewer', ['$parse', function ($parse) {
    var canvas = null;
    var instance_id = null;
    var context = null;

    return {
        restrict: "E",
        template: '<canvas></canvas>',
        scope: {
            onPageLoad: '&',
            loadProgress: '&',
            src: '@',
            id: '='
        },
        controller: ['$scope', function ($scope) {
            $scope.pageNum = 1;
            $scope.pdfDoc = null;
            $scope.scale = 1.0;
            $scope.rotation = 0;
            $scope.pageNumPending = null;
            $scope.renderTask;
            $scope.path = null;
            $scope.getRandomSpan = function () {
                return Math.floor(Math.random() * 1000000000);
            };

            $scope.loadPDF = function (path) {

                $scope.path = path;
                var randNum = $scope.getRandomSpan();
                pdfjsLib.GlobalWorkerOptions.workerSrc = '/Content/js/pdf.worker.js?v=' + randNum;

                console.log('loadPDF ', path);
                $scope.scale = 1.0;
                $scope.rotation = 0;

                var loadingTask = pdfjsLib.getDocument(path);

                loadingTask.promise.then(function (_pdfDoc) {

                    $scope.pdfDoc = _pdfDoc;
                    $scope.renderPage($scope.pageNum);
                });
            };

            $scope.renderPage = function (num) {
                pageRendering = true;

                // Using promise to fetch the page
                $scope.pdfDoc.getPage(num).then(function (page) {

                    if ($scope.renderTask) {
                        try {
                            $scope.renderTask.cancel();
                        } catch (e) {
                            //
                        }
                    }

                    var viewport = page.getViewport({ scale: $scope.scale });

                    canvas.height = viewport.height;
                    canvas.width = viewport.width;

                    // Render PDF page into canvas context
                    var renderContext = {
                        canvasContext: context,
                        viewport: viewport,
                        continueCallback: function (cont) {
                            cont();
                        }
                    };

                    $scope.renderTask = page.render(renderContext);

                    // Wait for rendering to finish

                    try {
                        $scope.renderTask.promise.then(function () {

                            pageRendering = false;
                            if ($scope.pageNumPending !== null) {
                                // New page rendering is pending
                                $scope.renderPage($scope.pageNumPending);
                                $scope.pageNumPending = null;
                            }

                            $scope.$apply(function () {
                                $scope.onPageLoad({
                                    page: $scope.pageNum,
                                    total: $scope.pdfDoc.numPages
                                });
                            });

                        });
                    } catch (e) {
                        //
                    }
                });

                // Update page counters
                $scope.pageNum = num;
            };

            $scope.queueRenderPage = function (num) {

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

                if ($scope.pageRendering) {
                    $scope.pageNumPending = num;
                } else {
                    $scope.renderPage(num);
                }
            };

            $scope.$on('pdfviewer.prevPage', function () {
                if ($scope.pageNum <= 1) {
                    return;
                }
                $scope.pageNum--;
                $scope.queueRenderPage($scope.pageNum);
            });

            /**
             * Displays next page.
             */
            $scope.$on('pdfviewer.nextPage', function () {
                if ($scope.pageNum >= $scope.pdfDoc.numPages) {
                    return;
                }
                $scope.pageNum++;
                $scope.queueRenderPage($scope.pageNum);
            });

            $scope.$on('pdfviewer.gotoPage', function (evt, id, page) {
                if (id !== instance_id) {
                    return;
                }

                if ($scope.pdfDoc === null) {

                    $scope.pageNum = page;
                    $scope.loadPDF($scope.path);

                } else {
                    if (page >= 1 && page <= $scope.pdfDoc.numPages) {
                        $scope.pageNum = page;
                        $scope.renderPage($scope.pageNum);
                    }
                }
            });
        }],
        link: function (scope, iElement, iAttr) {
            canvas = iElement.find('canvas')[0];
            context = canvas.getContext('2d');

            instance_id = iAttr.id;

            iAttr.$observe('src', function (v) {
                console.log('src attribute changed, new value is', v);
                if (v !== undefined && v !== null && v !== '') {

                    scope.pageNum = 1;
                    scope.loadPDF(scope.src);
                }
            });
        }
    };
}])
.service("angularPDFViewerService", ['$rootScope', function ($rootScope) {

    var svc = {};
    svc.nextPage = function () {
        $rootScope.$broadcast('pdfviewer.nextPage');
    };

    svc.prevPage = function () {
        $rootScope.$broadcast('pdfviewer.prevPage');
    };

    svc.Instance = function (id) {
        var instance_id = id;

        return {
            prevPage: function () {
                $rootScope.$broadcast('pdfviewer.prevPage');
            },
            nextPage: function () {
                $rootScope.$broadcast('pdfviewer.nextPage');
            },
            gotoPage: function (page) {
                $rootScope.$broadcast('pdfviewer.gotoPage', instance_id, page);
            }
        };
    };

    return svc;
}]);

..

How can I cancel render operation or Is there any way to use different canvas to load PDF ?

Any help would be appreciated. Thanks in advance.!

Upvotes: 1

Views: 4116

Answers (2)

Nimesh khatri
Nimesh khatri

Reputation: 763

After two days of efforts finally I've fixed pdf load issue, console error is still there but functionality is working correctly now :)

I'm posting answer that might help someone, here are little steps I did:

Visit: https://cdnjs.com/libraries/pdf.js

I've downloaded most recent version of PDF.js and Pdf-worker.js from above mentioned link. And just replaced my old reference libraries with newer one and that's it.

It works like charm in my angular application now!!!

Upvotes: 0

georgeawg
georgeawg

Reputation: 48968

Use a "render in progress" flag:

var renderInProgress;
var renderTask;

if (renderInProgress) {

   //ensure previous operations were cancelled or completed.

   .then(function() {
       renderTask = doRender();
   });
} else {
    renderTask = doRender();
};

function doRender() {
    renderInProgress = true;
    var renderOp = page.render(renderContext);

    renderOp.promise = renderOp.promise.then(function () {
        //Do whatever
    }).finally(function() {
        renderInProgress = false;
    });

    return renderOp;
}

Upvotes: 1

Related Questions