diff --git a/AUTHORS b/AUTHORS index e07a482e4669f..20e596d4899c6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -10,6 +10,7 @@ Brendan Dahl Bill Walker Chris G Jones David Quintana +Felix Kälberer <@fkaelberer> Jakob Miland Jonas Jenwald Julian Viereck diff --git a/examples/components/simpleviewer.html b/examples/components/simpleviewer.html new file mode 100644 index 0000000000000..987a140326076 --- /dev/null +++ b/examples/components/simpleviewer.html @@ -0,0 +1,48 @@ + + + + + + + + PDF.js viewer using built components + + + + + + + + + + + + +
+
+
+ + + + + diff --git a/examples/components/simpleviewer.js b/examples/components/simpleviewer.js new file mode 100644 index 0000000000000..139adfa4995f2 --- /dev/null +++ b/examples/components/simpleviewer.js @@ -0,0 +1,50 @@ +/* Copyright 2014 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. + */ + +'use strict'; + +if (!PDFJS.PDFViewer || !PDFJS.getDocument) { + alert('Please build the library and components using\n' + + ' `node make generic components`'); +} + +// In cases when the pdf.worker.js is located at the different folder than the +// pdf.js's one, or the pdf.js is executed via eval(), the workerSrc property +// shall be specified. +// +// PDFJS.workerSrc = '../../build/pdf.worker.js'; + +// Some PDFs need external cmaps. +// +// PDFJS.cMapUrl = '../../external/bcmaps/'; +// PDFJS.cMapPacked = true; + +var DEFAULT_URL = '../../web/compressed.tracemonkey-pldi-09.pdf'; + +var container = document.getElementById('viewerContainer'); +var pdfViewer = new PDFJS.PDFViewer({ + container: container +}); + +container.addEventListener('pagesinit', function () { + // we can use pdfViewer now, e.g. let's change default scale. + pdfViewer.currentScaleValue = 'page-width'; +}); + +// Loading document. +PDFJS.getDocument(DEFAULT_URL).then(function (pdfDocument) { + // Document loaded, specifying document for the viewer. + pdfViewer.setDocument(pdfDocument); +}); diff --git a/examples/learning/helloworld.html b/examples/learning/helloworld.html index b344746352592..347dbbe99b158 100644 --- a/examples/learning/helloworld.html +++ b/examples/learning/helloworld.html @@ -30,8 +30,9 @@

'Hello, world!' example

// PDFJS.disableWorker = true; // - // If pdf.js must be execute via eval or pdf.worker.js is located at the - // different location that pdf.js, specify workerSrc. + // In cases when the pdf.worker.js is located at the different folder than the + // pdf.js's one, or the pdf.js is executed via eval(), the workerSrc property + // shall be specified. // // PDFJS.workerSrc = '../../build/pdf.worker.js'; diff --git a/examples/learning/prevnext.html b/examples/learning/prevnext.html index 42c1f1b816c56..0da467b797a0f 100644 --- a/examples/learning/prevnext.html +++ b/examples/learning/prevnext.html @@ -40,8 +40,9 @@

'Previous/Next' example

// PDFJS.disableWorker = true; // - // If pdf.js must be execute via eval or pdf.worker.js is located at the - // different location that pdf.js, specify workerSrc. + // In cases when the pdf.worker.js is located at the different folder than the + // pdf.js's one, or the pdf.js is executed via eval(), the workerSrc property + // shall be specified. // // PDFJS.workerSrc = '../../build/pdf.worker.js'; diff --git a/examples/node/domstubs.js b/examples/node/domstubs.js index b0e422b8a2c2a..7149f005dd9e4 100644 --- a/examples/node/domstubs.js +++ b/examples/node/domstubs.js @@ -16,6 +16,7 @@ var style = { function xmlEncode(s){ var i = 0, ch; + s = String(s); while (i < s.length && (ch = s[i]) !== '&' && ch !== '<' && ch !== '\"' && ch !== '\n' && ch !== '\r' && ch !== '\t') { i++; diff --git a/examples/node/pdf2svg.js b/examples/node/pdf2svg.js index f5979cacc8d08..1967ee1ffa8dc 100644 --- a/examples/node/pdf2svg.js +++ b/examples/node/pdf2svg.js @@ -14,9 +14,10 @@ global.window = global; global.navigator = { userAgent: 'node' }; global.PDFJS = {}; +require('./domstubs.js'); + PDFJS.workerSrc = true; require('../../build/singlefile/build/pdf.combined.js'); -require('./domstubs.js'); // Loading file from file system into typed array var pdfPath = process.argv[2] || '../../web/compressed.tracemonkey-pldi-09.pdf'; diff --git a/extensions/firefox/content/PdfJsTelemetry-addon.jsm b/extensions/firefox/content/PdfJsTelemetry-addon.jsm index f1dc908ed719e..f0c8a3771e3dc 100644 --- a/extensions/firefox/content/PdfJsTelemetry-addon.jsm +++ b/extensions/firefox/content/PdfJsTelemetry-addon.jsm @@ -26,17 +26,42 @@ Cu.import('resource://gre/modules/Services.jsm'); const ADDON_ID = "uriloader@pdf.js"; var Telemetry = Services.telemetry; -Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_USED", 1, 2, 3, Telemetry.HISTOGRAM_BOOLEAN); -Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_FALLBACK_SHOWN", 1, 2, 3, Telemetry.HISTOGRAM_BOOLEAN); -Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_DOCUMENT_VERSION", 1, 10, 11, Telemetry.HISTOGRAM_LINEAR); -Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_DOCUMENT_GENERATOR", 1, 25, 26, Telemetry.HISTOGRAM_LINEAR); -Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_DOCUMENT_SIZE_KB", 2, 64 * 1024, 20, Telemetry.HISTOGRAM_EXPONENTIAL); -Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_EMBED", 1, 2, 3, Telemetry.HISTOGRAM_BOOLEAN); -Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_FONT_TYPES", 1, 19, 20, Telemetry.HISTOGRAM_LINEAR); -Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_FORM", 1, 2, 3, Telemetry.HISTOGRAM_BOOLEAN); -Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_PRINT", 1, 2, 3, Telemetry.HISTOGRAM_BOOLEAN); -Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_STREAM_TYPES", 1, 19, 20, Telemetry.HISTOGRAM_LINEAR); -Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_TIME_TO_VIEW_MS", 1, 10000, 50, Telemetry.HISTOGRAM_EXPONENTIAL); + +var registerAddonHistogram = Telemetry.registerAddonHistogram; +try { + // Swapping arguments of the registerAddonHistogram for older Firefox versions. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1069953. + var ffVersion = parseInt(Services.appinfo.platformVersion); + var oldTelemetryAPI = ffVersion < 36; + if (ffVersion === 36) { + // Probing FF36 to check if it has new API. + try { + Telemetry.registerAddonHistogram(ADDON_ID, "PDF_36", + Telemetry.HISTOGRAM_LINEAR, 1, 40, 41); + var histogram = Telemetry.getAddonHistogram(ADDON_ID, "PDF_36"); + histogram.add(36); + } catch (e) { + oldTelemetryAPI = true; + } + } + if (oldTelemetryAPI) { + registerAddonHistogram = function (p1, p2, p3, p4, p5, p6) { + return Telemetry.registerAddonHistogram(p1, p2, p4, p5, p6, p3); + }; + } +} catch (ex) { } + +registerAddonHistogram(ADDON_ID, 'PDF_VIEWER_USED', Telemetry.HISTOGRAM_BOOLEAN, 1, 2, 3); +registerAddonHistogram(ADDON_ID, 'PDF_VIEWER_FALLBACK_SHOWN', Telemetry.HISTOGRAM_BOOLEAN, 1, 2, 3); +registerAddonHistogram(ADDON_ID, 'PDF_VIEWER_DOCUMENT_VERSION', Telemetry.HISTOGRAM_LINEAR, 1, 10, 11); +registerAddonHistogram(ADDON_ID, 'PDF_VIEWER_DOCUMENT_GENERATOR', Telemetry.HISTOGRAM_LINEAR, 1, 25, 26); +registerAddonHistogram(ADDON_ID, 'PDF_VIEWER_DOCUMENT_SIZE_KB', Telemetry.HISTOGRAM_EXPONENTIAL, 2, 64 * 1024, 20); +registerAddonHistogram(ADDON_ID, 'PDF_VIEWER_EMBED', Telemetry.HISTOGRAM_BOOLEAN, 1, 2, 3); +registerAddonHistogram(ADDON_ID, 'PDF_VIEWER_FONT_TYPES', Telemetry.HISTOGRAM_LINEAR, 1, 19, 20); +registerAddonHistogram(ADDON_ID, 'PDF_VIEWER_FORM', Telemetry.HISTOGRAM_BOOLEAN, 1, 2, 3); +registerAddonHistogram(ADDON_ID, 'PDF_VIEWER_PRINT', Telemetry.HISTOGRAM_BOOLEAN, 1, 2, 3); +registerAddonHistogram(ADDON_ID, 'PDF_VIEWER_STREAM_TYPES', Telemetry.HISTOGRAM_LINEAR, 1, 19, 20); +registerAddonHistogram(ADDON_ID, 'PDF_VIEWER_TIME_TO_VIEW_MS', Telemetry.HISTOGRAM_EXPONENTIAL, 1, 10000, 50); this.PdfJsTelemetry = { diff --git a/extensions/firefox/content/PdfStreamConverter.jsm b/extensions/firefox/content/PdfStreamConverter.jsm index 7f8124e6b8a29..22d114ef5c1fe 100644 --- a/extensions/firefox/content/PdfStreamConverter.jsm +++ b/extensions/firefox/content/PdfStreamConverter.jsm @@ -77,7 +77,18 @@ function getFindBar(domWindow) { var browser = getContainingBrowser(domWindow); try { var tabbrowser = browser.getTabBrowser(); - var tab = tabbrowser._getTabForBrowser(browser); + var tab; +//#if MOZCENTRAL + tab = tabbrowser.getTabForBrowser(browser); +//#else + if (tabbrowser.getTabForBrowser) { + tab = tabbrowser.getTabForBrowser(browser); + } else { + // _getTabForBrowser is depreciated in Firefox 35, see + // https://bugzilla.mozilla.org/show_bug.cgi?id=1039500. + tab = tabbrowser._getTabForBrowser(browser); + } +//#endif return tabbrowser.getFindBar(tab); } catch (e) { // FF22 has no _getTabForBrowser, and FF24 has no getFindBar @@ -177,46 +188,38 @@ function makeContentReadable(obj, window) { // PDF data storage function PdfDataListener(length) { this.length = length; // less than 0, if length is unknown - this.data = new Uint8Array(length >= 0 ? length : 0x10000); - this.position = 0; + this.buffer = null; this.loaded = 0; } PdfDataListener.prototype = { append: function PdfDataListener_append(chunk) { - var willBeLoaded = this.loaded + chunk.length; - if (this.length >= 0 && this.length < willBeLoaded) { - this.length = -1; // reset the length, server is giving incorrect one + // In most of the cases we will pass data as we receive it, but at the + // beginning of the loading we may accumulate some data. + if (!this.buffer) { + this.buffer = new Uint8Array(chunk); + } else { + var buffer = this.buffer; + var newBuffer = new Uint8Array(buffer.length + chunk.length); + newBuffer.set(buffer); + newBuffer.set(chunk, buffer.length); + this.buffer = newBuffer; } - if (this.length < 0 && this.data.length < willBeLoaded) { - // data length is unknown and new chunk will not fit in the existing - // buffer, resizing the buffer by doubling the its last length - var newLength = this.data.length; - for (; newLength < willBeLoaded; newLength *= 2) {} - var newData = new Uint8Array(newLength); - newData.set(this.data); - this.data = newData; + this.loaded += chunk.length; + if (this.length >= 0 && this.length < this.loaded) { + this.length = -1; // reset the length, server is giving incorrect one } - this.data.set(chunk, this.loaded); - this.loaded = willBeLoaded; this.onprogress(this.loaded, this.length >= 0 ? this.length : void(0)); }, readData: function PdfDataListener_readData() { - var data = this.data.subarray(this.position, this.loaded); - this.position = this.loaded; - return data; - }, - getData: function PdfDataListener_getData() { - var data = this.data; - if (this.loaded != data.length) - data = data.subarray(0, this.loaded); - delete this.data; // releasing temporary storage - return data; + var result = this.buffer; + this.buffer = null; + return result; }, finish: function PdfDataListener_finish() { this.isDataReady = true; if (this.oncompleteCallback) { - this.oncompleteCallback(this.getData()); + this.oncompleteCallback(this.readData()); } }, error: function PdfDataListener_error(errorCode) { @@ -232,7 +235,7 @@ PdfDataListener.prototype = { set oncomplete(value) { this.oncompleteCallback = value; if (this.isDataReady) { - value(this.getData()); + value(this.readData()); } if (this.errorCode) { value(null, this.errorCode); @@ -255,7 +258,14 @@ function ChromeActions(domWindow, contentDispositionFilename) { ChromeActions.prototype = { isInPrivateBrowsing: function() { - return PrivateBrowsingUtils.isWindowPrivate(this.domWindow); +//#if !MOZCENTRAL + if (!PrivateBrowsingUtils.isContentWindowPrivate) { + // pbu.isContentWindowPrivate was not supported prior Firefox 35. + // (https://bugzilla.mozilla.org/show_bug.cgi?id=1069059) + return PrivateBrowsingUtils.isWindowPrivate(this.domWindow); + } +//#endif + return PrivateBrowsingUtils.isContentWindowPrivate(this.domWindow); }, download: function(data, sendResponse) { var self = this; @@ -554,7 +564,9 @@ var RangedChromeActions = (function RangedChromeActionsClosure() { this.headers[aHeader] = aValue; } }; - originalRequest.visitRequestHeaders(httpHeaderVisitor); + if (originalRequest.visitRequestHeaders) { + originalRequest.visitRequestHeaders(httpHeaderVisitor); + } var self = this; var xhr_onreadystatechange = function xhr_onreadystatechange() { @@ -598,7 +610,7 @@ var RangedChromeActions = (function RangedChromeActionsClosure() { if (!this.streamingEnabled) { this.originalRequest.cancel(Cr.NS_BINDING_ABORTED); this.originalRequest = null; - data = this.dataListener.getData(); + data = this.dataListener.readData(); this.dataListener = null; } else { data = this.dataListener.readData(); @@ -874,8 +886,8 @@ PdfStreamConverter.prototype = { } catch (e) {} var rangeRequest = false; - var hash = aRequest.URI.ref; - if (isHttpRequest && !getBoolPref(PREF_PREFIX + '.disableRange', false)) { + var streamRequest = false; + if (isHttpRequest) { var contentEncoding = 'identity'; try { contentEncoding = aRequest.getResponseHeader('Content-Encoding'); @@ -886,13 +898,20 @@ PdfStreamConverter.prototype = { acceptRanges = aRequest.getResponseHeader('Accept-Ranges'); } catch (e) {} + var hash = aRequest.URI.ref; + var isPDFBugEnabled = getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false); rangeRequest = contentEncoding === 'identity' && acceptRanges === 'bytes' && aRequest.contentLength >= 0 && - hash.toLowerCase().indexOf('disablerange=true') < 0; + !getBoolPref(PREF_PREFIX + '.disableRange', false) && + (!isPDFBugEnabled || + hash.toLowerCase().indexOf('disablerange=true') < 0); + streamRequest = contentEncoding === 'identity' && + aRequest.contentLength >= 0 && + !getBoolPref(PREF_PREFIX + '.disableStream', false) && + (!isPDFBugEnabled || + hash.toLowerCase().indexOf('disablestream=true') < 0); } - var streamRequest = !getBoolPref(PREF_PREFIX + '.disableStream', false) && - hash.toLowerCase().indexOf('disablestream=true') < 0; aRequest.QueryInterface(Ci.nsIChannel); diff --git a/extensions/firefox/content/PdfjsChromeUtils.jsm b/extensions/firefox/content/PdfjsChromeUtils.jsm index f24f4abb4c4de..b830f3dfdb0c8 100644 --- a/extensions/firefox/content/PdfjsChromeUtils.jsm +++ b/extensions/firefox/content/PdfjsChromeUtils.jsm @@ -272,7 +272,18 @@ let PdfjsChromeUtils = { */ function PdfjsFindbarWrapper(aBrowser) { let tabbrowser = aBrowser.getTabBrowser(); - let tab = tabbrowser._getTabForBrowser(aBrowser); + let tab; +//#if MOZCENTRAL + tab = tabbrowser.getTabForBrowser(aBrowser); +//#else + if (tabbrowser.getTabForBrowser) { + tab = tabbrowser.getTabForBrowser(aBrowser); + } else { + // _getTabForBrowser is depreciated in Firefox 35, see + // https://bugzilla.mozilla.org/show_bug.cgi?id=1039500. + tab = tabbrowser._getTabForBrowser(aBrowser); + } +//#endif this._findbar = tabbrowser.getFindBar(tab); }; diff --git a/extensions/firefox/content/pdfjschildbootstrap.js b/extensions/firefox/content/pdfjschildbootstrap.js index c9758b3e51c84..737a49027a11a 100644 --- a/extensions/firefox/content/pdfjschildbootstrap.js +++ b/extensions/firefox/content/pdfjschildbootstrap.js @@ -15,7 +15,7 @@ * limitations under the License. */ /* jshint esnext:true */ -/* globals Components, PdfjsContentUtils, PdfJs */ +/* globals Components, PdfjsContentUtils, PdfJs, Services */ 'use strict'; @@ -24,11 +24,14 @@ * initializing our built-in version of pdfjs when running remote. */ +Components.utils.import('resource://gre/modules/Services.jsm'); Components.utils.import('resource://pdf.js/PdfJs.jsm'); Components.utils.import('resource://pdf.js/PdfjsContentUtils.jsm'); // init content utils shim pdfjs will use to access privileged apis. PdfjsContentUtils.init(); -// register various pdfjs factories that hook us into content loading. -PdfJs.updateRegistration(); +if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) { + // register various pdfjs factories that hook us into content loading. + PdfJs.updateRegistration(); +} diff --git a/extensions/firefox/tools/l10n.js b/extensions/firefox/tools/l10n.js index 4d7cf29a6e9b9..665d28b0443e4 100644 --- a/extensions/firefox/tools/l10n.js +++ b/extensions/firefox/tools/l10n.js @@ -123,7 +123,11 @@ // http://www.w3.org/International/questions/qa-scripts // Arabic, Hebrew, Farsi, Pashto, Urdu var rtlList = ['ar', 'he', 'fa', 'ps', 'ur']; - return (rtlList.indexOf(gLanguage) >= 0 ? 'rtl' : 'ltr'); + + // use the short language code for "full" codes like 'ar-sa' (issue 5440) + var shortCode = gLanguage.split('-')[0]; + + return (rtlList.indexOf(shortCode) >= 0) ? 'rtl' : 'ltr'; }, // translate an element or document fragment diff --git a/external/builder/builder.js b/external/builder/builder.js index 044741d88daf8..637e35d757a4f 100644 --- a/external/builder/builder.js +++ b/external/builder/builder.js @@ -123,6 +123,15 @@ function preprocessCSS(mode, source, destination) { deprecatedInMozcentral.test(line)); } + function expandImports(content, baseUrl) { + return content.replace(/^\s*@import\s+url\(([^\)]+)\);\s*$/gm, + function(all, url) { + var file = path.join(path.dirname(baseUrl), url); + var imported = fs.readFileSync(file, 'utf8').toString(); + return expandImports(imported, file); + }); + } + function removePrefixed(content, hasPrefixedFilter) { var lines = content.split(/\r?\n/g); var i = 0; @@ -168,14 +177,17 @@ function preprocessCSS(mode, source, destination) { return lines.join('\n'); } - if (mode !== 'firefox' && mode !== 'mozcentral') { + if (!mode) { throw new Error('Invalid CSS preprocessor mode'); } - var content = fs.readFileSync(source, 'utf8'); - var out = removePrefixed(content, - mode === 'mozcentral' ? hasPrefixedMozcentral : hasPrefixedFirefox); - fs.writeFileSync(destination, out); + var content = fs.readFileSync(source, 'utf8').toString(); + content = expandImports(content, source); + if (mode === 'mozcentral' || mode === 'firefox') { + content = removePrefixed(content, mode === 'mozcentral' ? + hasPrefixedMozcentral : hasPrefixedFirefox); + } + fs.writeFileSync(destination, content); } exports.preprocessCSS = preprocessCSS; diff --git a/external/webL10n/l10n.js b/external/webL10n/l10n.js index 37f3950c55e1a..9f7a0c94945e0 100644 --- a/external/webL10n/l10n.js +++ b/external/webL10n/l10n.js @@ -977,7 +977,11 @@ document.webL10n = (function(window, document, undefined) { // http://www.w3.org/International/questions/qa-scripts // Arabic, Hebrew, Farsi, Pashto, Urdu var rtlList = ['ar', 'he', 'fa', 'ps', 'ur']; - return (rtlList.indexOf(gLanguage) >= 0) ? 'rtl' : 'ltr'; + + // use the short language code for "full" codes like 'ar-sa' (issue 5440) + var shortCode = gLanguage.split('-')[0]; + + return (rtlList.indexOf(shortCode) >= 0) ? 'rtl' : 'ltr'; }, // translate an element or document fragment diff --git a/l10n/ast/viewer.properties b/l10n/ast/viewer.properties index fd5fb11db3c32..daa5cbf0ca8e3 100644 --- a/l10n/ast/viewer.properties +++ b/l10n/ast/viewer.properties @@ -105,3 +105,6 @@ document_properties_creation_date = Data de creación: document_properties_close = Zarrar document_properties_author = Autor: document_properties.title = Propiedaes del documentu… +attachments_label = Axuntos +attachments.title = Amosar axuntos +unexpected_response_error = Rempuesta inesperada del sirvidor. diff --git a/l10n/br/viewer.properties b/l10n/br/viewer.properties index de8063955719d..63657f0617644 100644 --- a/l10n/br/viewer.properties +++ b/l10n/br/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Fazi loading_error=Degouezhet ez eus bet ur fazi e-pad kargañ ar PDF. invalid_file_error=Restr PDF didalvoudek pe kontronet. missing_file_error=Restr PDF o vankout. +unexpected_response_error=Respont dic'hortoz a-berzh an dafariad # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/ca/chrome.properties b/l10n/ca/chrome.properties index 2373dbb02e1a8..11b7c6692b81a 100644 --- a/l10n/ca/chrome.properties +++ b/l10n/ca/chrome.properties @@ -14,6 +14,6 @@ # Chrome notification bar messages and buttons unsupported_feature=Aquest document PDF potser no es mostra correctament. -unsupported_feature_forms=Aquest document PDF conté formularis. L'emplenat de camps de formularis no està implementat. +unsupported_feature_forms=Aquest document PDF conté formularis. L'emplenament de camps de formularis no està implementat. open_with_different_viewer=Obre amb un altre visor open_with_different_viewer.accessKey=o diff --git a/l10n/cs/viewer.properties b/l10n/cs/viewer.properties index ce164ebd0ebe7..dd23193f61122 100644 --- a/l10n/cs/viewer.properties +++ b/l10n/cs/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Chyba loading_error=Při nahrávání PDF nastala chyba. invalid_file_error=Neplatný nebo chybný soubor PDF. missing_file_error=Chybí soubor PDF. +unexpected_response_error=Neočekávaná odpověď serveru. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/cy/viewer.properties b/l10n/cy/viewer.properties index 15d2656e069c9..14e3159fa12a4 100644 --- a/l10n/cy/viewer.properties +++ b/l10n/cy/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Gwall loading_error=Digwyddodd gwall wrth lwytho'r PDF. invalid_file_error=Ffeil PDF annilys neu llwgr. missing_file_error=Ffeil PDF coll. +unexpected_response_error=Ymateb annisgwyl gan y gweinydd. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/da/viewer.properties b/l10n/da/viewer.properties index f44262222c2fb..4c48bdae19973 100644 --- a/l10n/da/viewer.properties +++ b/l10n/da/viewer.properties @@ -32,8 +32,8 @@ zoom_in_label=Zoom ind zoom.title=Zoom print.title=Udskriv print_label=Udskriv -presentation_mode.title=Skift til præsentations-tilstand -presentation_mode_label=Præsentations-tilstand +presentation_mode.title=Skift til fuldskærmsvisning +presentation_mode_label=Fuldskærmsvisning open_file.title=Åbn fil open_file_label=Åbn download.title=Hent @@ -42,8 +42,8 @@ bookmark.title=Aktuel visning (kopier eller åbn i et nyt vindue) bookmark_label=Aktuel visning # Secondary toolbar and context menu -tools.title=Værktøj -tools_label=Værktøj +tools.title=Funktioner +tools_label=Funktioner first_page.title=Gå til første side first_page.label=Gå til første side first_page_label=Gå til første side diff --git a/l10n/en-GB/viewer.properties b/l10n/en-GB/viewer.properties index 64239d285eac3..40722f644ca1b 100644 --- a/l10n/en-GB/viewer.properties +++ b/l10n/en-GB/viewer.properties @@ -160,4 +160,4 @@ password_cancel=Cancel printing_not_supported=Warning: Printing is not fully supported by this browser. printing_not_ready=Warning: The PDF is not fully loaded for printing. web_fonts_disabled=Web fonts are disabled: unable to use embedded PDF fonts. -document_colors_disabled=PDF documents are not allowed to use their own colours: \'Allow pages to choose their own colours\' is deactivated in the browser. +document_colors_disabled=PDF documents are not allowed to use their own colours: 'Allow pages to choose their own colours' is deactivated in the browser. diff --git a/l10n/en-US/viewer.properties b/l10n/en-US/viewer.properties index c52020c5cab17..4a3a6aaa2ca81 100644 --- a/l10n/en-US/viewer.properties +++ b/l10n/en-US/viewer.properties @@ -140,6 +140,9 @@ page_scale_width=Page Width page_scale_fit=Page Fit page_scale_auto=Automatic Zoom page_scale_actual=Actual Size +# LOCALIZATION NOTE (page_scale_percent): "{{scale}}" will be replaced by a +# numerical scale value. +page_scale_percent={{scale}}% # Loading indicator messages loading_error_indicator=Error diff --git a/l10n/en-ZA/viewer.properties b/l10n/en-ZA/viewer.properties index 5588ff19597ae..6c063a4242c0f 100644 --- a/l10n/en-ZA/viewer.properties +++ b/l10n/en-ZA/viewer.properties @@ -89,6 +89,8 @@ toggle_sidebar.title=Toggle Sidebar toggle_sidebar_label=Toggle Sidebar outline.title=Show Document Outline outline_label=Document Outline +attachments.title=Show Attachments +attachments_label=Attachments thumbs.title=Show Thumbnails thumbs_label=Thumbnails findbar.title=Find in Document diff --git a/l10n/eo/viewer.properties b/l10n/eo/viewer.properties index 78b822943206a..ee1a58ab4064b 100644 --- a/l10n/eo/viewer.properties +++ b/l10n/eo/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Eraro loading_error=Okazis eraro dum la ŝargado de la PDF dosiero. invalid_file_error=Nevalida aŭ difektita PDF dosiero. missing_file_error=Mankas dosiero PDF. +unexpected_response_error=Neatendita respondo de servilo. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/es-AR/viewer.properties b/l10n/es-AR/viewer.properties index c931a101cb362..b5b07735b52a3 100644 --- a/l10n/es-AR/viewer.properties +++ b/l10n/es-AR/viewer.properties @@ -152,6 +152,7 @@ loading_error_indicator=Error loading_error=Ocurrió un error al cargar el PDF. invalid_file_error=Archivo PDF no válido o cocrrupto. missing_file_error=Archivo PDF faltante. +unexpected_response_error=Respuesta del servidor inesperada. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/es-CL/viewer.properties b/l10n/es-CL/viewer.properties index 9219cc9c154b7..c21e6f0702498 100644 --- a/l10n/es-CL/viewer.properties +++ b/l10n/es-CL/viewer.properties @@ -115,6 +115,7 @@ loading_error_indicator = Error loading_error = Ha ocurrido un error al cargar el PDF. invalid_file_error = Archivo PDF inválido o corrupto. missing_file_error=Falta el archivo PDF. +unexpected_response_error=Respuesta del servidor inesperada. text_annotation_type.alt=[{{type}} Anotación] password_label=Ingrese la contraseña para abrir este archivo PDF. diff --git a/l10n/es-ES/viewer.properties b/l10n/es-ES/viewer.properties index e0db6b00dc597..a8c244bc35087 100644 --- a/l10n/es-ES/viewer.properties +++ b/l10n/es-ES/viewer.properties @@ -98,6 +98,7 @@ loading_error_indicator = Error loading_error = Ocurrió un error al cargar el PDF. invalid_file_error = Fichero PDF no válido o corrupto. missing_file_error = No hay fichero PDF. +unexpected_response_error = Respuesta inesperada del servidor. text_annotation_type.alt = [Anotación {{type}}] password_label = Introduzca la contraseña para abrir este archivo PDF. password_invalid = Contraseña no válida. Vuelva a intentarlo. diff --git a/l10n/et/viewer.properties b/l10n/et/viewer.properties index 89a4a92a5138c..6eafcbba5ba25 100644 --- a/l10n/et/viewer.properties +++ b/l10n/et/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Viga loading_error=PDFi laadimisel esines viga. invalid_file_error=Vigane või rikutud PDF-fail. missing_file_error=PDF-fail puudub. +unexpected_response_error=Ootamatu vastus serverilt. # LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/fa/chrome.properties b/l10n/fa/chrome.properties index ca38b114fe662..07a9bd94a64a2 100644 --- a/l10n/fa/chrome.properties +++ b/l10n/fa/chrome.properties @@ -14,5 +14,6 @@ # Chrome notification bar messages and buttons unsupported_feature=این پروندۀ PDF ممکن است به‌طور صحیح نمایش داده نشود. +unsupported_feature_forms=این سند PDF حاوی فرم است. پر کردن فیلد های فرم اینجا پشتیبانی نمیشود. open_with_different_viewer=با یک نمایشگر دیگر نشان بده open_with_different_viewer.accessKey=o diff --git a/l10n/fa/viewer.properties b/l10n/fa/viewer.properties index 062f2c2d9c556..b75fee1350dcf 100644 --- a/l10n/fa/viewer.properties +++ b/l10n/fa/viewer.properties @@ -14,7 +14,9 @@ # Main toolbar buttons (tooltips and alt text for images) previous.title=صفحهٔ قبلی +previous_label=قبلی next.title=صفحهٔ بعدی +next_label=بعدی # LOCALIZATION NOTE (page_label, page_of): # These strings are concatenated to form the "Page: X of Y" string. @@ -40,14 +42,58 @@ bookmark.title=نمای فعلی (کپی کن، یا در پنجرۀ دیگری bookmark_label=نمای فعلی # Secondary toolbar and context menu +tools.title=ابزارها +tools_label=ابزارها +first_page.title=برو به اولین صفحه +first_page.label=برو یه اولین صفحه +first_page_label=برو به اولین صفحه +last_page.title=برو به آخرین صفحه +last_page.label=برو به آخرین صفحه +last_page_label=برو به آخرین صفحه +page_rotate_cw.title=چرخش ساعتگرد +page_rotate_cw.label=چرخش ساعتگرد +page_rotate_cw_label=چرخش ساعتگرد +page_rotate_ccw.title=چرخش پاد ساعتگرد +page_rotate_ccw.label=چرخش پاد ساعتگرد +page_rotate_ccw_label=چرخش پاد ساعتگرد + +hand_tool_enable.title=فعال سازی ابزار دست +hand_tool_enable_label=فعال سازی ابزار دست +hand_tool_disable.title=غیر‌فعال سازی ابزار دست +hand_tool_disable_label=غیر‌فعال سازی ابزار دست + +# Document properties dialog box +document_properties.title=خصوصیات سند... +document_properties_label=خصوصیات سند... +document_properties_file_name=نام فایل: +document_properties_file_size=حجم پرونده: +document_properties_kb={{size_kb}} کیلوبایت ({{size_b}} بایت) +document_properties_mb={{size_mb}} مگابایت ({{size_b}} بایت) +document_properties_title=عنوان: +document_properties_author=نویسنده: +document_properties_subject=موضوع: +document_properties_keywords=کلیدواژه‌ها: +document_properties_creation_date=تاریخ ایجاد: +document_properties_modification_date=تاریخ ویرایش: +document_properties_date_string={{date}}، {{time}} +document_properties_creator=ایجاد کننده: +document_properties_producer=ایجاد کننده PDF: +document_properties_version=نسخه PDF: +document_properties_page_count=تعداد صفحات: +document_properties_close=بستن # Tooltips and alt text for side panel toolbar buttons # (the _label strings are alt text for the buttons, the .title strings are # tooltips) +toggle_sidebar.title=باز و بسته کردن نوار کناری +toggle_sidebar_label=تغییرحالت نوارکناری outline.title=نمایش طرح نوشتار outline_label=طرح نوشتار +attachments.title=نمایش پیوست‌ها +attachments_label=پیوست‌ها thumbs.title=نمایش تصاویر بندانگشتی thumbs_label=تصاویر بندانگشتی +findbar.title=جستجو در سند findbar_label=پیدا کردن # Thumbnails panel item (tooltip and alt text for images) @@ -59,8 +105,15 @@ thumb_page_title=صفحه {{page}} thumb_page_canvas=تصویر بند‌ انگشتی صفحه {{page}} # Find panel button title and messages +find_label=جستجو: find_previous.title=پیدا کردن رخداد قبلی عبارت +find_previous_label=قبلی find_next.title=پیدا کردن رخداد بعدی عبارت +find_next_label=بعدی +find_highlight=برجسته و هایلایت کردن همه موارد +find_match_case_label=تطبیق کوچکی و بزرگی حروف +find_reached_top=به بالای صفحه رسیدیم، از پایین ادامه می‌دهیم +find_reached_bottom=به آخر صفحه رسیدیم، از بالا ادامه می‌دهیم find_not_found=عبارت پیدا نشد # Error panel labels @@ -69,6 +122,7 @@ error_less_info=اطلاعات کمتر error_close=بستن # LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be # replaced by the PDF.JS version and build ID. +error_version_info=‏PDF.js ورژن{{version}} ‏(ساخت: {{build}}) # LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an # english string describing the error. error_message=پیام: {{message}} @@ -90,13 +144,21 @@ page_scale_actual=اندازه واقعی‌ # Loading indicator messages loading_error_indicator=خطا loading_error=هنگام بارگیری پرونده (PDF) خطایی رخ داد. +invalid_file_error=پرونده PDF نامعتبر یامعیوب می‌باشد. +missing_file_error=پرونده PDF یافت نشد. +unexpected_response_error=پاسخ پیش بینی نشده سرور # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" text_annotation_type.alt=[{{type}} Annotation] +password_label=جهت باز کردن پرونده PDF گذرواژه را وارد نمائید. +password_invalid=گذرواژه نامعتبر. لطفا مجددا تلاش کنید. password_ok=تأیید password_cancel=انصراف printing_not_supported=هشدار: قابلیت چاپ به‌طور کامل در این مرورگر پشتیبانی نمی‌شود. +printing_not_ready=اخطار: پرونده PDF بطور کامل بارگیری نشده و امکان چاپ وجود ندارد. +web_fonts_disabled=فونت های تحت وب غیر فعال شده اند: امکان استفاده از نمایش دهنده داخلی PDF وجود ندارد. +document_colors_disabled=فایلهای PDF نمیتوانند که رنگ های خود را داشته باشند. لذا گزینه 'اجازه تغییر رنگ" در مرورگر غیر فعال شده است. diff --git a/l10n/fr/viewer.properties b/l10n/fr/viewer.properties index 41d1860cfd7e3..60269d10155fd 100644 --- a/l10n/fr/viewer.properties +++ b/l10n/fr/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Erreur loading_error=Une erreur s'est produite lors du chargement du fichier PDF. invalid_file_error=Fichier PDF invalide ou corrompu. missing_file_error=Fichier PDF manquant. +unexpected_response_error=Réponse inattendue du serveur. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/fy-NL/viewer.properties b/l10n/fy-NL/viewer.properties index 69c8d0bc18806..64cca2ab23bd0 100644 --- a/l10n/fy-NL/viewer.properties +++ b/l10n/fy-NL/viewer.properties @@ -152,6 +152,7 @@ loading_error_indicator=Flater loading_error=Der is in flater bard by it laden fan de PDF. invalid_file_error=Ynfalide of korruptearre PDF-bestân. missing_file_error=PDF-bestân ûntbrekt. +unexpected_response_error=Unferwacht tsjinnerantwurd. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/ga-IE/viewer.properties b/l10n/ga-IE/viewer.properties index 66c48760f62fb..4f3475ac914ab 100644 --- a/l10n/ga-IE/viewer.properties +++ b/l10n/ga-IE/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Earráid loading_error=Tharla earráid agus an cháipéis PDF á luchtú. invalid_file_error=Comhad neamhbhailí nó truaillithe PDF. missing_file_error=Comhad PDF ar iarraidh. +unexpected_response_error=Freagra ón bhfreastalaí gan súil leis. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/gd/viewer.properties b/l10n/gd/viewer.properties index f535f0660022c..a29b655caf69b 100644 --- a/l10n/gd/viewer.properties +++ b/l10n/gd/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Mearachd loading_error=Thachair mearachd rè luchdadh a' PDF. invalid_file_error=Faidhle PDF a tha mì-dhligheach no coirbte. missing_file_error=Faidhle PDF a tha a dhìth. +unexpected_response_error=Freagairt on fhrithealaiche ris nach robh dùil. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/he/chrome.properties b/l10n/he/chrome.properties index 0b1ea45a16109..f6a75b059314e 100644 --- a/l10n/he/chrome.properties +++ b/l10n/he/chrome.properties @@ -15,6 +15,6 @@ # Chrome notification bar messages and buttons unsupported_feature=יתכן שמסמך PDF זה לא יוצג כראוי. unsupported_feature_forms=מסמך PDF זה מכיל טפסים. מילוי שדות בטפסים אינו נתמך. -open_with_different_viewer=פתיחה בתוכנת צפייה שונה +open_with_different_viewer=פתיחה בתכנית צפייה שונה open_with_different_viewer.accessKey=פ diff --git a/l10n/he/viewer.properties b/l10n/he/viewer.properties index 078d401ab6a4a..6a1116f3a321f 100644 --- a/l10n/he/viewer.properties +++ b/l10n/he/viewer.properties @@ -78,6 +78,8 @@ toggle_sidebar.title=הצגה/הסתרה של סרגל הצד toggle_sidebar_label=הצגה/הסתרה של סרגל הצד outline.title=הצגת מתאר מסמך outline_label=מתאר מסמך +attachments.title=הצגת צרופות +attachments_label=צרופות thumbs.title=הצגת תצוגה מקדימה thumbs_label=תצוגה מקדימה findbar.title=חיפוש במסמך diff --git a/l10n/hr/viewer.properties b/l10n/hr/viewer.properties index 9c829b3ebc820..cb6219ae69f7e 100644 --- a/l10n/hr/viewer.properties +++ b/l10n/hr/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Greška loading_error=Došlo je do greške pri učitavanju PDF-a. invalid_file_error=Kriva ili oštećena PDF datoteka. missing_file_error=Nedostaje PDF datoteka. +unexpected_response_error=Neočekivani odgovor poslužitelja. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/hy-AM/viewer.properties b/l10n/hy-AM/viewer.properties index eda49756fdf69..6867aff1f976a 100644 --- a/l10n/hy-AM/viewer.properties +++ b/l10n/hy-AM/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Սխալ loading_error=Սխալ՝ PDF ֆայլը բացելիս։ invalid_file_error=Սխալ կամ բնասված PDF ֆայլ: missing_file_error=PDF ֆայլը բացակայում է: +unexpected_response_error=Սպասարկիչի անսպասելի պատասխան: # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/id/viewer.properties b/l10n/id/viewer.properties index 698a2ba118c8c..5892939ddb2b9 100644 --- a/l10n/id/viewer.properties +++ b/l10n/id/viewer.properties @@ -166,4 +166,4 @@ password_cancel=Batal printing_not_supported=Peringatan: Pencetakan tidak didukung secara lengkap pada peramban ini. printing_not_ready=Peringatan: Berkas PDF masih belum dimuat secara lengkap untuk dapat dicetak. web_fonts_disabled=Font web dinonaktifkan: tidak dapat menggunakan font PDF yang tersemat. -document_colors_disabled=Dokumen PDF tidak diizinkan untuk menggunakan warnanya sendiri karena setelan \'Izinkan laman memilih warna sendiri\’ dinonaktifkan pada pengaturan. +document_colors_disabled=Dokumen PDF tidak diizinkan untuk menggunakan warnanya sendiri karena setelan 'Izinkan laman memilih warna sendiri’ dinonaktifkan pada pengaturan. diff --git a/l10n/is/viewer.properties b/l10n/is/viewer.properties index 9c81b24c9529f..b15554b3abbbd 100644 --- a/l10n/is/viewer.properties +++ b/l10n/is/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Villa loading_error=Villa kom upp við að hlaða inn PDF. invalid_file_error=Ógild eða skemmd PDF skrá. missing_file_error=Vantar PDF skrá. +unexpected_response_error=Óvænt svar frá netþjóni. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/it/viewer.properties b/l10n/it/viewer.properties index 7457e216417a6..a52b5a8c6e0d1 100644 --- a/l10n/it/viewer.properties +++ b/l10n/it/viewer.properties @@ -98,6 +98,7 @@ loading_error_indicator = Errore loading_error = Si è verificato un errore durante il caricamento del PDF. invalid_file_error = File PDF non valido o danneggiato. missing_file_error = File PDF non disponibile. +unexpected_response_error = Risposta imprevista del server text_annotation_type.alt = [Annotazione: {{type}}] password_label = Inserire la password per aprire questo file PDF. password_invalid = Password non corretta. Riprovare. diff --git a/l10n/kk/viewer.properties b/l10n/kk/viewer.properties index 7d02ae5998090..f823515520551 100644 --- a/l10n/kk/viewer.properties +++ b/l10n/kk/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Қате loading_error=PDF жүктеу кезінде қате кетті. invalid_file_error=Зақымдалған немесе қате PDF файл. missing_file_error=PDF файлы жоқ. +unexpected_response_error=Сервердің күтпеген жауабы. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/km/viewer.properties b/l10n/km/viewer.properties index 45acd63cf5bf5..8591f3c869da5 100644 --- a/l10n/km/viewer.properties +++ b/l10n/km/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=កំហុស loading_error=មាន​កំហុស​បាន​កើតឡើង​ពេល​កំពុង​ផ្ទុក PDF ។ invalid_file_error=ឯកសារ PDF ខូច ឬ​មិន​ត្រឹមត្រូវ ។ missing_file_error=បាត់​ឯកសារ PDF +unexpected_response_error=ការ​ឆ្លើយ​តម​ម៉ាស៊ីន​មេ​ដែល​មិន​បាន​រំពឹង។ # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/lv/viewer.properties b/l10n/lv/viewer.properties index f46f13665ccbd..84a12611095ab 100644 --- a/l10n/lv/viewer.properties +++ b/l10n/lv/viewer.properties @@ -156,5 +156,5 @@ password_cancel=Atcelt printing_not_supported=Uzmanību: Drukāšana no šī pārlūka darbojas tikai daļēji. web_fonts_disabled=Tīmekļa fonti nav aktivizēti: Nevar iegult PDF fontus. printing_not_ready=Uzmanību: PDF nav pilnībā ielādēts drukāšanai. -document_colors_disabled=PDF dokumentiem nav atļauts izmantot pašiem savas krāsas: \'Atļaut lapām izvēlēties pašām savas krāsas\' ir deaktivēts pārlūkā. +document_colors_disabled=PDF dokumentiem nav atļauts izmantot pašiem savas krāsas: „Atļaut lapām izvēlēties pašām savas krāsas“ ir deaktivēts pārlūkā. diff --git a/l10n/mai/viewer.properties b/l10n/mai/viewer.properties index 1775dbfd14ff2..ea49e3546a0c3 100644 --- a/l10n/mai/viewer.properties +++ b/l10n/mai/viewer.properties @@ -42,10 +42,45 @@ bookmark.title=मोजुदा दृश्य (नव विंडोमे bookmark_label=वर्तमान दृश्य # Secondary toolbar and context menu +tools.title=अओजार +tools_label=अओजार +first_page.title=प्रथम पृष्ठ पर जाउ +first_page.label=प्रथम पृष्ठ पर जाउ +first_page_label=प्रथम पृष्ठ पर जाउ +last_page.title=अंतिम पृष्ठ पर जाउ +last_page.label=अंतिम पृष्ठ पर जाउ +last_page_label=अंतिम पृष्ठ पर जाउ +page_rotate_cw.title=घड़ीक दिशा मे घुमाउ +page_rotate_cw.label=घड़ीक दिशा मे घुमाउ +page_rotate_cw_label=घड़ीक दिशा मे घुमाउ +page_rotate_ccw.title=घड़ीक दिशा सँ उनटा घुमाउ +page_rotate_ccw.label=घड़ीक दिशा सँ उनटा घुमाउ +page_rotate_ccw_label=घड़ीक दिशा सँ उनटा घुमाउ +hand_tool_enable.title=हाथ अओजार सक्रिय करू +hand_tool_enable_label=हाथ अओजार सक्रिय करू +hand_tool_disable.title=हाथ अओजार निष्क्रिय कएनाइ +hand_tool_disable_label=हाथ अओजार निष्क्रिय कएनाइ # Document properties dialog box +document_properties.title=दस्तावेज़ विशेषता... +document_properties_label=दस्तावेज़ विशेषता... +document_properties_file_name=फाइल नाम: +document_properties_file_size=फ़ाइल आकार: +document_properties_kb={{size_kb}} KB ({{size_b}} बाइट) +document_properties_mb={{size_mb}} MB ({{size_b}} बाइट) document_properties_title=शीर्षक: +document_properties_author=लेखकः +document_properties_subject=विषय +document_properties_keywords=बीजशब्द +document_properties_creation_date=निर्माण तिथि: +document_properties_modification_date=संशोधन दिनांक: +document_properties_date_string=तिथि/समय +document_properties_creator=सृजक: +document_properties_producer=PDF उत्पादक: +document_properties_version=PDF संस्करण: +document_properties_page_count=पृष्ठ गिनती: +document_properties_close=बन्न करू # Tooltips and alt text for side panel toolbar buttons # (the _label strings are alt text for the buttons, the .title strings are @@ -54,6 +89,8 @@ toggle_sidebar.title=स्लाइडर टागल toggle_sidebar_label=स्लाइडर टागल outline.title=दस्तावेज आउटलाइन देखाउ outline_label=दस्तावेज खाका +attachments.title=संलग्नक देखाबू +attachments_label=संलग्नक thumbs.title=लघु-छवि देखाउ thumbs_label=लघु छवि findbar.title=दस्तावेजमे ढूँढू @@ -82,6 +119,7 @@ find_not_found=वाकींश नहि भेटल # Error panel labels error_more_info=बेसी सूचना error_less_info=कम सूचना +error_close=बन्न करू # LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be # replaced by the PDF.JS version and build ID. error_version_info=PDF.js v{{version}} (build: {{build}}) @@ -114,8 +152,12 @@ missing_file_error=अनुपस्थित PDF फाइल. # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" text_annotation_type.alt=[{{type}} Annotation] +password_label=एहि पीडीएफ फ़ाइल केँ खोलबाक लेल कृपया कूटशब्द भरू. +password_invalid=अवैध कूटशब्द, कृपया फिनु कोशिश करू. +password_ok=बेस password_cancel=रद्द करू\u0020 printing_not_supported=चेतावनी: ई ब्राउजर पर छपाइ पूर्ण तरह सँ समर्थित नहि अछि. printing_not_ready=चेतावनी: पीडीएफ छपाइक लेल पूर्ण तरह सँ लोड नहि अछि. web_fonts_disabled=वेब फॉन्ट्स निष्क्रिय अछि: अंतःस्थापित PDF फान्टसक उपयोगमे असमर्थ. +document_colors_disabled=PDF दस्तावेज़ हुकर अपन रंग केँ उपयोग करबाक लेल अनुमति प्राप्त नहि अछि: 'पृष्ठ केँ हुकर अपन रंग केँ चुनबाक लेल स्वीकृति दिअ जे ओ ओहि ब्राउज़र मे निष्क्रिय अछि. diff --git a/l10n/nl/viewer.properties b/l10n/nl/viewer.properties index d5ed70aeb50f9..4063e98073eea 100644 --- a/l10n/nl/viewer.properties +++ b/l10n/nl/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Fout loading_error=Er is een fout opgetreden bij het laden van de PDF. invalid_file_error=Ongeldig of beschadigd PDF-bestand. missing_file_error=PDF-bestand ontbreekt. +unexpected_response_error=Onverwacht serverantwoord. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/nn-NO/viewer.properties b/l10n/nn-NO/viewer.properties index 7b016f3262999..6c44bae703cf0 100644 --- a/l10n/nn-NO/viewer.properties +++ b/l10n/nn-NO/viewer.properties @@ -160,4 +160,4 @@ password_cancel=Avbryt printing_not_supported=Åtvaring: Utskrift er ikkje fullstendig støtta av denne nettlesaren. printing_not_ready=Åtvaring: PDF ikkje fullstendig innlasta for utskrift. web_fonts_disabled=Vev-fontar er slått av: Kan ikkje bruka innbundne PDF-fontar. -document_colors_disabled=PDF-dokument har ikkje løyve til å bruka eigne fargar: \'Tillat sider å velja eigne fargar\' er slått av i nettlesaren. +document_colors_disabled=PDF-dokument har ikkje løyve til å bruka eigne fargar: 'Tillat sider å velja eigne fargar' er slått av i nettlesaren. diff --git a/l10n/or/viewer.properties b/l10n/or/viewer.properties index 4706f82e90ebc..39d296275d2d4 100644 --- a/l10n/or/viewer.properties +++ b/l10n/or/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=ତ୍ରୁଟି loading_error=PDF ଧାରଣ କରିବା ସମୟରେ ଏକ ତ୍ରୁଟି ଘଟିଲା। invalid_file_error=ଅବୈଧ କିମ୍ବା ତ୍ରୁଟିଯୁକ୍ତ PDF ଫାଇଲ। missing_file_error=ହଜିଯାଇଥିବା PDF ଫାଇଲ। +unexpected_response_error=ଅପ୍ରତ୍ୟାଶିତ ସର୍ଭର ଉତ୍ତର। # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/pl/viewer.properties b/l10n/pl/viewer.properties index d603cfee95622..f85397f51180c 100644 --- a/l10n/pl/viewer.properties +++ b/l10n/pl/viewer.properties @@ -129,11 +129,11 @@ page_scale_fit=Dopasowanie strony page_scale_auto=Skala automatyczna page_scale_actual=Rozmiar rzeczywisty -# Loading indicator messages loading_error_indicator=Błąd loading_error=Podczas wczytywania dokumentu PDF wystąpił błąd. invalid_file_error=Nieprawidłowy lub uszkodzony plik PDF. missing_file_error=Brak pliku PDF. +unexpected_response_error=Nieoczekiwana odpowiedź serwera. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/pt-BR/viewer.properties b/l10n/pt-BR/viewer.properties index ec40786f2ecde..8be158dada466 100644 --- a/l10n/pt-BR/viewer.properties +++ b/l10n/pt-BR/viewer.properties @@ -67,8 +67,8 @@ document_properties.title=Propriedades do documento… document_properties_label=Propriedades do documento… document_properties_file_name=Nome do arquivo: document_properties_file_size=Tamanho do arquivo: -document_properties_kb={{size_kb}}\u202fKB ({{size_b}} bytes) -document_properties_mb={{size_mb}}\u202fMB ({{size_b}} bytes) +document_properties_kb={{size_kb}} KB ({{size_b}} bytes) +document_properties_mb={{size_mb}} MB ({{size_b}} bytes) document_properties_title=Título: document_properties_author=Autor: document_properties_subject=Assunto: diff --git a/l10n/pt-PT/viewer.properties b/l10n/pt-PT/viewer.properties index 43ec85baa0bb5..20e2fbf14e69c 100644 --- a/l10n/pt-PT/viewer.properties +++ b/l10n/pt-PT/viewer.properties @@ -160,4 +160,4 @@ password_cancel=Cancelar printing_not_supported=Aviso: a impressão não é totalmente suportada por este navegador. printing_not_ready=Aviso: o PDF ainda não está totalmente carregado. web_fonts_disabled=Os tipos de letra web estão desativados: não é possível utilizar os tipos de letra PDF incorporados. -document_colors_disabled=Os documentos PDF não permitem a utilização das suas próprias cores: \'Autorizar as páginas a escolher as suas próprias cores\' está desativado no navegador. +document_colors_disabled=Os documentos PDF não permitem a utilização das suas próprias cores: Autorizar as páginas a escolher as suas próprias cores está desativado no navegador. diff --git a/l10n/ro/viewer.properties b/l10n/ro/viewer.properties index 20d2d95f7fe1d..45c79eb21b25f 100644 --- a/l10n/ro/viewer.properties +++ b/l10n/ro/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Eroare loading_error=A intervenit o eroare la încărcarea fișierului PDF. invalid_file_error=Fișier PDF invalid sau deteriorat. missing_file_error=Fișier PDF lipsă. +unexpected_response_error=Răspuns neașteptat de la server. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/ru/viewer.properties b/l10n/ru/viewer.properties index b2de0fe69dd9c..1b270c97c0853 100644 --- a/l10n/ru/viewer.properties +++ b/l10n/ru/viewer.properties @@ -98,6 +98,7 @@ loading_error_indicator = Ошибка loading_error = При загрузке PDF произошла ошибка. invalid_file_error = Некорректный или повреждённый PDF-файл. missing_file_error = PDF-файл отсутствует. +unexpected_response_error = Неожиданный ответ сервера. text_annotation_type.alt = [Аннотация {{type}}] password_label = Введите пароль, чтобы открыть этот PDF-файл. password_invalid = Неверный пароль. Пожалуйста, попробуйте снова. diff --git a/l10n/sk/viewer.properties b/l10n/sk/viewer.properties index 13da450b84e44..3349e3b726a45 100644 --- a/l10n/sk/viewer.properties +++ b/l10n/sk/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Chyba loading_error=Počas načítavania dokumentu PDF sa vyskytla chyba. invalid_file_error=Neplatný alebo poškodený súbor PDF. missing_file_error=Chýbajúci súbor PDF. +unexpected_response_error=Neočakávaná odpoveď zo servera. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/sl/viewer.properties b/l10n/sl/viewer.properties index 5e091078ecf01..501bb638503d6 100644 --- a/l10n/sl/viewer.properties +++ b/l10n/sl/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Napaka loading_error=Med nalaganjem datoteke PDF je prišlo do napake. invalid_file_error=Neveljavna ali pokvarjena datoteka PDF. missing_file_error=Ni datoteke PDF. +unexpected_response_error=Nepričakovan odgovor strežnika. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/sq/viewer.properties b/l10n/sq/viewer.properties index 79cd381d4c118..e88b7568e988c 100644 --- a/l10n/sq/viewer.properties +++ b/l10n/sq/viewer.properties @@ -144,6 +144,7 @@ loading_error_indicator=Gabim loading_error=Ndodhi një gabim gjatë ngarkimit të PDF-së. invalid_file_error=Kartelë PDF e pavlefshme ose e dëmtuar. missing_file_error=Kartelë PDF që mungon. +unexpected_response_error=Përgjigje e papritur nga shërbyesi. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/sr/chrome.properties b/l10n/sr/chrome.properties index 426b18b037011..107fdd54c8daf 100644 --- a/l10n/sr/chrome.properties +++ b/l10n/sr/chrome.properties @@ -13,7 +13,7 @@ # limitations under the License. # Chrome notification bar messages and buttons -unsupported_feature=Ови PDF документи можда нису приказани исправно. +unsupported_feature=Овај PDF докуменат можда није приказан исправно. unsupported_feature_forms=Овај PDF докуменат садржи форме. Попуњавање поља форме није подржано. open_with_different_viewer=Отвори са другим приказивачем open_with_different_viewer.accessKey=О diff --git a/l10n/sr/viewer.properties b/l10n/sr/viewer.properties index f91a33be019a5..33a1368985041 100644 --- a/l10n/sr/viewer.properties +++ b/l10n/sr/viewer.properties @@ -143,16 +143,17 @@ page_scale_actual=Стварна величина # Loading indicator messages loading_error_indicator=Грешка -loading_error=Дошло је до грешке приликом учитавање PDF. +loading_error=Дошло је до грешке приликом учитавања PDF-а. invalid_file_error=PDF датотека је оштећена или је неисправна. missing_file_error=PDF датотека није пронађена. +unexpected_response_error=Неочекиван одговор од сервера. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" text_annotation_type.alt=[{{type}} коментар] -password_label=_Унесите лозинку да бисте отворили овај PDF докуменат. +password_label=Унесите лозинку да бисте отворили овај PDF докуменат. password_invalid=Неисправна лозинка. Покушајте поново. password_ok=У реду password_cancel=Откажи @@ -160,4 +161,4 @@ password_cancel=Откажи printing_not_supported=Упозорење: Штампање није у потпуности подржано у овом прегледачу. printing_not_ready=Упозорење: PDF није у потпуности учитан за штампу. web_fonts_disabled=Веб фонтови су онемогућени: не могу користити уграђене PDF фонтове. -document_colors_disabled=PDF документи не могу да користе сопствене боје: “Дозволи страницама да одаберу своје боје” је деактивирано у прегледачу. +document_colors_disabled=PDF документи не могу да користе сопствене боје: “Дозволи страницама да изаберу своје боје” је деактивирано у прегледачу. diff --git a/l10n/sv-SE/viewer.properties b/l10n/sv-SE/viewer.properties index a88edbd4f13ba..5e7b20d60f645 100644 --- a/l10n/sv-SE/viewer.properties +++ b/l10n/sv-SE/viewer.properties @@ -140,12 +140,16 @@ page_scale_width=Sidbredd page_scale_fit=Anpassa sida page_scale_auto=Automatisk zoom page_scale_actual=Verklig storlek +# LOCALIZATION NOTE (page_scale_percent): "{{scale}}" will be replaced by a +# numerical scale value. +page_scale_percent={{scale}} % # Loading indicator messages loading_error_indicator=Fel loading_error=Ett fel uppstod vid laddning av PDF-filen. invalid_file_error=Ogiltig eller korrupt PDF-fil. missing_file_error=Saknad PDF-fil. +unexpected_response_error=Oväntat svar från servern. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/ta/viewer.properties b/l10n/ta/viewer.properties index 8643a688d304f..c5739a397d38d 100644 --- a/l10n/ta/viewer.properties +++ b/l10n/ta/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=பிழை loading_error=PDF ஐ ஏற்றும் போது ஒரு பிழை ஏற்பட்டது. invalid_file_error=செல்லுபடியாகாத அல்லது சிதைந்த PDF கோப்பு. missing_file_error=PDF கோப்பு காணவில்லை. +unexpected_response_error=சேவகன் பதில் எதிர்பாரதது. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/te/viewer.properties b/l10n/te/viewer.properties index da549f6e7f121..3ccecf2d6ace6 100644 --- a/l10n/te/viewer.properties +++ b/l10n/te/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=దోషం loading_error=PDF లోడవుచున్నప్పుడు వొక దోషం యెదురైంది. invalid_file_error=చెల్లని లేదా పాడైన PDF ఫైలు. missing_file_error=దొరకని PDF ఫైలు. +unexpected_response_error=అనుకోని సేవిక స్పందన. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/tr/viewer.properties b/l10n/tr/viewer.properties index 6eefe27602ce7..c1761596da00a 100644 --- a/l10n/tr/viewer.properties +++ b/l10n/tr/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=Hata loading_error=PDF yüklenirken bir hata oluştu. invalid_file_error=Geçersiz veya bozulmuş PDF dosyası. missing_file_error=PDF dosyası eksik. +unexpected_response_error=Beklenmeyen sunucu yanıtı. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/xh/viewer.properties b/l10n/xh/viewer.properties index 89c9e8d230ef1..cebcb5c0e58c9 100644 --- a/l10n/xh/viewer.properties +++ b/l10n/xh/viewer.properties @@ -122,7 +122,7 @@ error_less_info=Inkcazelo Encinane error_close=Vala # LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be # replaced by the PDF.JS version and build ID. -error_version_info=I-PDF.js v{{uhlelo}} (yakha: {{yakha}}) +error_version_info=I-PDF.js v{{version}} (yakha: {{build}}) # LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an # english string describing the error. error_message=Umyalezo: {{message}} @@ -146,6 +146,7 @@ loading_error_indicator=Imposiso loading_error=Imposiso yenzekile xa kulayishwa i-PDF. invalid_file_error=Ifayile ye-PDF engeyiyo okanye eyonakalisiweyo. missing_file_error=Ifayile ye-PDF edukileyo. +unexpected_response_error=Impendulo yeseva engalindelekanga. # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/zh-CN/viewer.properties b/l10n/zh-CN/viewer.properties index 878c5f210d5fd..445d2aafa788d 100644 --- a/l10n/zh-CN/viewer.properties +++ b/l10n/zh-CN/viewer.properties @@ -146,6 +146,7 @@ loading_error_indicator=错误 loading_error=载入PDF时发生错误。 invalid_file_error=无效或损坏的PDF文件。 missing_file_error=缺少PDF文件。 +unexpected_response_error=意外的服务器响应。 # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/l10n/zh-TW/viewer.properties b/l10n/zh-TW/viewer.properties index 80b88142d211f..d260514b73ee5 100644 --- a/l10n/zh-TW/viewer.properties +++ b/l10n/zh-TW/viewer.properties @@ -146,7 +146,7 @@ loading_error_indicator=錯誤 loading_error=載入 PDF 時發生錯誤。 invalid_file_error=無效或毀損的 PDF 檔案。 missing_file_error=找不到 PDF 檔案。 -unexpected_response_error=伺服器傳回未預期的回應。 +unexpected_response_error=伺服器回應未預期的內容。 # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in diff --git a/make.js b/make.js index e257c11a9fd4d..3a4456fec1b5b 100644 --- a/make.js +++ b/make.js @@ -49,7 +49,8 @@ var ROOT_DIR = __dirname + '/', // absolute path to project's root GH_PAGES_DIR = BUILD_DIR + 'gh-pages/', GENERIC_DIR = BUILD_DIR + 'generic/', MINIFIED_DIR = BUILD_DIR + 'minified/', - SINGLE_FILE_DIR = BUILD_DIR + '/singlefile/', + SINGLE_FILE_DIR = BUILD_DIR + 'singlefile/', + COMPONENTS_DIR = BUILD_DIR + 'components/', REPO = 'git@github.com:mozilla/pdf.js.git', MOZCENTRAL_PREF_PREFIX = 'pdfjs', FIREFOX_PREF_PREFIX = 'extensions.uriloader@pdf.js', @@ -65,7 +66,8 @@ var DEFINES = { B2G: false, CHROME: false, MINIFIED: false, - SINGLE_FILE: false + SINGLE_FILE: false, + COMPONENTS: false }; // @@ -126,7 +128,6 @@ target.generic = function() { [COMMON_WEB_FILES, GENERIC_DIR + '/web'], ['LICENSE', GENERIC_DIR], ['external/webL10n/l10n.js', GENERIC_DIR + '/web'], - ['web/viewer.css', GENERIC_DIR + '/web'], ['web/compatibility.js', GENERIC_DIR + '/web'], ['web/compressed.tracemonkey-pldi-09.pdf', GENERIC_DIR + '/web'], ['external/bcmaps/*', GENERIC_DIR + '/web/cmaps/'], @@ -135,11 +136,53 @@ target.generic = function() { preprocess: [ [BUILD_TARGETS, GENERIC_DIR + BUILD_DIR], [COMMON_WEB_FILES_PREPROCESS, GENERIC_DIR + '/web'] + ], + preprocessCSS: [ + ['generic', 'web/viewer.css', + GENERIC_DIR + '/web/viewer.css'] ] }; builder.build(setup); cleanupJSSource(GENERIC_DIR + '/web/viewer.js'); + cleanupCSSSource(GENERIC_DIR + '/web/viewer.css'); +}; + +target.components = function() { + cd(ROOT_DIR); + echo(); + echo('### Creating generic components'); + + rm('-rf', COMPONENTS_DIR); + mkdir('-p', COMPONENTS_DIR); + mkdir('-p', COMPONENTS_DIR + 'images'); + + var defines = builder.merge(DEFINES, {COMPONENTS: true}); + + var COMPONENTS_IMAGES = [ + 'web/images/annotation-*.svg', + 'web/images/loading-icon.gif', + 'web/images/shadow.png', + 'web/images/texture.png', + ]; + + var setup = { + defines: defines, + copy: [ + [COMPONENTS_IMAGES, COMPONENTS_DIR + 'images'], + ['web/compatibility.js', COMPONENTS_DIR], + ], + preprocess: [ + ['web/pdf_viewer.component.js', COMPONENTS_DIR + 'pdf_viewer.js'], + ], + preprocessCSS: [ + ['components', 'web/pdf_viewer.css', COMPONENTS_DIR + 'pdf_viewer.css'], + ] + }; + builder.build(setup); + + cleanupJSSource(COMPONENTS_DIR + 'pdf_viewer.js'); + cleanupCSSSource(COMPONENTS_DIR + 'pdf_viewer.css'); }; target.jsdoc = function() { @@ -229,6 +272,7 @@ target.web = function() { target.dist = function() { target.generic(); target.singlefile(); + target.components(); var DIST_DIR = BUILD_DIR + 'dist/'; var DIST_REPO_URL = 'https://github.com/mozilla/pdfjs-dist'; @@ -258,7 +302,7 @@ target.dist = function() { mkdir('-p', DIST_DIR + 'web/'); cp('-R', [ - GENERIC_DIR + 'web/compatibility.js', + COMPONENTS_DIR + '*', ], DIST_DIR + 'web/'); echo(); @@ -557,6 +601,16 @@ function cleanupJSSource(file) { content.to(file); } +function cleanupCSSSource(file) { + var content = cat(file); + + // Strip out all license headers in the middle. + var reg = /\n\/\* Copyright(.|\n)*?Mozilla Foundation(.|\n)*?\*\//g; + content = content.replace(reg, ''); + + content.to(file); +} + // // make minified // Builds the minified production viewer that should be compatible with most @@ -588,7 +642,6 @@ target.minified = function() { defines: defines, copy: [ [COMMON_WEB_FILES, MINIFIED_DIR + '/web'], - ['web/viewer.css', MINIFIED_DIR + '/web'], ['web/compressed.tracemonkey-pldi-09.pdf', MINIFIED_DIR + '/web'], ['external/bcmaps/*', MINIFIED_DIR + '/web/cmaps'], ['web/locale', MINIFIED_DIR + '/web'] @@ -596,10 +649,16 @@ target.minified = function() { preprocess: [ [BUILD_TARGETS, MINIFIED_DIR + BUILD_DIR], [COMMON_WEB_FILES_PREPROCESS, MINIFIED_DIR + '/web'] + ], + preprocessCSS: [ + ['minified', 'web/viewer.css', + MINIFIED_DIR + '/web/viewer.css'] ] }; builder.build(setup); + cleanupCSSSource(MINIFIED_DIR + '/web/viewer.css'); + var viewerFiles = [ 'web/compatibility.js', 'external/webL10n/l10n.js', @@ -750,6 +809,7 @@ target.firefox = function() { cleanupJSSource(FIREFOX_BUILD_CONTENT_DIR + '/web/viewer.js'); cleanupJSSource(FIREFOX_BUILD_DIR + 'bootstrap.js'); cleanupJSSource(FIREFOX_BUILD_CONTENT_DIR + 'PdfjsChromeUtils.jsm'); + cleanupCSSSource(FIREFOX_BUILD_CONTENT_DIR + '/web/viewer.css'); // Remove '.DS_Store' and other hidden files find(FIREFOX_BUILD_DIR).forEach(function(file) { @@ -872,6 +932,7 @@ target.mozcentral = function() { cleanupJSSource(MOZCENTRAL_CONTENT_DIR + '/web/viewer.js'); cleanupJSSource(MOZCENTRAL_CONTENT_DIR + '/PdfJs.jsm'); cleanupJSSource(MOZCENTRAL_CONTENT_DIR + '/PdfjsChromeUtils.jsm'); + cleanupCSSSource(MOZCENTRAL_CONTENT_DIR + '/web/viewer.css'); // Remove '.DS_Store' and other hidden files find(MOZCENTRAL_DIR).forEach(function(file) { @@ -982,18 +1043,22 @@ target.chromium = function() { CHROME_BUILD_DIR], ['extensions/chromium/pageAction/*.*', CHROME_BUILD_DIR + '/pageAction'], ['external/webL10n/l10n.js', CHROME_BUILD_CONTENT_DIR + '/web'], - ['web/viewer.css', CHROME_BUILD_CONTENT_DIR + '/web'], ['external/bcmaps/*', CHROME_BUILD_CONTENT_DIR + '/web/cmaps'], ['web/locale', CHROME_BUILD_CONTENT_DIR + '/web'] ], preprocess: [ [BUILD_TARGETS, CHROME_BUILD_CONTENT_DIR + BUILD_DIR], [COMMON_WEB_FILES_PREPROCESS, CHROME_BUILD_CONTENT_DIR + '/web'] + ], + preprocessCSS: [ + ['chrome', 'web/viewer.css', + CHROME_BUILD_CONTENT_DIR + '/web/viewer.css'] ] }; builder.build(setup); cleanupJSSource(CHROME_BUILD_CONTENT_DIR + '/web/viewer.js'); + cleanupCSSSource(CHROME_BUILD_CONTENT_DIR + '/web/viewer.css'); // Update the build version number sed('-i', /PDFJSSCRIPT_VERSION/, VERSION, diff --git a/package.json b/package.json index 1485c928c0290..cc5fba87f76ff 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,12 @@ "name": "pdf.js", "version": "0.8.0", "devDependencies": { - "jsdoc": "~3.3.0", - "jshint": "2.4.x", - "wintersmith": "2.0.x", - "moment": "2.3.x", - "underscore": "1.4.x", - "typogr": "0.5.x", + "jsdoc": "3.3.0-alpha9", + "jshint": "~2.4.0", + "wintersmith": "~2.0.0", + "moment": "~2.3.0", + "underscore": "~1.4.0", + "typogr": "~0.5.0", "yargs": "~1.2.1" }, "scripts": { diff --git a/pdfjs.config b/pdfjs.config index 5d04e200a48ef..38e5cd059630c 100644 --- a/pdfjs.config +++ b/pdfjs.config @@ -1,6 +1,6 @@ { - "betaVersion": "1.0.712", - "stableVersion": "1.0.473", + "betaVersion": "1.0.907", + "stableVersion": "1.0.712", "baseVersion": "d17d91e1f8b79bf6a542459d058277d891c39a83", "versionPrefix": "1.0." } \ No newline at end of file diff --git a/src/core/annotation.js b/src/core/annotation.js index 94ef131d9c56e..a7ae2ec452a83 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -375,7 +375,7 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() { var name = namedItem.get('T'); if (name) { fieldName.unshift(stringToPDFString(name)); - } else { + } else if (parent && ref) { // The field name is absent, that means more than one field // with the same name may exist. Replacing the empty name // with the '`' plus index in the parent's 'Kids' array. diff --git a/src/core/fonts.js b/src/core/fonts.js index 1cee3e9d35ea0..f84feb6383ab8 100644 --- a/src/core/fonts.js +++ b/src/core/fonts.js @@ -2485,6 +2485,12 @@ var Font = (function FontClosure() { for (var code in GlyphMapForStandardFonts) { map[+code] = GlyphMapForStandardFonts[code]; } + var isIdentityUnicode = this.toUnicode instanceof IdentityToUnicodeMap; + if (!isIdentityUnicode) { + this.toUnicode.forEach(function(charCode, unicodeCharCode) { + map[+charCode] = unicodeCharCode; + }); + } this.toFontChar = map; this.toUnicode = new ToUnicodeMap(map); } else if (/Symbol/i.test(fontName)) { @@ -2496,6 +2502,13 @@ var Font = (function FontClosure() { } this.toFontChar[charCode] = fontChar; } + for (charCode in properties.differences) { + fontChar = GlyphsUnicode[properties.differences[charCode]]; + if (!fontChar) { + continue; + } + this.toFontChar[charCode] = fontChar; + } } else if (/Dingbats/i.test(fontName)) { var dingbats = Encodings.ZapfDingbatsEncoding; for (charCode in dingbats) { diff --git a/src/core/function.js b/src/core/function.js index d8b7af7d5f73e..4ea70bb026b7b 100644 --- a/src/core/function.js +++ b/src/core/function.js @@ -433,7 +433,7 @@ var PDFFunction = (function PDFFunctionClosure() { var cachedValue = cache[key]; if (cachedValue !== undefined) { - cachedValue.set(dest, destOffset); + dest.set(cachedValue, destOffset); return; } @@ -457,7 +457,7 @@ var PDFFunction = (function PDFFunctionClosure() { cache_available--; cache[key] = output; } - output.set(dest, destOffset); + dest.set(output, destOffset); }; } }; diff --git a/src/core/jpg.js b/src/core/jpg.js index 9aa0e81f12681..91302e2776fc5 100644 --- a/src/core/jpg.js +++ b/src/core/jpg.js @@ -130,9 +130,8 @@ var JpegImage = (function jpegImage() { function decodeHuffman(tree) { var node = tree; - var bit; - while ((bit = readBit()) !== null) { - node = node[bit]; + while (true) { + node = node[readBit()]; if (typeof node === 'number') { return node; } @@ -140,17 +139,12 @@ var JpegImage = (function jpegImage() { throw 'invalid huffman sequence'; } } - return null; } function receive(length) { var n = 0; while (length > 0) { - var bit = readBit(); - if (bit === null) { - return; - } - n = (n << 1) | bit; + n = (n << 1) | readBit(); length--; } return n; @@ -509,7 +503,7 @@ var JpegImage = (function jpegImage() { // stage 3 // Shift v0 by 128.5 << 5 here, so we don't need to shift p0...p7 when - // converting to UInt8 range later. + // converting to UInt8 range later. v0 = ((v0 + v1 + 1) >> 1) + 4112; v1 = v0 - v1; t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12; @@ -866,8 +860,7 @@ var JpegImage = (function jpegImage() { } } - // decodeTransform will contains pairs of multiplier (-256..256) and - // additive + // decodeTransform contains pairs of multiplier (-256..256) and additive var transform = this.decodeTransform; if (transform) { for (i = 0; i < dataLength;) { @@ -904,7 +897,7 @@ var JpegImage = (function jpegImage() { }, _convertYcckToRgb: function convertYcckToRgb(data) { - var Y, Cb, Cr, k, CbCb, CbCr, CbY, Cbk, CrCr, Crk, CrY, YY, Yk, kk; + var Y, Cb, Cr, k; var offset = 0; for (var i = 0, length = data.length; i < length; i += 4) { Y = data[i]; @@ -912,43 +905,35 @@ var JpegImage = (function jpegImage() { Cr = data[i + 2]; k = data[i + 3]; - CbCb = Cb * Cb; - CbCr = Cb * Cr; - CbY = Cb * Y; - Cbk = Cb * k; - CrCr = Cr * Cr; - Crk = Cr * k; - CrY = Cr * Y; - YY = Y * Y; - Yk = Y * k; - kk = k * k; - - var r = - 122.67195406894 - - 6.60635669420364e-5 * CbCb + 0.000437130475926232 * CbCr - - 5.4080610064599e-5* CbY + 0.00048449797120281* Cbk - - 0.154362151871126 * Cb - 0.000957964378445773 * CrCr + - 0.000817076911346625 * CrY - 0.00477271405408747 * Crk + - 1.53380253221734 * Cr + 0.000961250184130688 * YY - - 0.00266257332283933 * Yk + 0.48357088451265 * Y - - 0.000336197177618394 * kk + 0.484791561490776 * k; + var r = -122.67195406894 + + Cb * (-6.60635669420364e-5 * Cb + 0.000437130475926232 * Cr - + 5.4080610064599e-5 * Y + 0.00048449797120281 * k - + 0.154362151871126) + + Cr * (-0.000957964378445773 * Cr + 0.000817076911346625 * Y - + 0.00477271405408747 * k + 1.53380253221734) + + Y * (0.000961250184130688 * Y - 0.00266257332283933 * k + + 0.48357088451265) + + k * (-0.000336197177618394 * k + 0.484791561490776); var g = 107.268039397724 + - 2.19927104525741e-5 * CbCb - 0.000640992018297945 * CbCr + - 0.000659397001245577* CbY + 0.000426105652938837* Cbk - - 0.176491792462875 * Cb - 0.000778269941513683 * CrCr + - 0.00130872261408275 * CrY + 0.000770482631801132 * Crk - - 0.151051492775562 * Cr + 0.00126935368114843 * YY - - 0.00265090189010898 * Yk + 0.25802910206845 * Y - - 0.000318913117588328 * kk - 0.213742400323665 * k; - - var b = - 20.810012546947 - - 0.000570115196973677 * CbCb - 2.63409051004589e-5 * CbCr + - 0.0020741088115012* CbY - 0.00288260236853442* Cbk + - 0.814272968359295 * Cb - 1.53496057440975e-5 * CrCr - - 0.000132689043961446 * CrY + 0.000560833691242812 * Crk - - 0.195152027534049 * Cr + 0.00174418132927582 * YY - - 0.00255243321439347 * Yk + 0.116935020465145 * Y - - 0.000343531996510555 * kk + 0.24165260232407 * k; + Cb * (2.19927104525741e-5 * Cb - 0.000640992018297945 * Cr + + 0.000659397001245577 * Y + 0.000426105652938837 * k - + 0.176491792462875) + + Cr * (-0.000778269941513683 * Cr + 0.00130872261408275 * Y + + 0.000770482631801132 * k - 0.151051492775562) + + Y * (0.00126935368114843 * Y - 0.00265090189010898 * k + + 0.25802910206845) + + k * (-0.000318913117588328 * k - 0.213742400323665); + + var b = -20.810012546947 + + Cb * (-0.000570115196973677 * Cb - 2.63409051004589e-5 * Cr + + 0.0020741088115012 * Y - 0.00288260236853442 * k + + 0.814272968359295) + + Cr * (-1.53496057440975e-5 * Cr - 0.000132689043961446 * Y + + 0.000560833691242812 * k - 0.195152027534049) + + Y * (0.00174418132927582 * Y - 0.00255243321439347 * k + + 0.116935020465145) + + k * (-0.000343531996510555 * k + 0.24165260232407); data[offset++] = clamp0to255(r); data[offset++] = clamp0to255(g); diff --git a/src/core/jpx.js b/src/core/jpx.js index cb79d273aef7f..c7e1fd48ce233 100644 --- a/src/core/jpx.js +++ b/src/core/jpx.js @@ -15,7 +15,7 @@ * limitations under the License. */ /* globals ArithmeticDecoder, globalScope, log2, readUint16, readUint32, - warn */ + info, warn */ 'use strict'; @@ -63,18 +63,52 @@ var JpxImage = (function JpxImageClosure() { var dataLength = lbox - headerSize; var jumpDataLength = true; switch (tbox) { - case 0x6A501A1A: // 'jP\032\032' - // TODO - break; case 0x6A703268: // 'jp2h' jumpDataLength = false; // parsing child boxes break; case 0x636F6C72: // 'colr' - // TODO + // Colorspaces are not used, the CS from the PDF is used. + var method = data[position]; + var precedence = data[position + 1]; + var approximation = data[position + 2]; + if (method === 1) { + // enumerated colorspace + var colorspace = readUint32(data, position + 3); + switch (colorspace) { + case 16: // this indicates a sRGB colorspace + case 17: // this indicates a grayscale colorspace + case 18: // this indicates a YUV colorspace + break; + default: + warn('Unknown colorspace ' + colorspace); + break; + } + } else if (method === 2) { + info('ICC profile not supported'); + } break; case 0x6A703263: // 'jp2c' this.parseCodestream(data, position, position + dataLength); break; + case 0x6A502020: // 'jP\024\024' + if (0x0d0a870a !== readUint32(data, position)) { + warn('Invalid JP2 signature'); + } + break; + // The following header types are valid but currently not used: + case 0x6A501A1A: // 'jP\032\032' + case 0x66747970: // 'ftyp' + case 0x72726571: // 'rreq' + case 0x72657320: // 'res ' + case 0x69686472: // 'ihdr' + break; + default: + var headerType = String.fromCharCode((tbox >> 24) & 0xFF, + (tbox >> 16) & 0xFF, + (tbox >> 8) & 0xFF, + tbox & 0xFF); + warn('Unsupported header type ' + tbox + ' (' + headerType + ')'); + break; } if (jumpDataLength) { position += dataLength; @@ -446,6 +480,23 @@ var JpxImage = (function JpxImageClosure() { // Section B.6 Division resolution to precincts var precinctWidth = 1 << dimensions.PPx; var precinctHeight = 1 << dimensions.PPy; + // Jasper introduces codeblock groups for mapping each subband codeblocks + // to precincts. Precinct partition divides a resolution according to width + // and height parameters. The subband that belongs to the resolution level + // has a different size than the level, unless it is the zero resolution. + + // From Jasper documentation: jpeg2000.pdf, section K: Tier-2 coding: + // The precinct partitioning for a particular subband is derived from a + // partitioning of its parent LL band (i.e., the LL band at the next higher + // resolution level)... The LL band associated with each resolution level is + // divided into precincts... Each of the resulting precinct regions is then + // mapped into its child subbands (if any) at the next lower resolution + // level. This is accomplished by using the coordinate transformation + // (u, v) = (ceil(x/2), ceil(y/2)) where (x, y) and (u, v) are the + // coordinates of a point in the LL band and child subband, respectively. + var isZeroRes = resolution.resLevel === 0; + var precinctWidthInSubband = 1 << (dimensions.PPx + (isZeroRes ? 0 : -1)); + var precinctHeightInSubband = 1 << (dimensions.PPy + (isZeroRes ? 0 : -1)); var numprecinctswide = (resolution.trx1 > resolution.trx0 ? Math.ceil(resolution.trx1 / precinctWidth) - Math.floor(resolution.trx0 / precinctWidth) : 0); @@ -453,18 +504,15 @@ var JpxImage = (function JpxImageClosure() { Math.ceil(resolution.try1 / precinctHeight) - Math.floor(resolution.try0 / precinctHeight) : 0); var numprecincts = numprecinctswide * numprecinctshigh; - var precinctXOffset = Math.floor(resolution.trx0 / precinctWidth) * - precinctWidth; - var precinctYOffset = Math.floor(resolution.try0 / precinctHeight) * - precinctHeight; + resolution.precinctParameters = { - precinctXOffset: precinctXOffset, - precinctYOffset: precinctYOffset, precinctWidth: precinctWidth, precinctHeight: precinctHeight, numprecinctswide: numprecinctswide, numprecinctshigh: numprecinctshigh, - numprecincts: numprecincts + numprecincts: numprecincts, + precinctWidthInSubband: precinctWidthInSubband, + precinctHeightInSubband: precinctHeightInSubband }; } function buildCodeblocks(context, subband, dimensions) { @@ -491,21 +539,29 @@ var JpxImage = (function JpxImageClosure() { tbx1: codeblockWidth * (i + 1), tby1: codeblockHeight * (j + 1) }; - // calculate precinct number - var pi = Math.floor((codeblock.tbx0 - - precinctParameters.precinctXOffset) / - precinctParameters.precinctWidth); - var pj = Math.floor((codeblock.tby0 - - precinctParameters.precinctYOffset) / - precinctParameters.precinctHeight); - precinctNumber = pj + pi * precinctParameters.numprecinctswide; + codeblock.tbx0_ = Math.max(subband.tbx0, codeblock.tbx0); codeblock.tby0_ = Math.max(subband.tby0, codeblock.tby0); codeblock.tbx1_ = Math.min(subband.tbx1, codeblock.tbx1); codeblock.tby1_ = Math.min(subband.tby1, codeblock.tby1); + + // Calculate precinct number for this codeblock, codeblock position + // should be relative to its subband, use actual dimension and position + // See comment about codeblock group width and height + var pi = Math.floor((codeblock.tbx0_ - subband.tbx0) / + precinctParameters.precinctWidthInSubband); + var pj = Math.floor((codeblock.tby0_ - subband.tby0) / + precinctParameters.precinctHeightInSubband); + precinctNumber = pi + (pj * precinctParameters.numprecinctswide); + codeblock.precinctNumber = precinctNumber; codeblock.subbandType = subband.type; codeblock.Lblock = 3; + + if (codeblock.tbx1_ <= codeblock.tbx0_ || + codeblock.tby1_ <= codeblock.tby0_) { + continue; + } codeblocks.push(codeblock); // building precinct for the sub-band var precinct = precincts[precinctNumber]; @@ -662,6 +718,7 @@ var JpxImage = (function JpxImageClosure() { resolution.try0 = Math.ceil(component.tcy0 / scale); resolution.trx1 = Math.ceil(component.tcx1 / scale); resolution.try1 = Math.ceil(component.tcy1 / scale); + resolution.resLevel = r; buildPrecincts(context, resolution, blocksDimensions); resolutions.push(resolution); @@ -788,11 +845,11 @@ var JpxImage = (function JpxImageClosure() { var tile = context.tiles[tileIndex]; var packetsIterator = tile.packetsIterator; while (position < dataLength) { - var packet = packetsIterator.nextPacket(); + alignToByte(); if (!readBits(1)) { - alignToByte(); continue; } + var packet = packetsIterator.nextPacket(); var layerNumber = packet.layerNumber; var queue = [], codeblock; for (var i = 0, ii = packet.codeblocks.length; i < ii; i++) { @@ -803,13 +860,13 @@ var JpxImage = (function JpxImageClosure() { var codeblockIncluded = false; var firstTimeInclusion = false; var valueReady; - if ('included' in codeblock) { + if (codeblock['included'] !== undefined) { codeblockIncluded = !!readBits(1); } else { // reading inclusion tree precinct = codeblock.precinct; var inclusionTree, zeroBitPlanesTree; - if ('inclusionTree' in precinct) { + if (precinct['inclusionTree'] !== undefined) { inclusionTree = precinct.inclusionTree; } else { // building inclusion and zero bit-planes trees @@ -874,7 +931,7 @@ var JpxImage = (function JpxImageClosure() { while (queue.length > 0) { var packetItem = queue.shift(); codeblock = packetItem.codeblock; - if (!('data' in codeblock)) { + if (codeblock['data'] === undefined) { codeblock.data = []; } codeblock.data.push({ @@ -904,7 +961,7 @@ var JpxImage = (function JpxImageClosure() { if (blockWidth === 0 || blockHeight === 0) { continue; } - if (!('data' in codeblock)) { + if (codeblock['data'] === undefined) { continue; } @@ -1159,10 +1216,10 @@ var JpxImage = (function JpxImageClosure() { var tile = context.tiles[tileIndex]; for (var c = 0; c < componentsCount; c++) { var component = tile.components[c]; - var qcdOrQcc = (c in context.currentTile.QCC ? + var qcdOrQcc = (context.currentTile.QCC[c] !== undefined ? context.currentTile.QCC[c] : context.currentTile.QCD); component.quantizationParameters = qcdOrQcc; - var codOrCoc = (c in context.currentTile.COC ? + var codOrCoc = (context.currentTile.COC[c] !== undefined ? context.currentTile.COC[c] : context.currentTile.COD); component.codingStyleParameters = codOrCoc; } @@ -1191,7 +1248,7 @@ var JpxImage = (function JpxImageClosure() { while (currentLevel < this.levels.length) { level = this.levels[currentLevel]; var index = i + j * level.width; - if (index in level.items) { + if (level.items[index] !== undefined) { value = level.items[index]; break; } diff --git a/src/core/obj.js b/src/core/obj.js index f1de50f59d58d..babfdaf8a0b66 100644 --- a/src/core/obj.js +++ b/src/core/obj.js @@ -457,6 +457,38 @@ var Catalog = (function CatalogClosure() { } return shadow(this, 'destinations', dests); }, + getDestination: function Catalog_getDestination(destinationId) { + function fetchDestination(dest) { + return isDict(dest) ? dest.get('D') : dest; + } + + var xref = this.xref; + var dest, nameTreeRef, nameDictionaryRef; + var obj = this.catDict.get('Names'); + if (obj && obj.has('Dests')) { + nameTreeRef = obj.getRaw('Dests'); + } else if (this.catDict.has('Dests')) { + nameDictionaryRef = this.catDict.get('Dests'); + } + + if (nameDictionaryRef) { + // reading simple destination dictionary + obj = nameDictionaryRef; + obj.forEach(function catalogForEach(key, value) { + if (!value) { + return; + } + if (key === destinationId) { + dest = fetchDestination(value); + } + }); + } + if (nameTreeRef) { + var nameTree = new NameTree(nameTreeRef, xref); + dest = fetchDestination(nameTree.get(destinationId)); + } + return dest; + }, get attachments() { var xref = this.xref; var attachments = null, nameTreeRef; @@ -1358,6 +1390,76 @@ var NameTree = (function NameTreeClosure() { } } return dict; + }, + + get: function NameTree_get(destinationId) { + if (!this.root) { + return null; + } + + var xref = this.xref; + var kidsOrNames = xref.fetchIfRef(this.root); + var loopCount = 0; + var MAX_NAMES_LEVELS = 10; + var l, r, m; + + // Perform a binary search to quickly find the entry that + // contains the named destination we are looking for. + while (kidsOrNames.has('Kids')) { + loopCount++; + if (loopCount > MAX_NAMES_LEVELS) { + warn('Search depth limit for named destionations has been reached.'); + return null; + } + + var kids = kidsOrNames.get('Kids'); + if (!isArray(kids)) { + return null; + } + + l = 0; + r = kids.length - 1; + while (l <= r) { + m = (l + r) >> 1; + var kid = xref.fetchIfRef(kids[m]); + var limits = kid.get('Limits'); + + if (destinationId < limits[0]) { + r = m - 1; + } else if (destinationId > limits[1]) { + l = m + 1; + } else { + kidsOrNames = xref.fetchIfRef(kids[m]); + break; + } + } + if (l > r) { + return null; + } + } + + // If we get here, then we have found the right entry. Now + // go through the named destinations in the Named dictionary + // until we find the exact destination we're looking for. + var names = kidsOrNames.get('Names'); + if (isArray(names)) { + // Perform a binary search to reduce the lookup time. + l = 0; + r = names.length - 2; + while (l <= r) { + // Check only even indices (0, 2, 4, ...) because the + // odd indices contain the actual D array. + m = (l + r) & ~1; + if (destinationId < names[m]) { + r = m - 2; + } else if (destinationId > names[m]) { + l = m + 2; + } else { + return xref.fetchIfRef(names[m + 1]); + } + } + } + return null; } }; return NameTree; diff --git a/src/core/stream.js b/src/core/stream.js index bd244735c3916..a842956971f1b 100644 --- a/src/core/stream.js +++ b/src/core/stream.js @@ -884,7 +884,7 @@ var JpegStream = (function JpegStreamClosure() { var jpegImage = new JpegImage(); // checking if values needs to be transformed before conversion - if (this.dict && isArray(this.dict.get('Decode'))) { + if (this.forceRGB && this.dict && isArray(this.dict.get('Decode'))) { var decodeArr = this.dict.get('Decode'); var bitsPerComponent = this.dict.get('BitsPerComponent') || 8; var decodeArrLength = decodeArr.length; diff --git a/src/core/worker.js b/src/core/worker.js index 30effa8a4da67..40d7d899e68b7 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -138,7 +138,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } }, - onProgressiveData: PDFJS.disableStream ? null : + onProgressiveData: source.disableStream ? null : function onProgressiveData(chunk) { if (!pdfManager) { cachedChunks.push(chunk); @@ -325,6 +325,12 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } ); + handler.on('GetDestination', + function wphSetupGetDestination(data) { + return pdfManager.ensureCatalog('getDestination', [ data.id ]); + } + ); + handler.on('GetAttachments', function wphSetupGetAttachments(data) { return pdfManager.ensureCatalog('attachments'); diff --git a/src/display/api.js b/src/display/api.js index 581a4fabf6eb8..b63aaf896c215 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -17,8 +17,9 @@ /* globals PDFJS, isArrayBuffer, error, combineUrl, createPromiseCapability, StatTimer, globalScope, MessageHandler, info, FontLoader, Util, warn, Promise, PasswordResponses, PasswordException, InvalidPDFException, - MissingPDFException, UnknownErrorException, FontFace, loadJpegStream, - createScratchCanvas, CanvasGraphics, UnexpectedResponseException */ + MissingPDFException, UnknownErrorException, FontFaceObject, + loadJpegStream, createScratchCanvas, CanvasGraphics, + UnexpectedResponseException */ 'use strict'; @@ -292,10 +293,20 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { /** * @return {Promise} A promise that is resolved with a lookup table for * mapping named destinations to reference numbers. + * + * This can be slow for large documents: use getDestination instead */ getDestinations: function PDFDocumentProxy_getDestinations() { return this.transport.getDestinations(); }, + /** + * @param {string} id The named destination to get. + * @return {Promise} A promise that is resolved with all information + * of the given named destination. + */ + getDestination: function PDFDocumentProxy_getDestination(id) { + return this.transport.getDestination(id); + }, /** * @return {Promise} A promise that is resolved with a lookup table for * mapping named attachments to their content. @@ -963,7 +974,7 @@ var WorkerTransport = (function WorkerTransportClosure() { this.commonObjs.resolve(id, error); break; } else { - font = new FontFace(exportedData); + font = new FontFaceObject(exportedData); } FontLoader.bind( @@ -1076,6 +1087,7 @@ var WorkerTransport = (function WorkerTransportClosure() { fetchDocument: function WorkerTransport_fetchDocument(source) { source.disableAutoFetch = PDFJS.disableAutoFetch; + source.disableStream = PDFJS.disableStream; source.chunkedViewerLoading = !!this.pdfDataRangeTransport; this.messageHandler.send('GetDocRequest', { source: source, @@ -1127,6 +1139,10 @@ var WorkerTransport = (function WorkerTransportClosure() { return this.messageHandler.sendWithPromise('GetDestinations', null); }, + getDestination: function WorkerTransport_getDestination(id) { + return this.messageHandler.sendWithPromise('GetDestination', { id: id } ); + }, + getAttachments: function WorkerTransport_getAttachments() { return this.messageHandler.sendWithPromise('GetAttachments', null); }, diff --git a/src/display/canvas.js b/src/display/canvas.js index 55c26d38a896b..61e4e724cd140 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -26,6 +26,8 @@ // Minimal font size that would be used during canvas fillText operations. var MIN_FONT_SIZE = 16; +// Maximum font size that would be used during canvas fillText operations. +var MAX_FONT_SIZE = 100; var MAX_GROUP_SIZE = 4096; var COMPILE_TYPE3_GLYPHS = true; @@ -1229,9 +1231,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { // Keeping the font at minimal size and using the fontSizeScale to change // the current transformation matrix before the fillText/strokeText. // See https://bugzilla.mozilla.org/show_bug.cgi?id=726227 - var browserFontSize = size >= MIN_FONT_SIZE ? size : MIN_FONT_SIZE; - this.current.fontSizeScale = browserFontSize !== MIN_FONT_SIZE ? 1.0 : - size / MIN_FONT_SIZE; + var browserFontSize = size < MIN_FONT_SIZE ? MIN_FONT_SIZE : + size > MAX_FONT_SIZE ? MAX_FONT_SIZE : size; + this.current.fontSizeScale = size / browserFontSize; var rule = italic + ' ' + bold + ' ' + browserFontSize + 'px ' + typeface; this.ctx.font = rule; @@ -1664,10 +1666,12 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var textHScale = current.textHScale * fontDirection; var fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX; var glyphsLength = glyphs.length; + var isTextInvisible = + current.textRenderingMode === TextRenderingMode.INVISIBLE; var i, glyph, width; var sw, spaceWidthProj, font2dev = []; - if (fontSize === 0) { + if (isTextInvisible || fontSize === 0) { return; } diff --git a/src/display/font_loader.js b/src/display/font_loader.js index 105ed83226f9f..5326a3e051c17 100644 --- a/src/display/font_loader.js +++ b/src/display/font_loader.js @@ -15,7 +15,7 @@ * limitations under the License. */ /* globals PDFJS, shadow, isWorker, assert, warn, bytesToString, string32, - globalScope */ + globalScope, FontFace, Promise */ 'use strict'; @@ -40,6 +40,12 @@ var FontLoader = { if (styleElement) { styleElement.parentNode.removeChild(styleElement); } +//#if !(MOZCENTRAL) + this.nativeFontFaces.forEach(function(nativeFontFace) { + document.fonts.delete(nativeFontFace); + }); + this.nativeFontFaces.length = 0; +//#endif }, //#if !(MOZCENTRAL) get loadTestFont() { @@ -97,10 +103,21 @@ var FontLoader = { return false; })(), + nativeFontFaces: [], + + isFontLoadingAPISupported: !isWorker && !!document.fonts, + + addNativeFontFace: function fontLoader_addNativeFontFace(nativeFontFace) { + this.nativeFontFaces.push(nativeFontFace); + document.fonts.add(nativeFontFace); + }, + bind: function fontLoaderBind(fonts, callback) { assert(!isWorker, 'bind() shall be called from main thread'); - var rules = [], fontsToLoad = []; + var rules = []; + var fontsToLoad = []; + var fontLoadPromises = []; for (var i = 0, ii = fonts.length; i < ii; i++) { var font = fonts[i]; @@ -111,15 +128,26 @@ var FontLoader = { } font.attached = true; - var rule = font.bindDOM(); - if (rule) { - rules.push(rule); - fontsToLoad.push(font); + if (this.isFontLoadingAPISupported) { + var nativeFontFace = font.createNativeFontFace(); + if (nativeFontFace) { + fontLoadPromises.push(nativeFontFace.loaded); + } + } else { + var rule = font.bindDOM(); + if (rule) { + rules.push(rule); + fontsToLoad.push(font); + } } } var request = FontLoader.queueLoadingCallback(callback); - if (rules.length > 0 && !this.isSyncFontLoadingSupported) { + if (this.isFontLoadingAPISupported) { + Promise.all(fontsToLoad).then(function() { + request.complete(); + }); + } else if (rules.length > 0 && !this.isSyncFontLoadingSupported) { FontLoader.prepareFontLoadEvent(rules, fontsToLoad, request); } else { request.complete(); @@ -271,8 +299,8 @@ var FontLoader = { //#endif }; -var FontFace = (function FontFaceClosure() { - function FontFace(name, file, properties) { +var FontFaceObject = (function FontFaceObjectClosure() { + function FontFaceObject(name, file, properties) { this.compiledGlyphs = {}; if (arguments.length === 1) { // importing translated data @@ -283,8 +311,31 @@ var FontFace = (function FontFaceClosure() { return; } } - FontFace.prototype = { - bindDOM: function FontFace_bindDOM() { + FontFaceObject.prototype = { +//#if !(MOZCENTRAL) + createNativeFontFace: function FontFaceObject_createNativeFontFace() { + if (!this.data) { + return null; + } + + if (PDFJS.disableFontFace) { + this.disableFontFace = true; + return null; + } + + var nativeFontFace = new FontFace(this.loadedName, this.data, {}); + + FontLoader.addNativeFontFace(nativeFontFace); + + if (PDFJS.pdfBug && 'FontInspector' in globalScope && + globalScope['FontInspector'].enabled) { + globalScope['FontInspector'].fontAdded(this); + } + return nativeFontFace; + }, +//#endif + + bindDOM: function FontFaceObject_bindDOM() { if (!this.data) { return null; } @@ -311,7 +362,7 @@ var FontFace = (function FontFaceClosure() { return rule; }, - getPathGenerator: function (objs, character) { + getPathGenerator: function FontLoader_getPathGenerator(objs, character) { if (!(character in this.compiledGlyphs)) { var js = objs.get(this.loadedName + '_path_' + character); /*jshint -W054 */ @@ -320,5 +371,5 @@ var FontFace = (function FontFaceClosure() { return this.compiledGlyphs[character]; } }; - return FontFace; + return FontFaceObject; })(); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 94c9c87a977a5..9244559dd2b3e 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -26,6 +26,7 @@ !issue3438.pdf !issue2074.pdf !scan-bad.pdf +!bug847420.pdf !bug860632.pdf !pdfjsbad1586.pdf !freeculture.pdf @@ -59,16 +60,22 @@ !bug864847.pdf !issue1002.pdf !issue925.pdf +!issue2840.pdf !issue4668.pdf !issue5039.pdf !issue5070.pdf +!issue5238.pdf !issue5244.pdf +!issue5291.pdf +!issue5421.pdf +!issue5470.pdf !gradientfill.pdf !bug903856.pdf !bug850854.pdf !bug866395.pdf !bug1027533.pdf !bug1028735.pdf +!bug1065245.pdf !basicapi.pdf !mixedfonts.pdf !shading_extend.pdf diff --git a/test/pdfs/bug1065245.pdf b/test/pdfs/bug1065245.pdf new file mode 100644 index 0000000000000..04cfbd5f015e5 Binary files /dev/null and b/test/pdfs/bug1065245.pdf differ diff --git a/test/pdfs/bug1072164.pdf.link b/test/pdfs/bug1072164.pdf.link new file mode 100644 index 0000000000000..c3868f64158b7 --- /dev/null +++ b/test/pdfs/bug1072164.pdf.link @@ -0,0 +1 @@ +https://bugzilla.mozilla.org/attachment.cgi?id=8494369 diff --git a/test/pdfs/bug847420.pdf b/test/pdfs/bug847420.pdf new file mode 100644 index 0000000000000..ba804dd14e656 Binary files /dev/null and b/test/pdfs/bug847420.pdf differ diff --git a/test/pdfs/bug865858.pdf.link b/test/pdfs/bug865858.pdf.link new file mode 100644 index 0000000000000..45b946434c025 --- /dev/null +++ b/test/pdfs/bug865858.pdf.link @@ -0,0 +1 @@ +https://bug865858.bugzilla.mozilla.org/attachment.cgi?id=742273 diff --git a/test/pdfs/issue2840.pdf b/test/pdfs/issue2840.pdf new file mode 100644 index 0000000000000..b623fe7dac1ae Binary files /dev/null and b/test/pdfs/issue2840.pdf differ diff --git a/test/pdfs/issue5238.pdf b/test/pdfs/issue5238.pdf new file mode 100644 index 0000000000000..a4ac44b22eb0a Binary files /dev/null and b/test/pdfs/issue5238.pdf differ diff --git a/test/pdfs/issue5291.pdf b/test/pdfs/issue5291.pdf new file mode 100644 index 0000000000000..a206a91d1ad5e Binary files /dev/null and b/test/pdfs/issue5291.pdf differ diff --git a/test/pdfs/issue5421.pdf b/test/pdfs/issue5421.pdf new file mode 100644 index 0000000000000..f288a712517c4 Binary files /dev/null and b/test/pdfs/issue5421.pdf differ diff --git a/test/pdfs/issue5470.pdf b/test/pdfs/issue5470.pdf new file mode 100644 index 0000000000000..6962d1f1a8440 Binary files /dev/null and b/test/pdfs/issue5470.pdf differ diff --git a/test/test_manifest.json b/test/test_manifest.json index 00db5533f0b36..c93a911c99b4e 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -364,6 +364,13 @@ "rounds": 1, "type": "load" }, + { "id": "bug847420", + "file": "pdfs/bug847420.pdf", + "md5": "0decd96fec4ef858c2c663a6de24e887", + "rounds": 1, + "link": false, + "type": "eq" + }, { "id": "bug878026", "file": "pdfs/bug878026.pdf", "md5": "13072db0586f2b4e96de189e23fc7395", @@ -478,6 +485,20 @@ "lastPage": 1, "type": "eq" }, + { "id": "issue5238", + "file": "pdfs/issue5238.pdf", + "md5": "6ddecda00893be1793de20a70c83a3c2", + "rounds": 1, + "link": false, + "type": "eq" + }, + { "id": "issue5470", + "file": "pdfs/issue5470.pdf", + "md5": "4805fdcd7e142e8df3c04c6ba06025af", + "rounds": 1, + "link": false, + "type": "eq" + }, { "id": "txt2pdf", "file": "pdfs/txt2pdf.pdf", "md5": "02cefa0f5e8d96313bb05163b2f88c8c", @@ -739,6 +760,13 @@ "type": "eq", "about": "Seac with differences array that messes up mapping." }, + { "id": "issue2840", + "file": "pdfs/issue2840.pdf", + "md5": "d9df49f6d62668d099e0fb7e74f8f337", + "rounds": 1, + "link": false, + "type": "eq" + }, { "id": "bug866395", "file": "pdfs/bug866395.pdf", "md5": "f03bc77e84637241980b09a0a220f575", @@ -913,6 +941,13 @@ "rounds": 1, "type": "eq" }, + { "id": "issue5291", + "file": "pdfs/issue5291.pdf", + "md5": "edae085495c702069ffdbf785a826556", + "link": false, + "rounds": 1, + "type": "eq" + }, { "id": "issue5244", "file": "pdfs/issue5244.pdf", "md5": "a50cd364c3976c744627b4b9bb90c761", @@ -1005,6 +1040,14 @@ "link": true, "type": "eq" }, + { "id": "bug1065245", + "file": "pdfs/bug1065245.pdf", + "md5": "844b3af0a1d338a2e1bbe742f474bbb7", + "rounds": 1, + "link": false, + "type": "eq", + "about": "Inline JPEG images." + }, { "id": "issue1655", "file": "pdfs/issue1655.pdf", "md5": "696ef6de6f4f71643771419ef04fc968", @@ -1541,6 +1584,14 @@ "type": "eq", "about": "True type font with encoding dict with no base encoding but with differences." }, + { "id": "issue5421", + "file": "pdfs/issue5421.pdf", + "md5": "273f6813758a2349090003c7c8a0d85e", + "link": false, + "rounds": 1, + "type": "eq", + "about": "Invisible Type3 font used for text selection and searching." + }, { "id": "issue5280", "file": "pdfs/issue5280.pdf", "md5": "0ea1230e2964e74cb6db063a89b78803", @@ -1556,7 +1607,7 @@ "rounds": 1, "firstPage": 4, "lastPage": 4, - "type": "load" + "type": "eq" }, { "id": "p020121130574743273239", "file": "pdfs/P020121130574743273239.pdf", @@ -1588,6 +1639,14 @@ "rounds": 1, "type": "eq" }, + { "id": "bug1072164", + "file": "pdfs/bug1072164.pdf", + "md5": "cfee3c51e8464aa44218f4eaf27e084b", + "rounds": 1, + "link": true, + "type": "eq", + "about": "CMYK jpeg with mask" + }, { "id": "bug886717", "file": "pdfs/bug886717.pdf", "md5": "8ba614192797a1324765610231a1bc9d", @@ -1645,6 +1704,14 @@ "type": "eq", "about": "JPX with 0xFF55 marker" }, + { "id": "bug865858", + "file": "pdfs/bug865858.pdf", + "md5": "7a81bd987dc1d95e9a0be46b7c3f2e18", + "link": true, + "rounds": 1, + "type": "eq", + "about": "JPX packets" + }, { "id": "bug766138", "file": "pdfs/bug766138.pdf", "md5": "b171f5cf8d9834348112fba60ee54f8c", diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index a8ba8382d5313..0044764e26686 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -123,6 +123,13 @@ describe('api', function() { 0, 841.89, null] }); }); }); + it('gets a destination', function() { + var promise = doc.getDestination('chapter1'); + waitsForPromiseResolved(promise, function(data) { + expect(data).toEqual([{ gen: 0, num: 17 }, { name: 'XYZ' }, + 0, 841.89, null]); + }); + }); it('gets attachments', function() { var promise = doc.getAttachments(); waitsForPromiseResolved(promise, function (data) { diff --git a/web/chromecom.js b/web/chromecom.js index f1739860b2724..5fe69fef2a32a 100644 --- a/web/chromecom.js +++ b/web/chromecom.js @@ -14,7 +14,7 @@ * limitations under the License. */ -/* globals chrome, PDFJS, PDFView */ +/* globals chrome, PDFJS, PDFViewerApplication */ 'use strict'; var ChromeCom = (function ChromeComClosure() { @@ -64,10 +64,10 @@ var ChromeCom = (function ChromeComClosure() { var streamUrl = response.streamUrl; if (streamUrl) { console.log('Found data stream for ' + file); - PDFView.open(streamUrl, 0, undefined, undefined, { + PDFViewerApplication.open(streamUrl, 0, undefined, undefined, { length: response.contentLength }); - PDFView.setTitleUsingUrl(file); + PDFViewerApplication.setTitleUsingUrl(file); return; } if (isFTPFile && !response.extensionSupportsFTP) { @@ -91,7 +91,7 @@ var ChromeCom = (function ChromeComClosure() { resolveLocalFileSystemURL(file, function onResolvedFSURL(fileEntry) { fileEntry.file(function(fileObject) { var blobUrl = URL.createObjectURL(fileObject); - PDFView.open(blobUrl, 0, undefined, undefined, { + PDFViewerApplication.open(blobUrl, 0, undefined, undefined, { length: fileObject.size }); }); @@ -100,11 +100,11 @@ var ChromeCom = (function ChromeComClosure() { // usual way of getting the File's data (via the Web worker). console.warn('Cannot resolve file ' + file + ', ' + error.name + ' ' + error.message); - PDFView.open(file, 0); + PDFViewerApplication.open(file, 0); }); return; } - PDFView.open(file, 0); + PDFViewerApplication.open(file, 0); }); }; return ChromeCom; diff --git a/web/debugger.js b/web/debugger.js index 81da232ee77c2..046fd34ad733d 100644 --- a/web/debugger.js +++ b/web/debugger.js @@ -112,13 +112,20 @@ var FontInspector = (function FontInspectorClosure() { return moreInfo; } var moreInfo = properties(fontObj, ['name', 'type']); - var m = /url\(['"]?([^\)"']+)/.exec(url); var fontName = fontObj.loadedName; var font = document.createElement('div'); var name = document.createElement('span'); name.textContent = fontName; var download = document.createElement('a'); - download.href = m[1]; + if (url) { + url = /url\(['"]?([^\)"']+)/.exec(url); + download.href = url[1]; + } else if (fontObj.data) { + url = URL.createObjectURL(new Blob([fontObj.data], { + type: fontObj.mimeType + })); + download.href = url; + } download.textContent = 'Download'; var logIt = document.createElement('a'); logIt.href = ''; diff --git a/web/document_attachments_view.js b/web/document_attachments_view.js index c8477dcede10c..fd015040cfada 100644 --- a/web/document_attachments_view.js +++ b/web/document_attachments_view.js @@ -14,20 +14,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals PDFView, DownloadManager, getFileName */ +/* globals DownloadManager, getFileName */ 'use strict'; -var DocumentAttachmentsView = function documentAttachmentsView(attachments) { - var attachmentsView = document.getElementById('attachmentsView'); +var DocumentAttachmentsView = function documentAttachmentsView(options) { + var attachments = options.attachments; + var attachmentsView = options.attachmentsView; while (attachmentsView.firstChild) { attachmentsView.removeChild(attachmentsView.firstChild); } if (!attachments) { - if (!attachmentsView.classList.contains('hidden')) { - PDFView.switchSidebarView('thumbs'); - } return; } diff --git a/web/document_outline_view.js b/web/document_outline_view.js index 74dbe3fb402b6..5a3ccd3154622 100644 --- a/web/document_outline_view.js +++ b/web/document_outline_view.js @@ -14,27 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals PDFView */ 'use strict'; -var DocumentOutlineView = function documentOutlineView(outline) { - var outlineView = document.getElementById('outlineView'); +var DocumentOutlineView = function documentOutlineView(options) { + var outline = options.outline; + var outlineView = options.outlineView; while (outlineView.firstChild) { outlineView.removeChild(outlineView.firstChild); } if (!outline) { - if (!outlineView.classList.contains('hidden')) { - PDFView.switchSidebarView('thumbs'); - } return; } + var linkService = options.linkService; + function bindItemLink(domObj, item) { - domObj.href = PDFView.getDestinationHash(item.dest); + domObj.href = linkService.getDestinationHash(item.dest); domObj.onclick = function documentOutlineViewOnclick(e) { - PDFView.navigateTo(item.dest); + linkService.navigateTo(item.dest); return false; }; } diff --git a/web/document_properties.js b/web/document_properties.js index c718cc57547dc..b8d30aa05d584 100644 --- a/web/document_properties.js +++ b/web/document_properties.js @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals PDFView, Promise, mozL10n, getPDFFileNameFromURL, OverlayManager */ +/* globals Promise, mozL10n, getPDFFileNameFromURL, OverlayManager */ 'use strict'; @@ -35,6 +35,8 @@ var DocumentProperties = { producerField: null, versionField: null, pageCountField: null, + url: null, + pdfDocument: null, initialize: function documentPropertiesInitialize(options) { this.overlayName = options.overlayName; @@ -72,7 +74,7 @@ var DocumentProperties = { return; } // Get the file size (if it hasn't already been set). - PDFView.pdfDocument.getDownloadInfo().then(function(data) { + this.pdfDocument.getDownloadInfo().then(function(data) { if (data.length === this.rawFileSize) { return; } @@ -81,10 +83,10 @@ var DocumentProperties = { }.bind(this)); // Get the document properties. - PDFView.pdfDocument.getMetadata().then(function(data) { + this.pdfDocument.getMetadata().then(function(data) { var fields = [ { field: this.fileNameField, - content: getPDFFileNameFromURL(PDFView.url) }, + content: getPDFFileNameFromURL(this.url) }, { field: this.fileSizeField, content: this.parseFileSize() }, { field: this.titleField, content: data.info.Title }, { field: this.authorField, content: data.info.Author }, @@ -97,7 +99,7 @@ var DocumentProperties = { { field: this.creatorField, content: data.info.Creator }, { field: this.producerField, content: data.info.Producer }, { field: this.versionField, content: data.info.PDFFormatVersion }, - { field: this.pageCountField, content: PDFView.pdfDocument.numPages } + { field: this.pageCountField, content: this.pdfDocument.numPages } ]; // Show the properties in the dialog. diff --git a/web/images/loading-small.png b/web/images/loading-small.png index 51848a70fe392..8831a80588dca 100644 Binary files a/web/images/loading-small.png and b/web/images/loading-small.png differ diff --git a/web/images/loading-small@2x.png b/web/images/loading-small@2x.png new file mode 100644 index 0000000000000..b25b4452aa1a5 Binary files /dev/null and b/web/images/loading-small@2x.png differ diff --git a/web/interfaces.js b/web/interfaces.js new file mode 100644 index 0000000000000..5f0ad04c6b349 --- /dev/null +++ b/web/interfaces.js @@ -0,0 +1,85 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* Copyright 2014 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. + */ + +'use strict'; + +/** + * @interface + */ +function IPDFLinkService() {} +IPDFLinkService.prototype = { + /** + * @returns {number} + */ + get page() {}, + /** + * @param {number} value + */ + set page(value) {}, + /** + * @param dest - The PDF destination object. + */ + navigateTo: function (dest) {}, + /** + * @param dest - The PDF destination object. + * @returns {string} The hyperlink to the PDF object. + */ + getDestinationHash: function (dest) {}, + /** + * @param hash - The PDF parameters/hash. + * @returns {string} The hyperlink to the PDF object. + */ + getAnchorUrl: function (hash) {}, + /** + * @param {string} hash + */ + setHash: function (hash) {}, + /** + * @param {string} action + */ + executeNamedAction: function (action) {}, +}; + +/** + * @interface + */ +function IRenderableView() {} +IRenderableView.prototype = { + /** + * @returns {string} - Unique ID for rendering queue. + */ + get renderingId() {}, + /** + * @returns {RenderingStates} + */ + get renderingState() {}, + /** + * @param {function} callback - The draw completion callback. + */ + draw: function (callback) {}, + resume: function () {}, +}; + +/** + * @interface + */ +function ILastScrollSource() {} +ILastScrollSource.prototype = { + /** + * @returns {number} + */ + get lastScroll() {}, +}; diff --git a/web/page_view.js b/web/page_view.js index 62a90686dc9f9..92ed39b7d5b81 100644 --- a/web/page_view.js +++ b/web/page_view.js @@ -14,16 +14,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals RenderingStates, PDFView, PDFHistory, PDFJS, mozL10n, CustomStyle, - PresentationMode, scrollIntoView, SCROLLBAR_PADDING, CSS_UNITS, - UNKNOWN_SCALE, DEFAULT_SCALE, getOutputScale, TextLayerBuilder, - cache, Stats */ +/* globals RenderingStates, PDFJS, mozL10n, CustomStyle, getOutputScale, Stats, + CSS_UNITS */ 'use strict'; -var PageView = function pageView(container, id, scale, - navigateTo, defaultViewport) { +/** + * @constructor + * @param {HTMLDivElement} container - The viewer element. + * @param {number} id - The page unique ID (normally its number). + * @param {number} scale - The page scale display. + * @param {PageViewport} defaultViewport - The page viewport. + * @param {IPDFLinkService} linkService - The navigation/linking service. + * @param {PDFRenderingQueue} renderingQueue - The rendering queue object. + * @param {Cache} cache - The page cache. + * @param {PDFPageSource} pageSource + * @param {PDFViewer} viewer + * + * @implements {IRenderableView} + */ +var PageView = function pageView(container, id, scale, defaultViewport, + linkService, renderingQueue, cache, + pageSource, viewer) { this.id = id; + this.renderingId = 'page' + id; this.rotation = 0; this.scale = scale || 1.0; @@ -31,6 +45,12 @@ var PageView = function pageView(container, id, scale, this.pdfPageRotate = defaultViewport.rotation; this.hasRestrictedScaling = false; + this.linkService = linkService; + this.renderingQueue = renderingQueue; + this.cache = cache; + this.pageSource = pageSource; + this.viewer = viewer; + this.renderingState = RenderingStates.INITIAL; this.resume = null; @@ -241,10 +261,10 @@ var PageView = function pageView(container, id, scale, function setupAnnotations(pageDiv, pdfPage, viewport) { function bindLink(link, dest) { - link.href = PDFView.getDestinationHash(dest); + link.href = linkService.getDestinationHash(dest); link.onclick = function pageViewSetupLinksOnclick() { if (dest) { - PDFView.navigateTo(dest); + linkService.navigateTo(dest); } return false; }; @@ -254,47 +274,9 @@ var PageView = function pageView(container, id, scale, } function bindNamedAction(link, action) { - link.href = PDFView.getAnchorUrl(''); + link.href = linkService.getAnchorUrl(''); link.onclick = function pageViewSetupNamedActionOnClick() { - // See PDF reference, table 8.45 - Named action - switch (action) { - case 'GoToPage': - document.getElementById('pageNumber').focus(); - break; - - case 'GoBack': - PDFHistory.back(); - break; - - case 'GoForward': - PDFHistory.forward(); - break; - - case 'Find': - if (!PDFView.supportsIntegratedFind) { - PDFView.findBar.toggle(); - } - break; - - case 'NextPage': - PDFView.page++; - break; - - case 'PrevPage': - PDFView.page--; - break; - - case 'LastPage': - PDFView.page = PDFView.pages.length; - break; - - case 'FirstPage': - PDFView.page = 1; - break; - - default: - break; // No action according to spec - } + linkService.executeNamedAction(action); return false; }; link.className = 'internalLink'; @@ -375,99 +357,6 @@ var PageView = function pageView(container, id, scale, return this.viewport.convertToPdfPoint(x, y); }; - this.scrollIntoView = function pageViewScrollIntoView(dest) { - if (PresentationMode.active) { - if (PDFView.page !== this.id) { - // Avoid breaking PDFView.getVisiblePages in presentation mode. - PDFView.page = this.id; - return; - } - dest = null; - PDFView.setScale(PDFView.currentScaleValue, true, true); - } - if (!dest) { - scrollIntoView(div); - return; - } - - var x = 0, y = 0; - var width = 0, height = 0, widthScale, heightScale; - var changeOrientation = (this.rotation % 180 === 0 ? false : true); - var pageWidth = (changeOrientation ? this.height : this.width) / - this.scale / CSS_UNITS; - var pageHeight = (changeOrientation ? this.width : this.height) / - this.scale / CSS_UNITS; - var scale = 0; - switch (dest[1].name) { - case 'XYZ': - x = dest[2]; - y = dest[3]; - scale = dest[4]; - // If x and/or y coordinates are not supplied, default to - // _top_ left of the page (not the obvious bottom left, - // since aligning the bottom of the intended page with the - // top of the window is rarely helpful). - x = x !== null ? x : 0; - y = y !== null ? y : pageHeight; - break; - case 'Fit': - case 'FitB': - scale = 'page-fit'; - break; - case 'FitH': - case 'FitBH': - y = dest[2]; - scale = 'page-width'; - break; - case 'FitV': - case 'FitBV': - x = dest[2]; - width = pageWidth; - height = pageHeight; - scale = 'page-height'; - break; - case 'FitR': - x = dest[2]; - y = dest[3]; - width = dest[4] - x; - height = dest[5] - y; - widthScale = (PDFView.container.clientWidth - SCROLLBAR_PADDING) / - width / CSS_UNITS; - heightScale = (PDFView.container.clientHeight - SCROLLBAR_PADDING) / - height / CSS_UNITS; - scale = Math.min(Math.abs(widthScale), Math.abs(heightScale)); - break; - default: - return; - } - - if (scale && scale !== PDFView.currentScale) { - PDFView.setScale(scale, true, true); - } else if (PDFView.currentScale === UNKNOWN_SCALE) { - PDFView.setScale(DEFAULT_SCALE, true, true); - } - - if (scale === 'page-fit' && !dest[4]) { - scrollIntoView(div); - return; - } - - var boundingRect = [ - this.viewport.convertToViewportPoint(x, y), - this.viewport.convertToViewportPoint(x + width, y + height) - ]; - var left = Math.min(boundingRect[0][0], boundingRect[1][0]); - var top = Math.min(boundingRect[0][1], boundingRect[1][1]); - - scrollIntoView(div, { left: left, top: top }); - }; - - this.getTextContent = function pageviewGetTextContent() { - return PDFView.getPage(this.id).then(function(pdfPage) { - return pdfPage.getTextContent(); - }); - }; - this.draw = function pageviewDraw(callback) { var pdfPage = this.pdfPage; @@ -475,7 +364,7 @@ var PageView = function pageView(container, id, scale, return; } if (!pdfPage) { - var promise = PDFView.getPage(this.id); + var promise = this.pageSource.getPage(); promise.then(function(pdfPage) { delete this.pagePdfPromise; this.setPdfPage(pdfPage); @@ -543,6 +432,7 @@ var PageView = function pageView(container, id, scale, canvas._viewport = viewport; var textLayerDiv = null; + var textLayer = null; if (!PDFJS.disableTextLayer) { textLayerDiv = document.createElement('div'); textLayerDiv.className = 'textLayer'; @@ -554,16 +444,12 @@ var PageView = function pageView(container, id, scale, } else { div.appendChild(textLayerDiv); } + + textLayer = this.viewer.createTextLayerBuilder(textLayerDiv, this.id - 1, + this.viewport); } - var textLayer = this.textLayer = - textLayerDiv ? new TextLayerBuilder({ - textLayerDiv: textLayerDiv, - pageIndex: this.id - 1, - lastScrollSource: PDFView, - viewport: this.viewport, - isViewerInPresentationMode: PresentationMode.active, - findController: PDFView.findController - }) : null; + this.textLayer = textLayer; + // TODO(mack): use data attributes to store these ctx._scaleX = outputScale.sx; ctx._scaleY = outputScale.sy; @@ -598,22 +484,7 @@ var PageView = function pageView(container, id, scale, self.zoomLayer = null; } -//#if (FIREFOX || MOZCENTRAL) -// if (self.textLayer && self.textLayer.textDivs && -// self.textLayer.textDivs.length > 0 && -// !PDFView.supportsDocumentColors) { -// console.error(mozL10n.get('document_colors_disabled', null, -// 'PDF documents are not allowed to use their own colors: ' + -// '\'Allow pages to choose their own colors\' ' + -// 'is deactivated in the browser.')); -// PDFView.fallback(); -// } -//#endif - if (error) { - PDFView.error(mozL10n.get('rendering_error', null, - 'An error occurred while rendering the page.'), error); - } - + self.error = error; self.stats = pdfPage.stats; self.updateStats(); if (self.onAfterDraw) { @@ -626,18 +497,6 @@ var PageView = function pageView(container, id, scale, }); div.dispatchEvent(event); -//#if (FIREFOX || MOZCENTRAL) -// FirefoxCom.request('reportTelemetry', JSON.stringify({ -// type: 'pageInfo' -// })); -// // It is a good time to report stream and font types -// PDFView.pdfDocument.getStats().then(function (stats) { -// FirefoxCom.request('reportTelemetry', JSON.stringify({ -// type: 'documentStats', -// stats: stats -// })); -// }); -//#endif callback(); } @@ -646,7 +505,7 @@ var PageView = function pageView(container, id, scale, viewport: this.viewport, // intent: 'default', // === 'display' continueCallback: function pdfViewcContinueCallback(cont) { - if (PDFView.highestPriorityPage !== 'page' + self.id) { + if (!self.renderingQueue.isHighestPriority(self)) { self.renderingState = RenderingStates.PAUSED; self.resume = function resumeCallback() { self.renderingState = RenderingStates.RUNNING; @@ -663,7 +522,7 @@ var PageView = function pageView(container, id, scale, function pdfPageRenderCallback() { pageViewDrawCallback(null); if (textLayer) { - self.getTextContent().then( + self.pdfPage.getTextContent().then( function textContentResolved(textContent) { textLayer.setTextContent(textContent); } diff --git a/web/pdf_find_controller.js b/web/pdf_find_controller.js index 6459faf27242f..1aa9121097845 100644 --- a/web/pdf_find_controller.js +++ b/web/pdf_find_controller.js @@ -13,10 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals PDFJS, FindStates, FirefoxCom, Promise */ +/* globals PDFJS, FirefoxCom, Promise */ 'use strict'; +var FindStates = { + FIND_FOUND: 0, + FIND_NOTFOUND: 1, + FIND_WRAPPED: 2, + FIND_PENDING: 3 +}; + /** * Provides "search" or "find" functionality for the PDF. * This object actually performs the search for a given string. @@ -37,11 +44,12 @@ var PDFFindController = (function PDFFindControllerClosure() { pageIdx: null, matchIdx: null }; + this.pagesToSearch = null; this.resumePageIdx = null; this.state = null; this.dirtyMatch = false; this.findTimeout = null; - this.pdfPageSource = options.pdfPageSource || null; + this.pdfViewer = options.pdfViewer || null; this.integratedFind = options.integratedFind || false; this.charactersToNormalize = { '\u2018': '\'', // Left single quotation mark @@ -137,7 +145,7 @@ var PDFFindController = (function PDFFindControllerClosure() { this.pageContents = []; var extractTextPromisesResolves = []; - var numPages = this.pdfPageSource.pdfDocument.numPages; + var numPages = this.pdfViewer.pagesCount; for (var i = 0; i < numPages; i++) { this.extractTextPromises.push(new Promise(function (resolve) { extractTextPromisesResolves.push(resolve); @@ -146,7 +154,7 @@ var PDFFindController = (function PDFFindControllerClosure() { var self = this; function extractPageText(pageIndex) { - self.pdfPageSource.pages[pageIndex].getTextContent().then( + self.pdfViewer.getPageTextContent(pageIndex).then( function textContentResolved(textContent) { var textItems = textContent.items; var str = []; @@ -159,7 +167,7 @@ var PDFFindController = (function PDFFindControllerClosure() { self.pageContents.push(str.join('')); extractTextPromisesResolves[pageIndex](pageIndex); - if ((pageIndex + 1) < self.pdfPageSource.pages.length) { + if ((pageIndex + 1) < self.pdfViewer.pagesCount) { extractPageText(pageIndex + 1); } } @@ -189,13 +197,13 @@ var PDFFindController = (function PDFFindControllerClosure() { }, updatePage: function PDFFindController_updatePage(index) { - var page = this.pdfPageSource.pages[index]; + var page = this.pdfViewer.getPageView(index); if (this.selected.pageIdx === index) { // If the page is selected, scroll the page into view, which triggers // rendering the page, which adds the textLayer. Once the textLayer is // build, it will scroll onto the selected match. - page.scrollIntoView(); + this.pdfViewer.scrollPageIntoView(index + 1); } if (page.textLayer) { @@ -205,8 +213,8 @@ var PDFFindController = (function PDFFindControllerClosure() { nextMatch: function PDFFindController_nextMatch() { var previous = this.state.findPrevious; - var currentPageIndex = this.pdfPageSource.page - 1; - var numPages = this.pdfPageSource.pages.length; + var currentPageIndex = this.pdfViewer.currentPageNumber - 1; + var numPages = this.pdfViewer.pagesCount; this.active = true; @@ -248,6 +256,8 @@ var PDFFindController = (function PDFFindControllerClosure() { } var offset = this.offset; + // Keep track of how many pages we should maximally iterate through. + this.pagesToSearch = numPages; // If there's already a matchIdx that means we are iterating through a // page's matches. if (offset.matchIdx !== null) { @@ -286,8 +296,8 @@ var PDFFindController = (function PDFFindControllerClosure() { this.advanceOffsetPage(previous); if (offset.wrapped) { offset.matchIdx = null; - if (!this.hadMatch) { - // No point in wrapping, there were no matches. + if (this.pagesToSearch < 0) { + // No point in wrapping again, there were no matches. this.updateMatch(false); // while matches were not found, searching for a page // with matches should nevertheless halt. @@ -320,11 +330,12 @@ var PDFFindController = (function PDFFindControllerClosure() { var numPages = this.extractTextPromises.length; offset.pageIdx = (previous ? offset.pageIdx - 1 : offset.pageIdx + 1); offset.matchIdx = null; + + this.pagesToSearch--; if (offset.pageIdx >= numPages || offset.pageIdx < 0) { offset.pageIdx = (previous ? numPages - 1 : 0); offset.wrapped = true; - return; } }, @@ -346,7 +357,7 @@ var PDFFindController = (function PDFFindControllerClosure() { this.updateUIState(state, this.state.findPrevious); if (this.selected.pageIdx !== -1) { - this.updatePage(this.selected.pageIdx, true); + this.updatePage(this.selected.pageIdx); } }, diff --git a/web/pdf_history.js b/web/pdf_history.js index 79161da259786..e15e03a66d595 100644 --- a/web/pdf_history.js +++ b/web/pdf_history.js @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals PDFJS, PDFView, PresentationMode */ +/* globals PDFJS, PresentationMode */ 'use strict'; @@ -22,12 +22,11 @@ var PDFHistory = { initialized: false, initialDestination: null, - initialize: function pdfHistoryInitialize(fingerprint) { - if (PDFJS.disableHistory || PDFView.isViewerEmbedded) { - // The browsing history is only enabled when the viewer is standalone, - // i.e. not when it is embedded in a web page. - return; - } + /** + * @param {string} fingerprint + * @param {IPDFLinkService} linkService + */ + initialize: function pdfHistoryInitialize(fingerprint, linkService) { this.initialized = true; this.reInitialized = false; this.allowHashChange = true; @@ -42,6 +41,7 @@ var PDFHistory = { this.nextHashParam = ''; this.fingerprint = fingerprint; + this.linkService = linkService; this.currentUid = this.uid = 0; this.current = {}; @@ -52,7 +52,7 @@ var PDFHistory = { if (state.target.dest) { this.initialDestination = state.target.dest; } else { - PDFView.initialBookmark = state.target.hash; + linkService.setHash(state.target.hash); } this.currentUid = state.uid; this.uid = state.uid + 1; @@ -203,7 +203,7 @@ var PDFHistory = { params.hash = (this.current.hash && this.current.dest && this.current.dest === params.dest) ? this.current.hash : - PDFView.getDestinationHash(params.dest).split('#')[1]; + this.linkService.getDestinationHash(params.dest).split('#')[1]; } if (params.page) { params.page |= 0; @@ -212,7 +212,7 @@ var PDFHistory = { var target = window.history.state.target; if (!target) { // Invoked when the user specifies an initial bookmark, - // thus setting PDFView.initialBookmark, when the document is loaded. + // thus setting initialBookmark, when the document is loaded. this._pushToHistory(params, false); this.previousHash = window.location.hash.substring(1); } @@ -337,9 +337,9 @@ var PDFHistory = { this.historyUnlocked = false; if (state.target.dest) { - PDFView.navigateTo(state.target.dest); + this.linkService.navigateTo(state.target.dest); } else { - PDFView.setHash(state.target.hash); + this.linkService.setHash(state.target.hash); } this.currentUid = state.uid; if (state.uid > this.uid) { diff --git a/web/pdf_rendering_queue.js b/web/pdf_rendering_queue.js new file mode 100644 index 0000000000000..c7b1b3aba1d83 --- /dev/null +++ b/web/pdf_rendering_queue.js @@ -0,0 +1,176 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* 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. + */ + +'use strict'; + +var CLEANUP_TIMEOUT = 30000; + +var RenderingStates = { + INITIAL: 0, + RUNNING: 1, + PAUSED: 2, + FINISHED: 3 +}; + +/** + * Controls rendering of the views for pages and thumbnails. + * @class + */ +var PDFRenderingQueue = (function PDFRenderingQueueClosure() { + /** + * @constructs + */ + function PDFRenderingQueue() { + this.pdfViewer = null; + this.pdfThumbnailViewer = null; + this.onIdle = null; + + this.highestPriorityPage = null; + this.idleTimeout = null; + this.printing = false; + this.isThumbnailViewEnabled = false; + } + + PDFRenderingQueue.prototype = /** @lends PDFRenderingQueue.prototype */ { + /** + * @param {PDFViewer} pdfViewer + */ + setViewer: function PDFRenderingQueue_setViewer(pdfViewer) { + this.pdfViewer = pdfViewer; + }, + + /** + * @param {PDFThumbnailViewer} pdfThumbnailViewer + */ + setThumbnailViewer: + function PDFRenderingQueue_setThumbnailViewer(pdfThumbnailViewer) { + this.pdfThumbnailViewer = pdfThumbnailViewer; + }, + + /** + * @param {IRenderableView} view + * @returns {boolean} + */ + isHighestPriority: function PDFRenderingQueue_isHighestPriority(view) { + return this.highestPriorityPage === view.renderingId; + }, + + renderHighestPriority: function + PDFRenderingQueue_renderHighestPriority(currentlyVisiblePages) { + if (this.idleTimeout) { + clearTimeout(this.idleTimeout); + this.idleTimeout = null; + } + + // Pages have a higher priority than thumbnails, so check them first. + if (this.pdfViewer.forceRendering(currentlyVisiblePages)) { + return; + } + // No pages needed rendering so check thumbnails. + if (this.pdfThumbnailViewer && this.isThumbnailViewEnabled) { + if (this.pdfThumbnailViewer.forceRendering()) { + return; + } + } + + if (this.printing) { + // If printing is currently ongoing do not reschedule cleanup. + return; + } + + if (this.onIdle) { + this.idleTimeout = setTimeout(this.onIdle.bind(this), CLEANUP_TIMEOUT); + } + }, + + getHighestPriority: function + PDFRenderingQueue_getHighestPriority(visible, views, scrolledDown) { + // The state has changed figure out which page has the highest priority to + // render next (if any). + // Priority: + // 1 visible pages + // 2 if last scrolled down page after the visible pages + // 2 if last scrolled up page before the visible pages + var visibleViews = visible.views; + + var numVisible = visibleViews.length; + if (numVisible === 0) { + return false; + } + for (var i = 0; i < numVisible; ++i) { + var view = visibleViews[i].view; + if (!this.isViewFinished(view)) { + return view; + } + } + + // All the visible views have rendered, try to render next/previous pages. + if (scrolledDown) { + var nextPageIndex = visible.last.id; + // ID's start at 1 so no need to add 1. + if (views[nextPageIndex] && + !this.isViewFinished(views[nextPageIndex])) { + return views[nextPageIndex]; + } + } else { + var previousPageIndex = visible.first.id - 2; + if (views[previousPageIndex] && + !this.isViewFinished(views[previousPageIndex])) { + return views[previousPageIndex]; + } + } + // Everything that needs to be rendered has been. + return null; + }, + + /** + * @param {IRenderableView} view + * @returns {boolean} + */ + isViewFinished: function PDFRenderingQueue_isViewFinished(view) { + return view.renderingState === RenderingStates.FINISHED; + }, + + /** + * Render a page or thumbnail view. This calls the appropriate function + * based on the views state. If the view is already rendered it will return + * false. + * @param {IRenderableView} view + */ + renderView: function PDFRenderingQueue_renderView(view) { + var state = view.renderingState; + switch (state) { + case RenderingStates.FINISHED: + return false; + case RenderingStates.PAUSED: + this.highestPriorityPage = view.renderingId; + view.resume(); + break; + case RenderingStates.RUNNING: + this.highestPriorityPage = view.renderingId; + break; + case RenderingStates.INITIAL: + this.highestPriorityPage = view.renderingId; + view.draw(this.renderHighestPriority.bind(this)); + break; + } + return true; + }, + }; + + return PDFRenderingQueue; +})(); diff --git a/web/pdf_viewer.component.js b/web/pdf_viewer.component.js new file mode 100644 index 0000000000000..22c48385489d9 --- /dev/null +++ b/web/pdf_viewer.component.js @@ -0,0 +1,32 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* Copyright 2014 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. + */ +/*jshint globalstrict: false */ +/* globals PDFJS, PDFViewer */ + +// Initializing PDFJS global object (if still undefined) +if (typeof PDFJS === 'undefined') { + (typeof window !== 'undefined' ? window : this).PDFJS = {}; +} + +(function pdfViewerWrapper() { + 'use strict'; + +//#include ui_utils.js +//#include pdf_viewer.js + + PDFJS.PDFViewer = PDFViewer; +}).call((typeof window === 'undefined') ? this : window); diff --git a/web/pdf_viewer.css b/web/pdf_viewer.css new file mode 100644 index 0000000000000..0c907fcb7dfc6 --- /dev/null +++ b/web/pdf_viewer.css @@ -0,0 +1,123 @@ +/* Copyright 2014 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. + */ +@import url(text_layer_builder.css); + +.pdfViewer .canvasWrapper { + overflow: hidden; +} + +.pdfViewer .page { + direction: ltr; + width: 816px; + height: 1056px; + margin: 1px auto -8px auto; + position: relative; + overflow: visible; + border: 9px solid transparent; + background-clip: content-box; + border-image: url(images/shadow.png) 9 9 repeat; + background-color: white; +} + +.pdfViewer .page canvas { + margin: 0; + display: block; +} + +.pdfViewer .page .loadingIcon { + position: absolute; + display: block; + left: 0; + top: 0; + right: 0; + bottom: 0; + background: url('images/loading-icon.gif') center no-repeat; +} + +.pdfViewer .page .annotLink > a:hover { + opacity: 0.2; + background: #ff0; + box-shadow: 0px 2px 10px #ff0; +} + +:-webkit-full-screen .pdfViewer .page { + margin-bottom: 100%; + border: 0; +} + +:-moz-full-screen .pdfViewer .page { + margin-bottom: 100%; + border: 0; +} + +:-ms-fullscreen .pdfViewer .page { + margin-bottom: 100% !important; + border: 0; +} + +:fullscreen .pdfViewer .page { + margin-bottom: 100%; + border: 0; +} + +.pdfViewer .page .annotationHighlight { + position: absolute; + border: 2px #FFFF99 solid; +} + +.pdfViewer .page .annotText > img { + position: absolute; + cursor: pointer; +} + +.pdfViewer .page .annotTextContentWrapper { + position: absolute; + width: 20em; +} + +.pdfViewer .page .annotTextContent { + z-index: 200; + float: left; + max-width: 20em; + background-color: #FFFF99; + box-shadow: 0px 2px 5px #333; + border-radius: 2px; + padding: 0.6em; + cursor: pointer; +} + +.pdfViewer .page .annotTextContent > h1 { + font-size: 1em; + border-bottom: 1px solid #000000; + padding-bottom: 0.2em; +} + +.pdfViewer .page .annotTextContent > p { + padding-top: 0.2em; +} + +.pdfViewer .page .annotLink > a { + position: absolute; + font-size: 1em; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.pdfViewer .page .annotLink > a /* -ms-a */ { + background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAA\ + LAAAAAABAAEAAAIBRAA7") 0 0 repeat; +} diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js new file mode 100644 index 0000000000000..47e109fabac72 --- /dev/null +++ b/web/pdf_viewer.js @@ -0,0 +1,725 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* Copyright 2014 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 watchScroll, Cache, DEFAULT_CACHE_SIZE, PageView, UNKNOWN_SCALE, + SCROLLBAR_PADDING, VERTICAL_PADDING, MAX_AUTO_SCALE, CSS_UNITS, + DEFAULT_SCALE, scrollIntoView, getVisibleElements, RenderingStates, + PDFJS, Promise, TextLayerBuilder, PDFRenderingQueue */ + +'use strict'; + +var PresentationModeState = { + UNKNOWN: 0, + NORMAL: 1, + CHANGING: 2, + FULLSCREEN: 3, +}; + +var IGNORE_CURRENT_POSITION_ON_ZOOM = false; + +//#include pdf_rendering_queue.js +//#include page_view.js +//#include text_layer_builder.js + +/** + * @typedef {Object} PDFViewerOptions + * @property {HTMLDivElement} container - The container for the viewer element. + * @property {HTMLDivElement} viewer - (optional) The viewer element. + * @property {IPDFLinkService} linkService - The navigation/linking service. + * @property {PDFRenderingQueue} renderingQueue - (optional) The rendering + * queue object. + */ + +/** + * Simple viewer control to display PDF content/pages. + * @class + * @implements {ILastScrollSource} + * @implements {IRenderableView} + */ +var PDFViewer = (function pdfViewer() { + /** + * @constructs PDFViewer + * @param {PDFViewerOptions} options + */ + function PDFViewer(options) { + this.container = options.container; + this.viewer = options.viewer || options.container.firstElementChild; + this.linkService = options.linkService || new SimpleLinkService(this); + + this.defaultRenderingQueue = !options.renderingQueue; + if (this.defaultRenderingQueue) { + // Custom rendering queue is not specified, using default one + this.renderingQueue = new PDFRenderingQueue(); + this.renderingQueue.setViewer(this); + } else { + this.renderingQueue = options.renderingQueue; + } + + this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this)); + this.lastScroll = 0; + this.updateInProgress = false; + this.presentationModeState = PresentationModeState.UNKNOWN; + this._resetView(); + } + + PDFViewer.prototype = /** @lends PDFViewer.prototype */{ + get pagesCount() { + return this.pages.length; + }, + + getPageView: function (index) { + return this.pages[index]; + }, + + get currentPageNumber() { + return this._currentPageNumber; + }, + + set currentPageNumber(val) { + if (!this.pdfDocument) { + this._currentPageNumber = val; + return; + } + + var event = document.createEvent('UIEvents'); + event.initUIEvent('pagechange', true, true, window, 0); + event.updateInProgress = this.updateInProgress; + + if (!(0 < val && val <= this.pagesCount)) { + event.pageNumber = this._currentPageNumber; + event.previousPageNumber = val; + this.container.dispatchEvent(event); + return; + } + + this.pages[val - 1].updateStats(); + event.previousPageNumber = this._currentPageNumber; + this._currentPageNumber = val; + event.pageNumber = val; + this.container.dispatchEvent(event); + }, + + /** + * @returns {number} + */ + get currentScale() { + return this._currentScale; + }, + + /** + * @param {number} val - Scale of the pages in percents. + */ + set currentScale(val) { + if (isNaN(val)) { + throw new Error('Invalid numeric scale'); + } + if (!this.pdfDocument) { + this._currentScale = val; + this._currentScaleValue = val.toString(); + return; + } + this._setScale(val, false); + }, + + /** + * @returns {string} + */ + get currentScaleValue() { + return this._currentScaleValue; + }, + + /** + * @param val - The scale of the pages (in percent or predefined value). + */ + set currentScaleValue(val) { + if (!this.pdfDocument) { + this._currentScale = isNaN(val) ? UNKNOWN_SCALE : val; + this._currentScaleValue = val; + return; + } + this._setScale(val, false); + }, + + /** + * @returns {number} + */ + get pagesRotation() { + return this._pagesRotation; + }, + + /** + * @param {number} rotation - The rotation of the pages (0, 90, 180, 270). + */ + set pagesRotation(rotation) { + this._pagesRotation = rotation; + + for (var i = 0, l = this.pages.length; i < l; i++) { + var page = this.pages[i]; + page.update(page.scale, rotation); + } + + this._setScale(this._currentScaleValue, true); + }, + + /** + * @param pdfDocument {PDFDocument} + */ + setDocument: function (pdfDocument) { + if (this.pdfDocument) { + this._resetView(); + } + + this.pdfDocument = pdfDocument; + if (!pdfDocument) { + return; + } + + var pagesCount = pdfDocument.numPages; + var pagesRefMap = this.pagesRefMap = {}; + var self = this; + + var resolvePagesPromise; + var pagesPromise = new Promise(function (resolve) { + resolvePagesPromise = resolve; + }); + this.pagesPromise = pagesPromise; + pagesPromise.then(function () { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('pagesloaded', true, true, { + pagesCount: pagesCount + }); + self.container.dispatchEvent(event); + }); + + var isOnePageRenderedResolved = false; + var resolveOnePageRendered = null; + var onePageRendered = new Promise(function (resolve) { + resolveOnePageRendered = resolve; + }); + this.onePageRendered = onePageRendered; + + var bindOnAfterDraw = function (pageView) { + // when page is painted, using the image as thumbnail base + pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() { + if (!isOnePageRenderedResolved) { + isOnePageRenderedResolved = true; + resolveOnePageRendered(); + } + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('pagerendered', true, true, { + pageNumber: pageView.id + }); + self.container.dispatchEvent(event); + }; + }; + + var firstPagePromise = pdfDocument.getPage(1); + this.firstPagePromise = firstPagePromise; + + // Fetch a single page so we can get a viewport that will be the default + // viewport for all pages + return firstPagePromise.then(function(pdfPage) { + var scale = this._currentScale || 1.0; + var viewport = pdfPage.getViewport(scale * CSS_UNITS); + for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) { + var pageSource = new PDFPageSource(pdfDocument, pageNum); + var pageView = new PageView(this.viewer, pageNum, scale, + viewport.clone(), this.linkService, + this.renderingQueue, this.cache, + pageSource, this); + bindOnAfterDraw(pageView); + this.pages.push(pageView); + } + + // Fetch all the pages since the viewport is needed before printing + // starts to create the correct size canvas. Wait until one page is + // rendered so we don't tie up too many resources early on. + onePageRendered.then(function () { + if (!PDFJS.disableAutoFetch) { + var getPagesLeft = pagesCount; + for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) { + pdfDocument.getPage(pageNum).then(function (pageNum, pdfPage) { + var pageView = self.pages[pageNum - 1]; + if (!pageView.pdfPage) { + pageView.setPdfPage(pdfPage); + } + var refStr = pdfPage.ref.num + ' ' + pdfPage.ref.gen + ' R'; + pagesRefMap[refStr] = pageNum; + getPagesLeft--; + if (!getPagesLeft) { + resolvePagesPromise(); + } + }.bind(null, pageNum)); + } + } else { + // XXX: Printing is semi-broken with auto fetch disabled. + resolvePagesPromise(); + } + }); + + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('pagesinit', true, true, null); + self.container.dispatchEvent(event); + + if (this.defaultRenderingQueue) { + this.update(); + } + }.bind(this)); + }, + + _resetView: function () { + this.cache = new Cache(DEFAULT_CACHE_SIZE); + this.pages = []; + this._currentPageNumber = 1; + this._currentScale = UNKNOWN_SCALE; + this._currentScaleValue = null; + this.location = null; + this._pagesRotation = 0; + + var container = this.viewer; + while (container.hasChildNodes()) { + container.removeChild(container.lastChild); + } + }, + + _scrollUpdate: function () { + this.lastScroll = Date.now(); + + if (this.pagesCount === 0) { + return; + } + this.update(); + }, + + _setScaleUpdatePages: function pdfViewer_setScaleUpdatePages( + newScale, newValue, noScroll, preset) { + this._currentScaleValue = newValue; + if (newScale === this._currentScale) { + return; + } + for (var i = 0, ii = this.pages.length; i < ii; i++) { + this.pages[i].update(newScale); + } + this._currentScale = newScale; + + if (!noScroll) { + var page = this._currentPageNumber, dest; + var inPresentationMode = + this.presentationModeState === PresentationModeState.CHANGING || + this.presentationModeState === PresentationModeState.FULLSCREEN; + if (this.location && !inPresentationMode && + !IGNORE_CURRENT_POSITION_ON_ZOOM) { + page = this.location.pageNumber; + dest = [null, { name: 'XYZ' }, this.location.left, + this.location.top, null]; + } + this.scrollPageIntoView(page, dest); + } + + var event = document.createEvent('UIEvents'); + event.initUIEvent('scalechange', true, true, window, 0); + event.scale = newScale; + if (preset) { + event.presetValue = newValue; + } + this.container.dispatchEvent(event); + }, + + _setScale: function pdfViewer_setScale(value, noScroll) { + if (value === 'custom') { + return; + } + var scale = parseFloat(value); + + if (scale > 0) { + this._setScaleUpdatePages(scale, value, noScroll, false); + } else { + var currentPage = this.pages[this._currentPageNumber - 1]; + if (!currentPage) { + return; + } + var inPresentationMode = + this.presentationModeState === PresentationModeState.FULLSCREEN; + var hPadding = inPresentationMode ? 0 : SCROLLBAR_PADDING; + var vPadding = inPresentationMode ? 0 : VERTICAL_PADDING; + var pageWidthScale = (this.container.clientWidth - hPadding) / + currentPage.width * currentPage.scale; + var pageHeightScale = (this.container.clientHeight - vPadding) / + currentPage.height * currentPage.scale; + switch (value) { + case 'page-actual': + scale = 1; + break; + case 'page-width': + scale = pageWidthScale; + break; + case 'page-height': + scale = pageHeightScale; + break; + case 'page-fit': + scale = Math.min(pageWidthScale, pageHeightScale); + break; + case 'auto': + var isLandscape = (currentPage.width > currentPage.height); + // For pages in landscape mode, fit the page height to the viewer + // *unless* the page would thus become too wide to fit horizontally. + var horizontalScale = isLandscape ? + Math.min(pageHeightScale, pageWidthScale) : pageWidthScale; + scale = Math.min(MAX_AUTO_SCALE, horizontalScale); + break; + default: + console.error('pdfViewSetScale: \'' + value + + '\' is an unknown zoom value.'); + return; + } + this._setScaleUpdatePages(scale, value, noScroll, true); + } + }, + + /** + * Scrolls page into view. + * @param {number} pageNumber + * @param {Array} dest - (optional) original PDF destination array: + * + */ + scrollPageIntoView: function PDFViewer_scrollPageIntoView(pageNumber, + dest) { + var pageView = this.pages[pageNumber - 1]; + var pageViewDiv = pageView.el; + + if (this.presentationModeState === + PresentationModeState.FULLSCREEN) { + if (this.linkService.page !== pageView.id) { + // Avoid breaking getVisiblePages in presentation mode. + this.linkService.page = pageView.id; + return; + } + dest = null; + // Fixes the case when PDF has different page sizes. + this._setScale(this.currentScaleValue, true); + } + if (!dest) { + scrollIntoView(pageViewDiv); + return; + } + + var x = 0, y = 0; + var width = 0, height = 0, widthScale, heightScale; + var changeOrientation = (pageView.rotation % 180 === 0 ? false : true); + var pageWidth = (changeOrientation ? pageView.height : pageView.width) / + pageView.scale / CSS_UNITS; + var pageHeight = (changeOrientation ? pageView.width : pageView.height) / + pageView.scale / CSS_UNITS; + var scale = 0; + switch (dest[1].name) { + case 'XYZ': + x = dest[2]; + y = dest[3]; + scale = dest[4]; + // If x and/or y coordinates are not supplied, default to + // _top_ left of the page (not the obvious bottom left, + // since aligning the bottom of the intended page with the + // top of the window is rarely helpful). + x = x !== null ? x : 0; + y = y !== null ? y : pageHeight; + break; + case 'Fit': + case 'FitB': + scale = 'page-fit'; + break; + case 'FitH': + case 'FitBH': + y = dest[2]; + scale = 'page-width'; + break; + case 'FitV': + case 'FitBV': + x = dest[2]; + width = pageWidth; + height = pageHeight; + scale = 'page-height'; + break; + case 'FitR': + x = dest[2]; + y = dest[3]; + width = dest[4] - x; + height = dest[5] - y; + var viewerContainer = this.container; + widthScale = (viewerContainer.clientWidth - SCROLLBAR_PADDING) / + width / CSS_UNITS; + heightScale = (viewerContainer.clientHeight - SCROLLBAR_PADDING) / + height / CSS_UNITS; + scale = Math.min(Math.abs(widthScale), Math.abs(heightScale)); + break; + default: + return; + } + + if (scale && scale !== this.currentScale) { + this.currentScaleValue = scale; + } else if (this.currentScale === UNKNOWN_SCALE) { + this.currentScaleValue = DEFAULT_SCALE; + } + + if (scale === 'page-fit' && !dest[4]) { + scrollIntoView(pageViewDiv); + return; + } + + var boundingRect = [ + pageView.viewport.convertToViewportPoint(x, y), + pageView.viewport.convertToViewportPoint(x + width, y + height) + ]; + var left = Math.min(boundingRect[0][0], boundingRect[1][0]); + var top = Math.min(boundingRect[0][1], boundingRect[1][1]); + + scrollIntoView(pageViewDiv, { left: left, top: top }); + }, + + _updateLocation: function (firstPage) { + var currentScale = this._currentScale; + var currentScaleValue = this._currentScaleValue; + var normalizedScaleValue = + parseFloat(currentScaleValue) === currentScale ? + Math.round(currentScale * 10000) / 100 : currentScaleValue; + + var pageNumber = firstPage.id; + var pdfOpenParams = '#page=' + pageNumber; + pdfOpenParams += '&zoom=' + normalizedScaleValue; + var currentPageView = this.pages[pageNumber - 1]; + var container = this.container; + var topLeft = currentPageView.getPagePoint( + (container.scrollLeft - firstPage.x), + (container.scrollTop - firstPage.y)); + var intLeft = Math.round(topLeft[0]); + var intTop = Math.round(topLeft[1]); + pdfOpenParams += ',' + intLeft + ',' + intTop; + + this.location = { + pageNumber: pageNumber, + scale: normalizedScaleValue, + top: intTop, + left: intLeft, + pdfOpenParams: pdfOpenParams + }; + }, + + update: function () { + var visible = this._getVisiblePages(); + var visiblePages = visible.views; + if (visiblePages.length === 0) { + return; + } + + this.updateInProgress = true; + + var suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE, + 2 * visiblePages.length + 1); + this.cache.resize(suggestedCacheSize); + + this.renderingQueue.renderHighestPriority(visible); + + var currentId = this.currentPageNumber; + var firstPage = visible.first; + + for (var i = 0, ii = visiblePages.length, stillFullyVisible = false; + i < ii; ++i) { + var page = visiblePages[i]; + + if (page.percent < 100) { + break; + } + if (page.id === currentId) { + stillFullyVisible = true; + break; + } + } + + if (!stillFullyVisible) { + currentId = visiblePages[0].id; + } + + if (this.presentationModeState !== PresentationModeState.FULLSCREEN) { + this.currentPageNumber = currentId; + } + + this._updateLocation(firstPage); + + this.updateInProgress = false; + + var event = document.createEvent('UIEvents'); + event.initUIEvent('updateviewarea', true, true, window, 0); + this.container.dispatchEvent(event); + }, + + containsElement: function (element) { + return this.container.contains(element); + }, + + focus: function () { + this.container.focus(); + }, + + blur: function () { + this.container.blur(); + }, + + get isHorizontalScrollbarEnabled() { + return (this.presentationModeState === PresentationModeState.FULLSCREEN ? + false : (this.container.scrollWidth > this.container.clientWidth)); + }, + + _getVisiblePages: function () { + if (this.presentationModeState !== PresentationModeState.FULLSCREEN) { + return getVisibleElements(this.container, this.pages, true); + } else { + // The algorithm in getVisibleElements doesn't work in all browsers and + // configurations when presentation mode is active. + var visible = []; + var currentPage = this.pages[this._currentPageNumber - 1]; + visible.push({ id: currentPage.id, view: currentPage }); + return { first: currentPage, last: currentPage, views: visible }; + } + }, + + cleanup: function () { + for (var i = 0, ii = this.pages.length; i < ii; i++) { + if (this.pages[i] && + this.pages[i].renderingState !== RenderingStates.FINISHED) { + this.pages[i].reset(); + } + } + }, + + forceRendering: function (currentlyVisiblePages) { + var visiblePages = currentlyVisiblePages || this._getVisiblePages(); + var pageView = this.renderingQueue.getHighestPriority(visiblePages, + this.pages, + this.scroll.down); + if (pageView) { + this.renderingQueue.renderView(pageView); + return true; + } + return false; + }, + + getPageTextContent: function (pageIndex) { + return this.pdfDocument.getPage(pageIndex + 1).then(function (page) { + return page.getTextContent(); + }); + }, + + /** + * @param textLayerDiv {HTMLDivElement} + * @param pageIndex {number} + * @param viewport {PageViewport} + * @returns {TextLayerBuilder} + */ + createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport) { + var isViewerInPresentationMode = + this.presentationModeState === PresentationModeState.FULLSCREEN; + return new TextLayerBuilder({ + textLayerDiv: textLayerDiv, + pageIndex: pageIndex, + viewport: viewport, + lastScrollSource: this, + isViewerInPresentationMode: isViewerInPresentationMode, + findController: this.findController + }); + }, + + setFindController: function (findController) { + this.findController = findController; + }, + }; + + return PDFViewer; +})(); + +var SimpleLinkService = (function SimpleLinkServiceClosure() { + function SimpleLinkService(pdfViewer) { + this.pdfViewer = pdfViewer; + } + SimpleLinkService.prototype = { + /** + * @returns {number} + */ + get page() { + return this.pdfViewer.currentPageNumber; + }, + /** + * @param {number} value + */ + set page(value) { + this.pdfViewer.currentPageNumber = value; + }, + /** + * @param dest - The PDF destination object. + */ + navigateTo: function (dest) {}, + /** + * @param dest - The PDF destination object. + * @returns {string} The hyperlink to the PDF object. + */ + getDestinationHash: function (dest) { + return '#'; + }, + /** + * @param hash - The PDF parameters/hash. + * @returns {string} The hyperlink to the PDF object. + */ + getAnchorUrl: function (hash) { + return '#'; + }, + /** + * @param {string} hash + */ + setHash: function (hash) {}, + /** + * @param {string} action + */ + executeNamedAction: function (action) {}, + }; + return SimpleLinkService; +})(); + +/** + * PDFPage object source. + * @class + */ +var PDFPageSource = (function PDFPageSourceClosure() { + /** + * @constructs + * @param {PDFDocument} pdfDocument + * @param {number} pageNumber + * @constructor + */ + function PDFPageSource(pdfDocument, pageNumber) { + this.pdfDocument = pdfDocument; + this.pageNumber = pageNumber; + } + + PDFPageSource.prototype = /** @lends PDFPageSource.prototype */ { + /** + * @returns {Promise} + */ + getPage: function () { + return this.pdfDocument.getPage(this.pageNumber); + } + }; + + return PDFPageSource; +})(); diff --git a/web/presentation_mode.js b/web/presentation_mode.js index 46a4fc2de9da6..0c3253b3476ab 100644 --- a/web/presentation_mode.js +++ b/web/presentation_mode.js @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals PDFView, scrollIntoView, HandTool */ +/* globals scrollIntoView, HandTool, PDFViewerApplication */ 'use strict'; @@ -68,7 +68,7 @@ var PresentationMode = { }, /** - * Initialize a timeout that is used to reset PDFView.currentPosition when the + * Initialize a timeout that is used to specify switchInProgress when the * browser transitions to fullscreen mode. Since resize events are triggered * multiple times during the switch to fullscreen mode, this is necessary in * order to prevent the page from being scrolled partially, or completely, @@ -81,9 +81,8 @@ var PresentationMode = { } this.switchInProgress = setTimeout(function switchInProgressTimeout() { delete this.switchInProgress; + this._notifyStateChange(); }.bind(this), DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS); - - PDFView.currentPosition = null; }, _resetSwitchInProgress: function presentationMode_resetSwitchInProgress() { @@ -94,11 +93,12 @@ var PresentationMode = { }, request: function presentationModeRequest() { - if (!PDFView.supportsFullscreen || this.isFullscreen || + if (!PDFViewerApplication.supportsFullscreen || this.isFullscreen || !this.viewer.hasChildNodes()) { return false; } this._setSwitchInProgress(); + this._notifyStateChange(); if (this.container.requestFullscreen) { this.container.requestFullscreen(); @@ -113,23 +113,33 @@ var PresentationMode = { } this.args = { - page: PDFView.page, - previousScale: PDFView.currentScaleValue + page: PDFViewerApplication.page, + previousScale: PDFViewerApplication.currentScaleValue }; return true; }, + _notifyStateChange: function presentationModeNotifyStateChange() { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('presentationmodechanged', true, true, { + active: PresentationMode.active, + switchInProgress: !!PresentationMode.switchInProgress + }); + window.dispatchEvent(event); + }, + enter: function presentationModeEnter() { this.active = true; this._resetSwitchInProgress(); + this._notifyStateChange(); // Ensure that the correct page is scrolled into view when entering // Presentation Mode, by waiting until fullscreen mode in enabled. // Note: This is only necessary in non-Mozilla browsers. setTimeout(function enterPresentationModeTimeout() { - PDFView.page = this.args.page; - PDFView.setScale('page-fit', true); + PDFViewerApplication.page = this.args.page; + PDFViewerApplication.setScale('page-fit', true); }.bind(this), 0); window.addEventListener('mousemove', this.mouseMove, false); @@ -140,18 +150,25 @@ var PresentationMode = { HandTool.enterPresentationMode(); this.contextMenuOpen = false; this.container.setAttribute('contextmenu', 'viewerContextMenu'); + + // Text selection is disabled in Presentation Mode, thus it's not possible + // for the user to deselect text that is selected (e.g. with "Select all") + // when entering Presentation Mode, hence we remove any active selection. + window.getSelection().removeAllRanges(); }, exit: function presentationModeExit() { - var page = PDFView.page; + var page = PDFViewerApplication.page; // Ensure that the correct page is scrolled into view when exiting // Presentation Mode, by waiting until fullscreen mode is disabled. // Note: This is only necessary in non-Mozilla browsers. setTimeout(function exitPresentationModeTimeout() { this.active = false; - PDFView.setScale(this.args.previousScale); - PDFView.page = page; + this._notifyStateChange(); + + PDFViewerApplication.setScale(this.args.previousScale, true); + PDFViewerApplication.page = page; this.args = null; }.bind(this), 0); @@ -160,7 +177,7 @@ var PresentationMode = { window.removeEventListener('contextmenu', this.contextMenu, false); this.hideControls(); - PDFView.clearMouseScrollState(); + PDFViewerApplication.clearMouseScrollState(); HandTool.exitPresentationMode(); this.container.removeAttribute('contextmenu'); this.contextMenuOpen = false; @@ -224,7 +241,7 @@ var PresentationMode = { if (!isInternalLink) { // Unless an internal link was clicked, advance one page. evt.preventDefault(); - PDFView.page += (evt.shiftKey ? -1 : 1); + PDFViewerApplication.page += (evt.shiftKey ? -1 : 1); } } }, diff --git a/web/secondary_toolbar.js b/web/secondary_toolbar.js index 7b6b5858c77d6..7c94b3185a7aa 100644 --- a/web/secondary_toolbar.js +++ b/web/secondary_toolbar.js @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals PDFView, SCROLLBAR_PADDING */ +/* globals PDFViewerApplication, SCROLLBAR_PADDING */ 'use strict'; @@ -87,7 +87,7 @@ var SecondaryToolbar = { }, downloadClick: function secondaryToolbarDownloadClick(evt) { - PDFView.download(); + PDFViewerApplication.download(); this.close(); }, @@ -96,23 +96,23 @@ var SecondaryToolbar = { }, firstPageClick: function secondaryToolbarFirstPageClick(evt) { - PDFView.page = 1; + PDFViewerApplication.page = 1; this.close(); }, lastPageClick: function secondaryToolbarLastPageClick(evt) { - if (PDFView.pdfDocument) { - PDFView.page = PDFView.pdfDocument.numPages; + if (PDFViewerApplication.pdfDocument) { + PDFViewerApplication.page = PDFViewerApplication.pagesCount; } this.close(); }, pageRotateCwClick: function secondaryToolbarPageRotateCwClick(evt) { - PDFView.rotatePages(90); + PDFViewerApplication.rotatePages(90); }, pageRotateCcwClick: function secondaryToolbarPageRotateCcwClick(evt) { - PDFView.rotatePages(-90); + PDFViewerApplication.rotatePages(-90); }, documentPropertiesClick: function secondaryToolbarDocumentPropsClick(evt) { diff --git a/web/text_layer_builder.css b/web/text_layer_builder.css new file mode 100644 index 0000000000000..32f7c497b34dd --- /dev/null +++ b/web/text_layer_builder.css @@ -0,0 +1,59 @@ +/* Copyright 2014 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. + */ + +.textLayer { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + overflow: hidden; +} + +.textLayer > div { + color: transparent; + position: absolute; + white-space: pre; + cursor: text; + -webkit-transform-origin: 0% 0%; + -moz-transform-origin: 0% 0%; + -o-transform-origin: 0% 0%; + -ms-transform-origin: 0% 0%; + transform-origin: 0% 0%; +} + +.textLayer .highlight { + margin: -1px; + padding: 1px; + + background-color: rgb(180, 0, 170); + border-radius: 4px; +} + +.textLayer .highlight.begin { + border-radius: 4px 0px 0px 4px; +} + +.textLayer .highlight.end { + border-radius: 0px 4px 4px 0px; +} + +.textLayer .highlight.middle { + border-radius: 0px; +} + +.textLayer .highlight.selected { + background-color: rgb(0, 100, 0); +} diff --git a/web/text_layer_builder.js b/web/text_layer_builder.js index bc27bb81b1519..5fac1c49335bd 100644 --- a/web/text_layer_builder.js +++ b/web/text_layer_builder.js @@ -28,11 +28,23 @@ function isAllWhitespace(str) { return !NonWhitespaceRegexp.test(str); } +/** + * @typedef {Object} TextLayerBuilderOptions + * @property {HTMLDivElement} textLayerDiv - The text layer container. + * @property {number} pageIndex - The page index. + * @property {PageViewport} viewport - The viewport of the text layer. + * @property {ILastScrollSource} lastScrollSource - The object that records when + * last time scroll happened. + * @property {boolean} isViewerInPresentationMode + * @property {PDFFindController} findController + */ + /** * 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. + * @class */ var TextLayerBuilder = (function TextLayerBuilderClosure() { function TextLayerBuilder(options) { diff --git a/web/thumbnail_view.js b/web/thumbnail_view.js index c4892669895b1..1e16a7de4a9a7 100644 --- a/web/thumbnail_view.js +++ b/web/thumbnail_view.js @@ -14,16 +14,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals PDFView, mozL10n, RenderingStates */ +/* globals mozL10n, RenderingStates, Promise, scrollIntoView, PDFPageSource, + watchScroll, getVisibleElements */ 'use strict'; -var ThumbnailView = function thumbnailView(container, id, defaultViewport) { +var THUMBNAIL_SCROLL_MARGIN = -19; + +/** + * @constructor + * @param container + * @param id + * @param defaultViewport + * @param linkService + * @param renderingQueue + * @param pageSource + * + * @implements {IRenderableView} + */ +var ThumbnailView = function thumbnailView(container, id, defaultViewport, + linkService, renderingQueue, + pageSource) { var anchor = document.createElement('a'); - anchor.href = PDFView.getAnchorUrl('#page=' + id); + anchor.href = linkService.getAnchorUrl('#page=' + id); anchor.title = mozL10n.get('thumb_page_title', {page: id}, 'Page {{page}}'); anchor.onclick = function stopNavigation() { - PDFView.page = id; + linkService.page = id; return false; }; @@ -36,6 +52,7 @@ var ThumbnailView = function thumbnailView(container, id, defaultViewport) { this.pageHeight = this.viewport.height; this.pageRatio = this.pageWidth / this.pageHeight; this.id = id; + this.renderingId = 'thumbnail' + id; this.canvasWidth = 98; this.canvasHeight = this.canvasWidth / this.pageWidth * this.pageHeight; @@ -62,6 +79,8 @@ var ThumbnailView = function thumbnailView(container, id, defaultViewport) { this.hasImage = false; this.renderingState = RenderingStates.INITIAL; + this.renderingQueue = renderingQueue; + this.pageSource = pageSource; this.setPdfPage = function thumbnailViewSetPdfPage(pdfPage) { this.pdfPage = pdfPage; @@ -125,7 +144,7 @@ var ThumbnailView = function thumbnailView(container, id, defaultViewport) { this.draw = function thumbnailViewDraw(callback) { if (!this.pdfPage) { - var promise = PDFView.getPage(this.id); + var promise = this.pageSource.getPage(this.id); promise.then(function(pdfPage) { this.setPdfPage(pdfPage); this.draw(callback); @@ -150,7 +169,7 @@ var ThumbnailView = function thumbnailView(container, id, defaultViewport) { canvasContext: ctx, viewport: drawViewport, continueCallback: function(cont) { - if (PDFView.highestPriorityPage !== 'thumbnail' + self.id) { + if (!self.renderingQueue.isHighestPriority(self)) { self.renderingState = RenderingStates.PAUSED; self.resume = function() { self.renderingState = RenderingStates.RUNNING; @@ -187,7 +206,7 @@ var ThumbnailView = function thumbnailView(container, id, defaultViewport) { this.setImage = function thumbnailViewSetImage(img) { if (!this.pdfPage) { - var promise = PDFView.getPage(this.id); + var promise = this.pageSource.getPage(); promise.then(function(pdfPage) { this.setPdfPage(pdfPage); this.setImage(img); @@ -232,3 +251,134 @@ var ThumbnailView = function thumbnailView(container, id, defaultViewport) { }; ThumbnailView.tempImageCache = null; + +/** + * @typedef {Object} PDFThumbnailViewerOptions + * @property {HTMLDivElement} container - The container for the thumbs elements. + * @property {IPDFLinkService} linkService - The navigation/linking service. + * @property {PDFRenderingQueue} renderingQueue - The rendering queue object. + */ + +/** + * Simple viewer control to display thumbs for pages. + * @class + */ +var PDFThumbnailViewer = (function pdfThumbnailViewer() { + /** + * @constructs + * @param {PDFThumbnailViewerOptions} options + */ + function PDFThumbnailViewer(options) { + this.container = options.container; + this.renderingQueue = options.renderingQueue; + this.linkService = options.linkService; + + this.scroll = watchScroll(this.container, this._scrollUpdated.bind(this)); + this._resetView(); + } + + PDFThumbnailViewer.prototype = { + _scrollUpdated: function PDFThumbnailViewer_scrollUpdated() { + this.renderingQueue.renderHighestPriority(); + }, + + getThumbnail: function PDFThumbnailViewer_getThumbnail(index) { + return this.thumbnails[index]; + }, + + _getVisibleThumbs: function PDFThumbnailViewer_getVisibleThumbs() { + return getVisibleElements(this.container, this.thumbnails); + }, + + scrollThumbnailIntoView: function (page) { + var selected = document.querySelector('.thumbnail.selected'); + if (selected) { + selected.classList.remove('selected'); + } + var thumbnail = document.getElementById('thumbnailContainer' + page); + thumbnail.classList.add('selected'); + var visibleThumbs = this._getVisibleThumbs(); + var numVisibleThumbs = visibleThumbs.views.length; + + // If the thumbnail isn't currently visible, scroll it into view. + if (numVisibleThumbs > 0) { + var first = visibleThumbs.first.id; + // Account for only one thumbnail being visible. + var last = (numVisibleThumbs > 1 ? visibleThumbs.last.id : first); + if (page <= first || page >= last) { + scrollIntoView(thumbnail, { top: THUMBNAIL_SCROLL_MARGIN }); + } + } + }, + + get pagesRotation() { + return this._pagesRotation; + }, + + set pagesRotation(rotation) { + this._pagesRotation = rotation; + for (var i = 0, l = this.thumbnails.length; i < l; i++) { + var thumb = this.thumbnails[i]; + thumb.update(rotation); + } + }, + + cleanup: function PDFThumbnailViewer_cleanup() { + ThumbnailView.tempImageCache = null; + }, + + _resetView: function () { + this.thumbnails = []; + this._pagesRotation = 0; + }, + + setDocument: function (pdfDocument) { + if (this.pdfDocument) { + // cleanup of the elements and views + var thumbsView = this.container; + while (thumbsView.hasChildNodes()) { + thumbsView.removeChild(thumbsView.lastChild); + } + this._resetView(); + } + + this.pdfDocument = pdfDocument; + if (!pdfDocument) { + return Promise.resolve(); + } + + return pdfDocument.getPage(1).then(function (firstPage) { + var pagesCount = pdfDocument.numPages; + var viewport = firstPage.getViewport(1.0); + for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) { + var pageSource = new PDFPageSource(pdfDocument, pageNum); + var thumbnail = new ThumbnailView(this.container, pageNum, + viewport.clone(), this.linkService, + this.renderingQueue, pageSource); + this.thumbnails.push(thumbnail); + } + }.bind(this)); + }, + + ensureThumbnailVisible: + function PDFThumbnailViewer_ensureThumbnailVisible(page) { + // Ensure that the thumbnail of the current page is visible + // when switching from another view. + scrollIntoView(document.getElementById('thumbnailContainer' + page)); + }, + + forceRendering: function () { + var visibleThumbs = this._getVisibleThumbs(); + var thumbView = this.renderingQueue.getHighestPriority(visibleThumbs, + this.thumbnails, + this.scroll.down); + if (thumbView) { + this.renderingQueue.renderView(thumbView); + return true; + } + return false; + } + }; + + return PDFThumbnailViewer; +})(); diff --git a/web/ui_utils.js b/web/ui_utils.js index b38ec4bc3297b..b4f2d7365136c 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -16,6 +16,14 @@ 'use strict'; +var CSS_UNITS = 96.0 / 72.0; +var DEFAULT_SCALE = 'auto'; +var UNKNOWN_SCALE = 0; +var MAX_AUTO_SCALE = 1.25; +var SCROLLBAR_PADDING = 40; +var VERTICAL_PADDING = 5; +var DEFAULT_CACHE_SIZE = 10; + // optimised CSS custom property getter/setter var CustomStyle = (function CustomStyleClosure() { @@ -138,6 +146,91 @@ function scrollIntoView(element, spot) { parent.scrollTop = offsetY; } +/** + * Helper function to start monitoring the scroll event and converting them into + * PDF.js friendly one: with scroll debounce and scroll direction. + */ +function watchScroll(viewAreaElement, callback) { + var debounceScroll = function debounceScroll(evt) { + if (rAF) { + return; + } + // schedule an invocation of scroll for next animation frame. + rAF = window.requestAnimationFrame(function viewAreaElementScrolled() { + rAF = null; + + var currentY = viewAreaElement.scrollTop; + var lastY = state.lastY; + if (currentY > lastY) { + state.down = true; + } else if (currentY < lastY) { + state.down = false; + } + state.lastY = currentY; + // else do nothing and use previous value + callback(state); + }); + }; + + var state = { + down: true, + lastY: viewAreaElement.scrollTop, + _eventHandler: debounceScroll + }; + + var rAF = null; + viewAreaElement.addEventListener('scroll', debounceScroll, true); + return state; +} + +/** + * Generic helper to find out what elements are visible within a scroll pane. + */ +function getVisibleElements(scrollEl, views, sortByVisibility) { + var top = scrollEl.scrollTop, bottom = top + scrollEl.clientHeight; + var left = scrollEl.scrollLeft, right = left + scrollEl.clientWidth; + + var visible = [], view; + var currentHeight, viewHeight, hiddenHeight, percentHeight; + var currentWidth, viewWidth; + for (var i = 0, ii = views.length; i < ii; ++i) { + view = views[i]; + currentHeight = view.el.offsetTop + view.el.clientTop; + viewHeight = view.el.clientHeight; + if ((currentHeight + viewHeight) < top) { + continue; + } + if (currentHeight > bottom) { + break; + } + currentWidth = view.el.offsetLeft + view.el.clientLeft; + viewWidth = view.el.clientWidth; + if ((currentWidth + viewWidth) < left || currentWidth > right) { + continue; + } + hiddenHeight = Math.max(0, top - currentHeight) + + Math.max(0, currentHeight + viewHeight - bottom); + percentHeight = ((viewHeight - hiddenHeight) * 100 / viewHeight) | 0; + + visible.push({ id: view.id, x: currentWidth, y: currentHeight, + view: view, percent: percentHeight }); + } + + var first = visible[0]; + var last = visible[visible.length - 1]; + + if (sortByVisibility) { + visible.sort(function(a, b) { + var pc = a.percent - b.percent; + if (Math.abs(pc) > 0.001) { + return -pc; + } + return a.id - b.id; // ensure stability + }); + } + return {first: first, last: last, views: visible}; +} + /** * Event handler to suppress context menu. */ @@ -182,6 +275,7 @@ var ProgressBar = (function ProgressBarClosure() { } function ProgressBar(id, opts) { + this.visible = true; // Fetch the sub-elements for later. this.div = document.querySelector(id + ' .progress'); @@ -235,8 +329,21 @@ var ProgressBar = (function ProgressBarClosure() { }, hide: function ProgressBar_hide() { + if (!this.visible) { + return; + } + this.visible = false; this.bar.classList.add('hidden'); - this.bar.removeAttribute('style'); + document.body.classList.remove('loadingInProgress'); + }, + + show: function ProgressBar_show() { + if (this.visible) { + return; + } + this.visible = true; + document.body.classList.add('loadingInProgress'); + this.bar.classList.remove('hidden'); } }; @@ -262,4 +369,3 @@ var Cache = function cacheCache(size) { } }; }; - diff --git a/web/viewer.css b/web/viewer.css index b6974dee53b43..4fbe0845bc142 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -1,4 +1,4 @@ -/* Copyright 2012 Mozilla Foundation +/* Copyright 2014 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,8 @@ * limitations under the License. */ +@import url(pdf_viewer.css); + * { padding: 0; margin: 0; @@ -53,6 +55,7 @@ select { height: 100%; overflow: hidden; cursor: none; + -webkit-user-select: none; } #viewerContainer:-moz-full-screen { @@ -63,6 +66,7 @@ select { height: 100%; overflow: hidden; cursor: none; + -moz-user-select: none; } #viewerContainer:-ms-fullscreen { @@ -72,6 +76,7 @@ select { height: 100%; overflow: hidden !important; cursor: none; + -ms-user-select: none; } #viewerContainer:-ms-fullscreen::-ms-backdrop { @@ -86,26 +91,9 @@ select { height: 100%; overflow: hidden; cursor: none; -} - -:-webkit-full-screen .page { - margin-bottom: 100%; - border: 0; -} - -:-moz-full-screen .page { - margin-bottom: 100%; - border: 0; -} - -:-ms-fullscreen .page { - margin-bottom: 100% !important; - border: 0; -} - -:fullscreen .page { - margin-bottom: 100%; - border: 0; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; } :-webkit-full-screen a:not(.internalLink) { @@ -1071,6 +1059,12 @@ html[dir='rtl'] .verticalToolbarSeparator { width: 40px; } +.toolbarField.pageNumber.visiblePageIsLoading { + background-image: url(images/loading-small.png); + background-repeat: no-repeat; + background-position: 1px; +} + .toolbarField.pageNumber::-webkit-inner-spin-button, .toolbarField.pageNumber::-webkit-outer-spin-button { -webkit-appearance: none; @@ -1264,88 +1258,6 @@ html[dir='rtl'] .attachmentsItem > button { cursor: default; } -.canvasWrapper { - overflow: hidden; -} - -canvas { - margin: 0; - display: block; -} - -.page { - direction: ltr; - width: 816px; - height: 1056px; - margin: 1px auto -8px auto; - position: relative; - overflow: visible; - border: 9px solid transparent; - background-clip: content-box; - border-image: url(images/shadow.png) 9 9 repeat; - background-color: white; -} - -.annotLink > a:hover { - opacity: 0.2; - background: #ff0; - box-shadow: 0px 2px 10px #ff0; -} - -.loadingIcon { - position: absolute; - display: block; - left: 0; - top: 0; - right: 0; - bottom: 0; - background: url('images/loading-icon.gif') center no-repeat; -} - -.textLayer { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - overflow: hidden; -} - -.textLayer > div { - color: transparent; - position: absolute; - white-space: pre; - cursor: text; - -webkit-transform-origin: 0% 0%; - -moz-transform-origin: 0% 0%; - -o-transform-origin: 0% 0%; - -ms-transform-origin: 0% 0%; - transform-origin: 0% 0%; -} - -.textLayer .highlight { - margin: -1px; - padding: 1px; - - background-color: rgb(180, 0, 170); - border-radius: 4px; -} - -.textLayer .highlight.begin { - border-radius: 4px 0px 0px 4px; -} - -.textLayer .highlight.end { - border-radius: 0px 4px 4px 0px; -} - -.textLayer .highlight.middle { - border-radius: 0px; -} - -.textLayer .highlight.selected { - background-color: rgb(0, 100, 0); -} /* TODO: file FF bug to support ::-moz-selection:window-inactive so we can override the opaque grey background when the window is inactive; @@ -1359,56 +1271,6 @@ canvas { opacity: 0.2; } -.annotationHighlight { - position: absolute; - border: 2px #FFFF99 solid; -} - -.annotText > img { - position: absolute; - cursor: pointer; -} - -.annotTextContentWrapper { - position: absolute; - width: 20em; -} - -.annotTextContent { - z-index: 200; - float: left; - max-width: 20em; - background-color: #FFFF99; - box-shadow: 0px 2px 5px #333; - border-radius: 2px; - padding: 0.6em; - cursor: pointer; -} - -.annotTextContent > h1 { - font-size: 1em; - border-bottom: 1px solid #000000; - padding-bottom: 0.2em; -} - -.annotTextContent > p { - padding-top: 0.2em; -} - -.annotLink > a { - position: absolute; - font-size: 1em; - top: 0; - left: 0; - width: 100%; - height: 100%; -} - -.annotLink > a /* -ms-a */ { - background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAA\ - LAAAAAABAAEAAAIBRAA7") 0 0 repeat; -} - #errorWrapper { background: none repeat scroll 0 0 #FF5555; color: white; @@ -1678,6 +1540,12 @@ html[dir='rtl'] #documentPropertiesOverlay .row > * { left: 186px; } + .toolbarField.pageNumber.visiblePageIsLoading, + #findInput[data-status="pending"] { + background-image: url(images/loading-small@2x.png); + background-size: 16px 17px; + } + .dropdownToolbarButton { background: url(images/toolbarButton-menuArrows@2x.png) no-repeat; background-size: 7px 16px; @@ -1827,6 +1695,8 @@ html[dir='rtl'] #documentPropertiesOverlay .row > * { display: none; border: none; box-shadow: none; + background-clip: content-box; + background-color: white; } .page[data-loaded] { @@ -1848,6 +1718,7 @@ html[dir='rtl'] #documentPropertiesOverlay .row > * { position: relative; top: 0; left: 0; + display: block; } } diff --git a/web/viewer.html b/web/viewer.html index d15c3456df0dd..f562dbd6688ab 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -68,9 +68,11 @@ + - + + @@ -94,8 +96,8 @@ - -
+ +
@@ -264,14 +266,14 @@ - - - - - - - - + + + + + + + +
@@ -298,7 +300,7 @@
-
+