Просмотр исходного кода

Merge pull request #134 from sharelatex/pdfng-reload-on-error

Pdfng reload on error
Brian Gough 11 лет назад
Родитель
Сommit
df9cb03dcc

+ 3 - 0
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

+ 72 - 12
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
 

+ 17 - 1
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)