diff --git a/examples/acroforms/index.html b/examples/acroforms/index.html index 3011e016a..d6c644f44 100644 --- a/examples/acroforms/index.html +++ b/examples/acroforms/index.html @@ -11,7 +11,9 @@ + + + + + diff --git a/test/test_manifest.json b/test/test_manifest.json index d478031c1..1bb299cee 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -323,12 +323,6 @@ "lastPage": 1, "about": "The same file as issue2337." }, - { "id": "thuluthfont-text", - "file": "pdfs/ThuluthFeatures.pdf", - "md5": "b7e18bf7a3d6a9c82aefa12d721072fc", - "rounds": 1, - "type": "text" - }, { "id": "freeculture", "file": "pdfs/freeculture.pdf", "md5": "dcdf3a8268e6a18938a42d5149efcfca", diff --git a/test/test_slave.html b/test/test_slave.html index 8a78e978b..7236914d5 100644 --- a/test/test_slave.html +++ b/test/test_slave.html @@ -25,7 +25,9 @@ limitations under the License. + + diff --git a/test/text_layer_test.css b/test/text_layer_test.css new file mode 100644 index 000000000..ed62e6c99 --- /dev/null +++ b/test/text_layer_test.css @@ -0,0 +1,38 @@ +/* Copyright 2015 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. + */ + +/* Used in 'text' tests */ + +.textLayer { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; +} +.textLayer > div { + position: absolute; + white-space: pre; + -webkit-transform-origin: 0% 0%; + -moz-transform-origin: 0% 0%; + -o-transform-origin: 0% 0%; + -ms-transform-origin: 0% 0%; + transform-origin: 0% 0%; + border: solid 1px rgba(255, 0, 0, 0.5); + background-color: rgba(255, 255, 32, 0.1); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} diff --git a/test/unit/unit_test.html b/test/unit/unit_test.html index 5fe1f254b..fcce65d19 100644 --- a/test/unit/unit_test.html +++ b/test/unit/unit_test.html @@ -36,7 +36,9 @@ + + diff --git a/web/annotations_layer_builder.js b/web/annotations_layer_builder.js index 7e81c7e0e..752e43134 100644 --- a/web/annotations_layer_builder.js +++ b/web/annotations_layer_builder.js @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/*globals PDFJS, CustomStyle, mozL10n, SimpleLinkService */ +/*globals PDFJS, mozL10n, SimpleLinkService */ 'use strict'; @@ -27,6 +27,8 @@ * @class */ var AnnotationsLayerBuilder = (function AnnotationsLayerBuilderClosure() { + var CustomStyle = PDFJS.CustomStyle; + /** * @param {AnnotationsLayerBuilderOptions} options * @constructs AnnotationsLayerBuilder diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index 52b3f59df..021fe7f4a 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals RenderingStates, PDFJS, CustomStyle, CSS_UNITS, getOutputScale, +/* globals RenderingStates, PDFJS, CSS_UNITS, getOutputScale, TextLayerBuilder, AnnotationsLayerBuilder, Promise, approximateFraction, roundToDivide */ @@ -36,6 +36,8 @@ var TEXT_LAYER_RENDER_DELAY = 200; // ms * @implements {IRenderableView} */ var PDFPageView = (function PDFPageViewClosure() { + var CustomStyle = PDFJS.CustomStyle; + /** * @constructs PDFPageView * @param {PDFPageViewOptions} options diff --git a/web/text_layer_builder.js b/web/text_layer_builder.js index 0691c8c14..6ef873684 100644 --- a/web/text_layer_builder.js +++ b/web/text_layer_builder.js @@ -12,18 +12,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals CustomStyle, PDFJS */ +/* globals PDFJS */ 'use strict'; -var MAX_TEXT_DIVS_TO_RENDER = 100000; - -var NonWhitespaceRegexp = /\S/; - -function isAllWhitespace(str) { - return !NonWhitespaceRegexp.test(str); -} - /** * @typedef {Object} TextLayerBuilderOptions * @property {HTMLDivElement} textLayerDiv - The text layer container. @@ -50,6 +42,7 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() { this.viewport = options.viewport; this.textDivs = []; this.findController = options.findController || null; + this.textLayerRenderTask = null; this._bindMouse(); } @@ -68,67 +61,6 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() { this.textLayerDiv.dispatchEvent(event); }, - renderLayer: function TextLayerBuilder_renderLayer() { - var textLayerFrag = document.createDocumentFragment(); - var textDivs = this.textDivs; - var textDivsLength = textDivs.length; - var canvas = document.createElement('canvas'); -//#if MOZCENTRAL || FIREFOX || GENERIC - canvas.mozOpaque = true; -//#endif - var ctx = canvas.getContext('2d', {alpha: false}); - - // No point in rendering many divs as it would make the browser - // unusable even after the divs are rendered. - if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) { - this._finishRendering(); - return; - } - - var lastFontSize; - var lastFontFamily; - for (var i = 0; i < textDivsLength; i++) { - var textDiv = textDivs[i]; - if (textDiv.dataset.isWhitespace !== undefined) { - continue; - } - - var fontSize = textDiv.style.fontSize; - var fontFamily = textDiv.style.fontFamily; - - // Only build font string and set to context if different from last. - if (fontSize !== lastFontSize || fontFamily !== lastFontFamily) { - ctx.font = fontSize + ' ' + fontFamily; - lastFontSize = fontSize; - lastFontFamily = fontFamily; - } - - var width = ctx.measureText(textDiv.textContent).width; - if (width > 0) { - textLayerFrag.appendChild(textDiv); - var transform; - if (textDiv.dataset.canvasWidth !== undefined) { - // Dataset values come of type string. - var textScale = textDiv.dataset.canvasWidth / width; - transform = 'scaleX(' + textScale + ')'; - } else { - transform = ''; - } - var rotation = textDiv.dataset.angle; - if (rotation) { - transform = 'rotate(' + rotation + 'deg) ' + transform; - } - if (transform) { - CustomStyle.setProp('transform' , textDiv, transform); - } - } - } - - this.textLayerDiv.appendChild(textLayerFrag); - this._finishRendering(); - this.updateMatches(); - }, - /** * Renders the text layer. * @param {number} timeout (optional) if specified, the rendering waits @@ -139,87 +71,35 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() { return; } - if (this.renderTimer) { - clearTimeout(this.renderTimer); - this.renderTimer = null; + if (this.textLayerRenderTask) { + this.textLayerRenderTask.cancel(); + this.textLayerRenderTask = null; } - if (!timeout) { // Render right away - this.renderLayer(); - } else { // Schedule - var self = this; - this.renderTimer = setTimeout(function() { - self.renderLayer(); - self.renderTimer = null; - }, timeout); - } - }, - - appendText: function TextLayerBuilder_appendText(geom, styles) { - var style = styles[geom.fontName]; - var textDiv = document.createElement('div'); - this.textDivs.push(textDiv); - if (isAllWhitespace(geom.str)) { - textDiv.dataset.isWhitespace = true; - return; - } - var tx = PDFJS.Util.transform(this.viewport.transform, geom.transform); - var angle = Math.atan2(tx[1], tx[0]); - if (style.vertical) { - angle += Math.PI / 2; - } - var fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3])); - var fontAscent = fontHeight; - if (style.ascent) { - fontAscent = style.ascent * fontAscent; - } else if (style.descent) { - fontAscent = (1 + style.descent) * fontAscent; - } - - var left; - var top; - if (angle === 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; - // |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. - if (PDFJS.pdfBug) { - textDiv.dataset.fontName = geom.fontName; - } - // Storing into dataset will convert number into string. - if (angle !== 0) { - textDiv.dataset.angle = angle * (180 / Math.PI); - } - // 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 (geom.str.length > 1) { - if (style.vertical) { - textDiv.dataset.canvasWidth = geom.height * this.viewport.scale; - } else { - textDiv.dataset.canvasWidth = geom.width * this.viewport.scale; - } - } + this.textDivs = []; + var textLayerFrag = document.createDocumentFragment(); + this.textLayerRenderTask = PDFJS.renderTextLayer({ + textContent: this.textContent, + container: textLayerFrag, + viewport: this.viewport, + textDivs: this.textDivs, + timeout: timeout + }); + this.textLayerRenderTask.promise.then(function () { + this.textLayerDiv.appendChild(textLayerFrag); + this._finishRendering(); + this.updateMatches(); + }.bind(this), function (reason) { + // canceled or failed to render text layer -- skipping errors + }); }, setTextContent: function TextLayerBuilder_setTextContent(textContent) { - this.textContent = textContent; - - var textItems = textContent.items; - for (var i = 0, len = textItems.length; i < len; i++) { - this.appendText(textItems[i], textContent.styles); + if (this.textLayerRenderTask) { + this.textLayerRenderTask.cancel(); + this.textLayerRenderTask = null; } + this.textContent = textContent; this.divContentDone = true; }, diff --git a/web/ui_utils.js b/web/ui_utils.js index 6f5aa26c7..5f4b4fdc7 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -23,57 +23,6 @@ var MAX_AUTO_SCALE = 1.25; var SCROLLBAR_PADDING = 40; var VERTICAL_PADDING = 5; -// optimised CSS custom property getter/setter -var CustomStyle = (function 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 - var prefixes = ['ms', 'Moz', 'Webkit', 'O']; - var _cache = {}; - - function CustomStyle() {} - - CustomStyle.getProp = function get(propName, element) { - // check cache only when no element is given - if (arguments.length === 1 && typeof _cache[propName] === 'string') { - return _cache[propName]; - } - - element = element || document.documentElement; - var style = element.style, prefixed, uPropName; - - // test standard property first - if (typeof style[propName] === 'string') { - return (_cache[propName] = propName); - } - - // capitalize - uPropName = propName.charAt(0).toUpperCase() + propName.slice(1); - - // test vendor specific properties - for (var i = 0, l = prefixes.length; i < l; i++) { - prefixed = prefixes[i] + uPropName; - if (typeof style[prefixed] === 'string') { - return (_cache[propName] = prefixed); - } - } - - //if all fails then set to undefined - return (_cache[propName] = 'undefined'); - }; - - CustomStyle.setProp = function set(propName, element, str) { - var prop = this.getProp(propName); - if (prop !== 'undefined') { - element.style[prop] = str; - } - }; - - return CustomStyle; -})(); - var NullCharactersRegExp = /\x00/g; function removeNullCharacters(str) { diff --git a/web/viewer.css b/web/viewer.css index b012631e6..b7826849f 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -1515,7 +1515,27 @@ html[dir='rtl'] #documentPropertiesOverlay .row > * { font-size: 10px; } -#viewer.textLayer-visible .textLayer > div, +#viewer.textLayer-visible .textLayer { + opacity: 1.0; +} + +#viewer.textLayer-visible .canvasWrapper { + background-color: rgb(128,255,128); +} + +#viewer.textLayer-visible .canvasWrapper canvas { + mix-blend-mode: screen; +} + +#viewer.textLayer-visible .textLayer > div { + background-color: rgba(255, 255, 0, 0.1); + color: black; + border: solid 1px rgba(255, 0, 0, 0.5); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + #viewer.textLayer-hover .textLayer > div:hover { background-color: white; color: black; diff --git a/web/viewer.html b/web/viewer.html index 8a65736ee..dbbaece08 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -60,7 +60,9 @@ See https://github.com/adobe-type-tools/cmap-resources + +