简体   繁体   中英

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:

  • Tried to render pdf by re-linking directive.
  • Tried to render all pdf by page cleanup function but still it causes an issue.
  • Tried to render pdf pages by canceling pages in between but it's not working.
  • Tried to clear canvas and context and reinitialized it but no trick.
  • Tried to create dynamic canvases by different different IDS but it didn't work.
  • Tried to assign class instead of Id of pdf viewer but it didn't work.

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.!

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

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!!!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM