diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 610009bf8c..eae6d8052d 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -10,6 +10,9 @@ define [ $scope.recompile(isAutoCompile: true) $scope.hasPremiumCompile = $scope.project.features.compileGroup == "priority" + $scope.$on "pdf:error:display", () -> + $scope.pdf.error = true + sendCompileRequest = (options = {}) -> url = "/project/#{$scope.project_id}/compile" if options.isAutoCompile diff --git a/services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee b/services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee index 15a45c8908..a173986203 100644 --- a/services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee +++ b/services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee @@ -7,6 +7,8 @@ define [ class PDFRenderer JOB_QUEUE_INTERVAL: 25 + PAGE_LOAD_TIMEOUT: 60*1000 + PAGE_RENDER_TIMEOUT: 60*1000 constructor: (@url, @options) -> PDFJS.disableFontFace = true # avoids repaints, uses worker more @@ -19,9 +21,11 @@ define [ @navigateFn = @options.navigateFn @spinner = new pdfSpinner @resetState() - + @errorCallback = @options.errorCallback + @pdfjs.catch (exception) => + # console.log 'ERROR in get document', exception + @errorCallback(exception) resetState: () -> - @page = [] @complete = [] @timeout = [] @pageLoad = [] @@ -34,9 +38,8 @@ define [ pdfDocument.numPages getPage: (pageNum) -> - # with promise caching - return @page[pageNum] if @page[pageNum]? - @page[pageNum] = @document.then (pdfDocument) -> + @document.then (pdfDocument) -> + # console.log 'got pdf document, now getting Page', pageNum pdfDocument.getPage(pageNum) getPdfViewport: (pageNum, scale) -> @@ -44,28 +47,37 @@ define [ @document.then (pdfDocument) -> pdfDocument.getPage(pageNum).then (page) -> viewport = page.getViewport scale + , (error) -> + console.log 'ERROR', error getDestinations: () -> @document.then (pdfDocument) -> pdfDocument.getDestinations() -# Not available in pdf.js-1.0.712, in later versions there is a direct -# call for this - we should use it as soon as it is available in a -# stable version getDestination: (dest) -> + # There is a direct method for this in pdf.js but it is not + # available in pdf.js-1.0.712. Use the following workaround of + # getting all the destinations and returning only the one we + # want. @destinations = @document.then (pdfDocument) -> pdfDocument.getDestinations() return @destinations.then (all) -> all[dest] - - @document.then (pdfDocument) -> - pdfDocument.getDestination(dest) - + , (error) -> + console.log 'ERROR', error + # When we upgrade we can switch to using the following direct + # code. + # @document.then (pdfDocument) -> + # pdfDocument.getDestination(dest) + # , (error) -> + # console.log 'ERROR', error getPageIndex: (ref) -> @document.then (pdfDocument) -> pdfDocument.getPageIndex(ref).then (idx) -> idx + , (error) -> + console.log 'ERROR', error getScale: () -> @scale @@ -101,6 +113,7 @@ define [ @triggerRenderQueue() processRenderQueue: () -> + return if @shuttingDown return if @jobs > 0 current = @renderQueue.shift() return unless current? @@ -118,17 +131,38 @@ define [ completeRef = @complete renderTaskRef = @renderTask + # console.log 'started page load', pagenum + + timedOut = false + timer = $timeout () => + Raven.captureMessage?('pdfng page load timed out after ' + @PAGE_LOAD_TIMEOUT + 'ms') + # console.log 'page load timed out', pagenum + timedOut = true + @spinner.stop(element.canvas) + # @jobs = @jobs - 1 + # @triggerRenderQueue(0) + this.errorCallback?('timeout') + , @PAGE_LOAD_TIMEOUT @pageLoad[pagenum] = @getPage(pagenum) + @pageLoad[pagenum].then (pageObject) => + # console.log 'in page load success', pagenum + $timeout.cancel(timer) @renderTask[pagenum] = @doRender element, pagenum, pageObject @renderTask[pagenum].then () => # complete + # console.log 'render task success', pagenum completeRef[pagenum] = true @removeCompletedJob renderTaskRef, pagenum , () => + # console.log 'render task failed', pagenum # rejected @removeCompletedJob renderTaskRef, pagenum + .catch (error) -> + # console.log 'in page load error', pagenum, 'timedOut=', timedOut + $timeout.cancel(timer) + # console.log 'ERROR', error doRender: (element, pagenum, page) -> self = this @@ -183,17 +217,43 @@ define [ element.canvas.replaceWith(canvas) + # console.log 'staring page render', pagenum + result = page.render { canvasContext: ctx viewport: viewport } + timedOut = false + + timer = $timeout () => + Raven.captureMessage?('pdfng page render timed out after ' + @PAGE_RENDER_TIMEOUT + 'ms') + # console.log 'page render timed out', pagenum + timedOut = true + result.cancel() + , @PAGE_RENDER_TIMEOUT + result.then () -> + # console.log 'page rendered', pagenum + $timeout.cancel(timer) canvas.removeClass('pdfng-rendering') page.getTextContent().then (textContent) -> textLayer.setTextContent textContent + , (error) -> + console.log 'ERROR', error page.getAnnotations().then (annotations) -> annotationsLayer.setAnnotations annotations + , (error) -> + console.log 'ERROR', error + .catch (error) -> + # console.log 'page render failed', pagenum, error + $timeout.cancel(timer) + if timedOut + # console.log 'calling ERROR callback - was timeout' + self.errorCallback?('timeout') + else if error != 'cancelled' + # console.log 'calling ERROR callback' + self.errorCallback?(error) return result diff --git a/services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee b/services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee index 7f3ac48250..844c572054 100644 --- a/services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee +++ b/services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee @@ -25,7 +25,7 @@ define [ # $scope.pages = [] $scope.document.destroy() if $scope.document? - + $scope.loadCount = $scope.loadCount? ? $scope.loadCount + 1 : 1 # TODO need a proper url manipulation library to add to query string $scope.document = new PDFRenderer($scope.pdfSrc + '&pdfng=true' , { scale: 1, @@ -35,6 +35,9 @@ define [ $scope.$apply() progressCallback: (progress) -> $scope.$emit 'progress', progress + errorCallback: (error) -> + Raven.captureMessage?('pdfng error ' + error) + $scope.$emit 'pdf:error', error }) # we will have all the main information needed to start display @@ -90,6 +93,8 @@ define [ # console.log 'position is', position.page, position.offset # console.log 'setting current page', position.page pagenum = position.page + if pagenum > $scope.numPages - 1 + pagenum = $scope.numPages - 1 $scope.pages[pagenum].current = true $scope.pages[pagenum].position = position @@ -291,6 +296,16 @@ define [ ] #scope.$apply() + scope.$on 'pdf:error', (event, error) -> + return if error == 'cancelled' + # check if too many retries or file is missing + if scope.loadCount > 3 || error.match(/^Missing PDF/i) + scope.$emit 'pdf:error:display' + return + ctrl.load() + # trigger a redraw + scope.scale = angular.copy (scope.scale) + element.on 'scroll', () -> #console.log 'scroll event', element.scrollTop(), 'adjusting?', scope.adjustingScroll if scope.adjustingScroll @@ -315,6 +330,7 @@ define [ scope.$watch 'pdfSrc', (newVal, oldVal) -> # console.log 'loading pdf', newVal, oldVal return unless newVal? + scope.loadCount = 0; # new pdf, so reset load count ctrl.load() # trigger a redraw scope.scale = angular.copy (scope.scale)