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

use PDFJS text layer renderer instead of our own

Brian Gough 10 лет назад
Родитель
Сommit
29080bafb2

+ 6 - 1
services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee

@@ -27,6 +27,7 @@ define [
 			PAGE_LOAD_TIMEOUT: 60*1000
 			INDICATOR_DELAY1: 100  # time to delay before showing the indicator
 			INDICATOR_DELAY2: 250  # time until the indicator starts animating
+			TEXTLAYER_TIMEOUT: 100
 
 			constructor: (@url, @options) ->
 				if window.location?.search?.indexOf("disable-font-face=true") >= 0
@@ -307,6 +308,7 @@ define [
 				textLayer = new pdfTextLayer({
 					textLayerDiv: element.text[0]
 					viewport: viewport
+					renderer: PDFJS.renderTextLayer
 				})
 
 				annotationsLayer = new pdfAnnotations({
@@ -321,11 +323,14 @@ define [
 					transform: [pixelRatio, 0, 0, pixelRatio, 0, 0]
 				}
 
+				textLayerTimeout = @TEXTLAYER_TIMEOUT
+
 				result.then () ->
 					# page render success
 					canvas.removeClass('pdfng-rendering')
-					page.getTextContent().then (textContent) ->
+					page.getTextContent({normalizeWhitespace: true}).then (textContent) ->
 						textLayer.setTextContent textContent
+						textLayer.render(textLayerTimeout)
 					, (error) ->
 						self.errorCallback?(error)
 					page.getAnnotations().then (annotations) ->

+ 32 - 200
services/web/public/coffee/ide/pdfng/directives/pdfTextLayer.coffee

@@ -1,225 +1,57 @@
 define [
 	"base"
 ], (App) ->
-	# App = angular.module 'pdfTextLayer', []
 
-	App.factory 'pdfTextLayer', [ () ->
-
-	# TRANSLATED FROM pdf.js-1.0.712
-	# pdf.js-1.0.712/web/ui_utils.js
-	# pdf.js-1.0.712/web/text_layer_builder.js
-
-	# -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
-
-	# Copyright 2012 Mozilla Foundation
-	# *
-	# * Licensed under the Apache License, Version 2.0 (the "License");
-	# * you may not use this file except in compliance with the License.
-	# * You may obtain a copy of the License at
-	# *
-	# *     http://www.apache.org/licenses/LICENSE-2.0
-	# *
-	# * Unless required by applicable law or agreed to in writing, software
-	# * distributed under the License is distributed on an "AS IS" BASIS,
-	# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-	# * See the License for the specific language governing permissions and
-	# * limitations under the License.
-	#
-
-	# globals CustomStyle, scrollIntoView, PDFJS
-	# ms
-
-	# optimised CSS custom property getter/setter
-
-		CustomStyle = (CustomStyleClosure = ->
-
-			# As noted on: http://www.zachstronaut.com/posts/2009/02/17/
-			#              animate-css-transforms-firefox-webkit.html
-			# in some versions of IE9 it is critical that ms appear in this list
-			# before Moz
-			CustomStyle = ->
-			prefixes = [
-				'ms'
-				'Moz'
-				'Webkit'
-				'O'
-			]
-			_cache = {}
-			CustomStyle.getProp = get = (propName, element) ->
-
-				# check cache only when no element is given
-				return _cache[propName]	if arguments.length is 1 and typeof _cache[propName] is 'string'
-				element = element or document.documentElement
-				style = element.style
-				prefixed = undefined
-				uPropName = undefined
-
-				# test standard property first
-				return (_cache[propName] = propName)	if typeof style[propName] is 'string'
-
-				# capitalize
-				uPropName = propName.charAt(0).toUpperCase() + propName.slice(1)
-
-				# test vendor specific properties
-				i = 0
-				l = prefixes.length
-
-				while i < l
-					prefixed = prefixes[i] + uPropName
-					return (_cache[propName] = prefixed)	if typeof style[prefixed] is 'string'
-					i++
+	# uses the PDFJS text layer renderer to provide invisible overlayed
+	# text for searching
 
-				#if all fails then set to undefined
-				_cache[propName] = 'undefined'
-
-			CustomStyle.setProp = set = (propName, element, str) ->
-				prop = @getProp(propName)
-				element.style[prop] = str	if prop isnt 'undefined'
-				return
-
-			CustomStyle
-		)()
-
-		#################################
-
-		isAllWhitespace = (str) ->
-			not NonWhitespaceRegexp.test(str)
-		'use strict'
-		FIND_SCROLL_OFFSET_TOP = -50
-		FIND_SCROLL_OFFSET_LEFT = -400
-		MAX_TEXT_DIVS_TO_RENDER = 100000
-		RENDER_DELAY = 200
-		NonWhitespaceRegexp = /\S/
-
-		###*
-		TextLayerBuilder provides text-selection functionality for the PDF.
-		It does this by creating overlay divs over the PDF text. These divs
-		contain text that matches the PDF text they are overlaying. This object
-		also provides a way to highlight text that is being searched for.
-		###
+	App.factory 'pdfTextLayer', [ () ->
 
 		class pdfTextLayer
 
 			constructor: (options) ->
 				@textLayerDiv = options.textLayerDiv
-				@layoutDone = false
 				@divContentDone = false
-				@pageIdx = options.pageIndex
-				@matches = []
-				@lastScrollSource = options.lastScrollSource or null
 				@viewport = options.viewport
-				@isViewerInPresentationMode = options.isViewerInPresentationMode
 				@textDivs = []
-				@findController = options.findController or null
+				@renderer = options.renderer
+				@renderingDone = false
 
-			renderLayer: () ->
-				textLayerFrag = document.createDocumentFragment()
-				textDivs = @textDivs
-				textDivsLength = textDivs.length
-				canvas = document.createElement('canvas')
-				ctx = canvas.getContext('2d')
-
-				# No point in rendering many divs as it would make the browser
-				# unusable even after the divs are rendered.
-				return	if textDivsLength > MAX_TEXT_DIVS_TO_RENDER
-				lastFontSize = undefined
-				lastFontFamily = undefined
-				i = 0
-				while i < textDivsLength
-					textDiv = textDivs[i]
-					if textDiv.dataset.isWhitespace
-						i++
-						continue
-					fontSize = textDiv.style.fontSize
-					fontFamily = textDiv.style.fontFamily
+			render: (timeout) ->
+				if @renderingDone or not @divContentDone
+					return
 
-					# Only build font string and set to context if different from last.
-					if fontSize isnt lastFontSize or fontFamily isnt lastFontFamily
-						ctx.font = fontSize + ' ' + fontFamily
-						lastFontSize = fontSize
-						lastFontFamily = fontFamily
-					width = ctx.measureText(textDiv.textContent).width
-					if width > 0
-						textLayerFrag.appendChild textDiv
+				if @textLayerRenderTask?
+					@textLayerRenderTask.cancel()
+					@textLayerRenderTask = null
 
-						if textDiv.dataset.canvasWidth?
-							# Dataset values come of type string.
-							textScale = textDiv.dataset.canvasWidth / width;
-							transform = 'scaleX(' + textScale + ')'
-						else
-							transform = ''
-						rotation = textDiv.dataset.angle
-						if rotation
-							transform = 'rotate(' + rotation + 'deg) ' + transform
-						if transform
-							CustomStyle.setProp 'transform', textDiv, transform
-					i++
-				@textLayerDiv.appendChild textLayerFrag
-				return
+				@textDivs = []
+				textLayerFrag = document.createDocumentFragment()
 
-			appendText: (geom, styles) ->
-				style = styles[geom.fontName]
-				textDiv = document.createElement('div')
-				@textDivs.push textDiv
-				if isAllWhitespace(geom.str)
-					textDiv.dataset.isWhitespace = true
-					return
-				tx = PDFJS.Util.transform(@viewport.transform, geom.transform)
-				angle = Math.atan2(tx[1], tx[0])
-				angle += Math.PI / 2	if style.vertical
-				fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3]))
-				fontAscent = fontHeight
-				if style.ascent
-					fontAscent = style.ascent * fontAscent
-				else fontAscent = (1 + style.descent) * fontAscent	if style.descent
-				left = undefined
-				top = undefined
-				if angle is 0
-					left = tx[4]
-					top = tx[5] - fontAscent
-				else
-					left = tx[4] + (fontAscent * Math.sin(angle))
-					top = tx[5] - (fontAscent * Math.cos(angle))
-				textDiv.style.left = left + 'px'
-				textDiv.style.top = top + 'px'
-				textDiv.style.fontSize = fontHeight + 'px'
-				textDiv.style.fontFamily = style.fontFamily
-				textDiv.textContent = geom.str
+				@textLayerRenderTask = @renderer {
+					textContent: this.textContent,
+					container: textLayerFrag,
+					viewport: this.viewport,
+					textDivs: this.textDivs,
+					timeout: timeout,
+					enhanceTextSelection: this.enhanceTextSelection,
+				}
 
-				textDiv.ondblclick = (e) ->
-					if (window.getSelection)
-						window.getSelection().removeAllRanges();
-					else if (document.selection)
-						document.selection.empty();
+				textLayerSuccess = () =>
+					@textLayerDiv.appendChild(textLayerFrag)
+					@renderingDone = true
 
-				# |fontName| is only used by the Font Inspector. This test will succeed
-				# when e.g. the Font Inspector is off but the Stepper is on, but it's
-				# not worth the effort to do a more accurate test.
-				textDiv.dataset.fontName = geom.fontName	if PDFJS.pdfBug
+				textLayerFailure = () ->
+					return # canceled or failed to render text layer -- skipping errors
 
-				# Storing into dataset will convert number into string.
-				textDiv.dataset.angle = angle * (180 / Math.PI)	if angle isnt 0
-				# We don't bother scaling single-char text divs, because it has very
-				# little effect on text highlighting. This makes scrolling on docs with
-				# lots of such divs a lot faster.
-				if textDiv.textContent.length > 1
-					if style.vertical
-						textDiv.dataset.canvasWidth = geom.height * @viewport.scale
-					else
-						textDiv.dataset.canvasWidth = geom.width * @viewport.scale
-					return
+				@textLayerRenderTask.promise.then(textLayerSuccess, textLayerFailure)
 
 			setTextContent: (textContent) ->
-				@textContent = textContent
-				textItems = textContent.items
-				i = 0
-				len = textItems.length
+				if (@textLayerRenderTask)
+					@textLayerRenderTask.cancel();
+					@textLayerRenderTask = null;
 
-				while i < len
-					@appendText textItems[i], textContent.styles
-					i++
-				@divContentDone = true
-				@renderLayer()
-				return
+				@textContent = textContent;
+				@divContentDone = true;
 
 	]