diff --git a/.gitignore b/.gitignore index 9b20a7778..b779eac0b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ local.mk build/ tags - +.DS_Store diff --git a/README.md b/README.md index 3b8d86bb1..34b1a3f5e 100644 --- a/README.md +++ b/README.md @@ -61,13 +61,13 @@ You can also view all the test pdf files on the right side serving + http://localhost:8888/test/pdfs/?frame -### Building pdf.js +### Building pdf.js. -In order to bundle all `src/` files into a final `pdf.js`, issue: +In order to bundle all `src/` files into a final `pdf.js` and build the generic viewer, issue: - $ node make bundle + $ node make generic -This will generate the file `build/pdf.js` that can be included in your final project. (WARNING: That's a large file! Consider minifying it). +This will generate the file `build/generic/build/pdf.js` that can be included in your final project. The pdf.js file is large and should be minified for production. Also, if you would like to support more browsers than firefox you'll also need to include `compatibility.js` from `build/generic/web/`. # Learning diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js index 2872da3fc..bac546c87 100644 --- a/extensions/firefox/components/PdfStreamConverter.js +++ b/extensions/firefox/components/PdfStreamConverter.js @@ -281,9 +281,26 @@ ChromeActions.prototype = { var strings = getLocalizedStrings('chrome.properties'); var message = getLocalizedString(strings, 'unsupported_feature'); - var win = Services.wm.getMostRecentWindow('navigator:browser'); - var browser = win.gBrowser.getBrowserForDocument(domWindow.top.document); - var notificationBox = win.gBrowser.getNotificationBox(browser); + var notificationBox = null; + // Multiple browser windows can be opened, finding one for notification box + var windowsEnum = Services.wm + .getZOrderDOMWindowEnumerator('navigator:browser', true); + while (windowsEnum.hasMoreElements()) { + var win = windowsEnum.getNext(); + if (win.closed) + continue; + var browser = win.gBrowser.getBrowserForDocument(domWindow.top.document); + if (browser) { + // right window/browser is found, getting the notification box + notificationBox = win.gBrowser.getNotificationBox(browser); + break; + } + } + if (!notificationBox) { + log('Unable to get a notification box for the fallback message'); + return; + } + // Flag so we don't call the response callback twice, since if the user // clicks open with different viewer both the button callback and // eventCallback will be called. diff --git a/extensions/firefox/install.rdf b/extensions/firefox/install.rdf index fdb78b175..eea7d45d6 100644 --- a/extensions/firefox/install.rdf +++ b/extensions/firefox/install.rdf @@ -13,7 +13,7 @@ <em:targetApplication> <Description> <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> - <em:minVersion>6.0</em:minVersion> + <em:minVersion>10.0</em:minVersion> <em:maxVersion>15.0a1</em:maxVersion> </Description> </em:targetApplication> @@ -22,7 +22,7 @@ <em:targetApplication> <Description> <em:id>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</em:id> - <em:minVersion>2.1.*</em:minVersion> + <em:minVersion>2.7</em:minVersion> <em:maxVersion>2.12a1</em:maxVersion> </Description> </em:targetApplication> diff --git a/external/builder/builder.js b/external/builder/builder.js new file mode 100644 index 000000000..6eb0cd6ae --- /dev/null +++ b/external/builder/builder.js @@ -0,0 +1,142 @@ +require('../shelljs/make'); +var fs = require('fs'), + path = require('path'), + vm = require('vm'); + +/** + * A simple preprocessor that is based on the firefox preprocessor + * see (https://developer.mozilla.org/en/Build/Text_Preprocessor). The main + * difference is that this supports a subset of the commands and it supports + * preproccesor commands in html style comments. + * Currently Supported commands: + * - if + * - else + * - endif + * - include + * - expand + */ +function preprocess(inFilename, outFilename, defines) { + // TODO make this really read line by line. + var lines = fs.readFileSync(inFilename).toString().split('\n'); + var totalLines = lines.length; + var out = ''; + var i = 0; + function readLine() { + if (i < totalLines) { + return lines[i++]; + } + return null; + } + var writeLine = typeof outFilename === 'function' ? outFilename : function(line) { + out += line + '\n'; + } + function include(file) { + var realPath = fs.realpathSync(inFilename); + var dir = path.dirname(realPath); + preprocess(path.join(dir, file), writeLine, defines); + } + function expand(line) { + line = line.replace(/__[\w]+__/g, function(variable) { + variable = variable.substring(2, variable.length - 2); + if (variable in defines) { + return defines[variable]; + } + return ''; + }); + writeLine(line); + } + + var s, state = 0, stack = []; + var control = /^(?:\/\/|<!--)\s*#(if|else|endif|expand|include)(?:\s+(.*?)(?:-->)?$)?/; + var lineNumber = 0; + while ((s = readLine()) !== null) { + ++lineNumber; + var m = control.exec(s); + if (m) { + switch (m[1]) { + case 'if': + stack.push(state); + try { + state = vm.runInNewContext(m[2], defines) ? 3 : 1; + } catch (e) { + console.error('Could not evalute line \'' + m[2] + '\' at ' + + fs.realpathSync(inFilename) + ':' + lineNumber); + throw e; + } + break; + case 'else': + state = state === 1 ? 3 : 2; + break; + case 'endif': + state = stack.pop(); + break; + case 'expand': + if (state === 0 || state === 3) + expand(m[2]); + break; + case 'include': + if (state === 0 || state === 3) + include(m[2]); + break; + } + } else { + if (state === 0) { + writeLine(s); + } else if(state === 3) { + writeLine(s.replace(/^\/\/|^<!--|-->/g, " ")); + } + } + } + if (state !== 0 || stack.length !== 0) + throw new Error('Missing endif in preprocessor.'); + if (typeof outFilename !== 'function') + fs.writeFileSync(outFilename, out); +} +exports.preprocess = preprocess; + +/** + * Simplifies common build steps. + * @param setup + * .defines defines for preprocessors + * .copy array of arrays of source and destination pairs of files to copy + * .preprocess array of arrays of source and destination pairs of files + * run through preprocessor + */ +function build(setup) { + var defines = setup.defines; + + setup.copy.forEach(function(option) { + var source = option[0]; + var destination = option[1]; + cp('-R', source, destination); + }); + + setup.preprocess.forEach(function(option) { + var sources = option[0]; + var destination = option[1]; + + sources = ls('-R', sources); + sources.forEach(function(source) { + // ??? Warn if the source is wildcard and dest is file? + var destWithFolder = destination; + if (test('-d', destination)) + destWithFolder += '/' + path.basename(source); + preprocess(source, destWithFolder, defines); + }); + }); +} +exports.build = build; + +/** + * Merge two defines arrays. Values in the second param will override values in + * the first. + */ +function merge(defaults, defines) { + var ret = {}; + for (var key in defaults) + ret[key] = defaults[key]; + for (key in defines) + ret[key] = defines[key]; + return ret; +} +exports.merge = merge; diff --git a/l10n/es/viewer.properties b/l10n/es/viewer.properties index 8e7080d03..8485cbf17 100644 --- a/l10n/es/viewer.properties +++ b/l10n/es/viewer.properties @@ -18,11 +18,13 @@ zoom_in_label=Ampliar zoom.title=Ampliación print.title=Imprimir print_label=Imprimir +fullscreen.title=Pantalla completa +fullscreen_label=Pantalla completa open_file.title=Abrir archivo open_file_label=Abrir download.title=Descargar download_label=Descargar -bookmark.title=Vista actual (copiar o abrir en una ventana nueva) +bookmark.title=Vista actual (copie o abra en una ventana nueva) bookmark_label=Vista actual # Tooltips and alt text for side panel toolbar buttons @@ -69,7 +71,7 @@ error_stack=Pila: {{stack}} error_file=Archivo: {{file}} # LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number error_line=Línea: {{line}} -rendering_error=Ocurrió un error al renderizar la página. +rendering_error=Ocurrió un error mientras se renderizaba la página. # Predefined zoom values page_scale_width=Anchura de página @@ -79,7 +81,7 @@ page_scale_actual=Tamaño real # Loading indicator messages loading_error_indicator=Error -loading_error=Ocurrió un error al cargar el PDF. +loading_error=Ocurrió un error mientras se cargaba el PDF. # LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in @@ -87,3 +89,5 @@ loading_error=Ocurrió un error al cargar el PDF. # Some common types are e.g.: "Check", "Text", "Comment", "Note" text_annotation_type=[Anotación {{type}}] request_password=El PDF está protegido con una contraseña: + +printing_not_supported=Aviso: La impresión no es compatible totalmente con este navegador. diff --git a/l10n/ja/viewer.properties b/l10n/ja/viewer.properties index b7981ecf0..c8bfde461 100644 --- a/l10n/ja/viewer.properties +++ b/l10n/ja/viewer.properties @@ -18,6 +18,8 @@ zoom_in_label=拡大 zoom.title=ズーム print.title=印刷 print_label=印刷 +fullscreen.title=全画面表示 +fullscreen_label=全画面表示 open_file.title=ファイルを開く open_file_label=開く download.title=ダウンロード diff --git a/make.js b/make.js index 727e740ca..1d2e5cfcf 100755 --- a/make.js +++ b/make.js @@ -1,5 +1,6 @@ #!/usr/bin/env node require('./external/shelljs/make'); +var builder = require('./external/builder/builder.js'); var ROOT_DIR = __dirname + '/', // absolute path to project's root BUILD_DIR = 'build/', @@ -8,6 +9,7 @@ var ROOT_DIR = __dirname + '/', // absolute path to project's root EXTENSION_SRC_DIR = 'extensions/', LOCALE_SRC_DIR = 'l10n/', GH_PAGES_DIR = BUILD_DIR + 'gh-pages/', + GENERIC_DIR = BUILD_DIR + 'generic/', REPO = 'git@github.com:mozilla/pdf.js.git', PYTHON_BIN = 'python2.7', MOZCENTRAL_PREF_PREFIX = 'pdfjs', @@ -15,6 +17,16 @@ var ROOT_DIR = __dirname + '/', // absolute path to project's root MOZCENTRAL_STREAM_CONVERTER_ID = 'd0c5195d-e798-49d4-b1d3-9324328b2291', FIREFOX_STREAM_CONVERTER_ID = '6457a96b-2d68-439a-bcfa-44465fbcdbb1'; +var DEFINES = { + PRODUCTION: true, + // The main build targets: + GENERIC: false, + FIREFOX: false, + MOZCENTRAL: false, + B2G: false, + CHROME: false +}; + // // make all // @@ -31,14 +43,59 @@ target.all = function() { // Production stuff // +// Files that need to be included in every build. +var COMMON_WEB_FILES = + ['web/viewer.css', + 'web/images', + 'web/debugger.js'], + COMMON_WEB_FILES_PREPROCESS = + ['web/viewer.js', + 'web/viewer.html']; + +// +// make generic +// Builds the generic production viewer that should be compatible with most +// modern HTML5 browsers. +// +target.generic = function() { + target.bundle(); + target.locale(); + + cd(ROOT_DIR); + echo(); + echo('### Creating generic viewer'); + + rm('-rf', GENERIC_DIR); + mkdir('-p', GENERIC_DIR); + mkdir('-p', GENERIC_DIR + BUILD_DIR); + mkdir('-p', GENERIC_DIR + '/web'); + + var defines = builder.merge(DEFINES, {GENERIC: true}); + + var setup = { + defines: defines, + copy: [ + [COMMON_WEB_FILES, GENERIC_DIR + '/web'], + ['external/webL10n/l10n.js', GENERIC_DIR + '/web'], + ['web/compatibility.js', GENERIC_DIR + '/web'], + ['web/compressed.tracemonkey-pldi-09.pdf', GENERIC_DIR + '/web'], + ['web/locale.properties', GENERIC_DIR + '/web'] + ], + preprocess: [ + [BUILD_TARGET, GENERIC_DIR + BUILD_TARGET], + [COMMON_WEB_FILES_PREPROCESS, GENERIC_DIR + '/web'] + ] + }; + builder.build(setup); +}; + // // make web // Generates the website for the project, by checking out the gh-pages branch underneath // the build directory, and then moving the various viewer files into place. // target.web = function() { - target.production(); - target.locale(); + target.generic(); target.extension(); target.pagesrepo(); @@ -46,25 +103,18 @@ target.web = function() { echo(); echo('### Creating web site'); - var GH_PAGES_SRC_FILES = [ - 'web/*', - 'external/webL10n/l10n.js' - ]; - - cp(BUILD_TARGET, GH_PAGES_DIR + BUILD_TARGET); - cp('-R', GH_PAGES_SRC_FILES, GH_PAGES_DIR + '/web'); + cp('-R', GENERIC_DIR + '/*', GH_PAGES_DIR); cp(FIREFOX_BUILD_DIR + '/*.xpi', FIREFOX_BUILD_DIR + '/*.rdf', - GH_PAGES_DIR + EXTENSION_SRC_DIR + 'firefox/'); - cp(GH_PAGES_DIR + '/web/index.html.template', GH_PAGES_DIR + '/index.html'); - mv('-f', GH_PAGES_DIR + '/web/viewer-production.html', - GH_PAGES_DIR + '/web/viewer.html'); + GH_PAGES_DIR + EXTENSION_SRC_DIR + 'firefox/'); + cp('web/index.html.template', GH_PAGES_DIR + '/index.html'); + cd(GH_PAGES_DIR); exec('git add -A'); echo(); echo("Website built in " + GH_PAGES_DIR); echo("Don't forget to cd into " + GH_PAGES_DIR + - " and issue 'git commit' to push changes."); + " and issue 'git commit' to push changes."); }; // @@ -124,20 +174,10 @@ target.locale = function() { chromeManifestContent.to(CHROME_MANIFEST_OUTPUT); }; -// -// make production -// Creates production output (pdf.js, and corresponding changes to web/ files) -// -target.production = function() { - target.bundle(); - target.viewer(); -}; - // // make bundle // Bundles all source files into one wrapper 'pdf.js' file, in the given order. // - target.bundle = function() { cd(ROOT_DIR); echo(); @@ -178,29 +218,12 @@ target.bundle = function() { bundleVersion = exec('git log --format="%h" -n 1', {silent: true}).output.replace('\n', ''); - sed(/.*PDFJSSCRIPT_INCLUDE_ALL.*\n/, bundle, 'pdf.js') - .to(ROOT_DIR + BUILD_TARGET); - sed('-i', 'PDFJSSCRIPT_BUNDLE_VER', bundleVersion, ROOT_DIR + BUILD_TARGET); + // This just preprocesses the empty pdf.js file, we don't actually want to + // preprocess everything yet since other build targets use this file. + builder.preprocess('pdf.js', ROOT_DIR + BUILD_TARGET, + {BUNDLE: bundle, BUNDLE_VERSION: bundleVersion}); }; -// -// make viewer -// Changes development <script> tags in our web viewer to use only 'pdf.js'. -// Produces 'viewer-production.html' -// -target.viewer = function() { - cd(ROOT_DIR); - echo(); - echo('### Generating production-level viewer'); - - cd('web'); - // Remove development lines - sed(/.*PDFJSSCRIPT_REMOVE_CORE.*\n/g, '', 'viewer.html') - .to('viewer-production.html'); - // Introduce snippet - sed('-i', /.*PDFJSSCRIPT_INCLUDE_BUILD.*\n/g, cat('viewer-snippet.html'), - 'viewer-production.html'); -}; // // make pagesrepo @@ -238,15 +261,7 @@ target.pagesrepo = function() { // Extension stuff // -var EXTENSION_WEB_FILES = - ['web/debugger.js', - 'web/images', - 'web/viewer.css', - 'web/viewer.js', - 'web/viewer.html', - 'extensions/firefox/tools/l10n.js', - 'web/viewer-production.html'], - EXTENSION_BASE_VERSION = 'f0f0418a9c6637981fe1182b9212c2d592774c7d', +var EXTENSION_BASE_VERSION = 'f0f0418a9c6637981fe1182b9212c2d592774c7d', EXTENSION_VERSION_PREFIX = '0.3.', EXTENSION_BUILD_NUMBER, EXTENSION_VERSION; @@ -260,7 +275,6 @@ target.extension = function() { echo('### Building extensions'); target.locale(); - target.production(); target.firefox(); target.chrome(); }; @@ -287,8 +301,10 @@ target.firefox = function() { cd(ROOT_DIR); echo(); echo('### Building Firefox extension'); + var defines = builder.merge(DEFINES, {FIREFOX: true}); var FIREFOX_BUILD_CONTENT_DIR = FIREFOX_BUILD_DIR + '/content/', + FIREFOX_EXTENSION_DIR = 'extensions/firefox/', FIREFOX_CONTENT_DIR = EXTENSION_SRC_DIR + '/firefox/content/', FIREFOX_EXTENSION_FILES_TO_COPY = ['*.js', @@ -313,7 +329,7 @@ target.firefox = function() { FIREFOX_AMO_EXTENSION_NAME = 'pdf.js.amo.xpi'; target.locale(); - target.production(); + target.bundle(); target.buildnumber(); cd(ROOT_DIR); @@ -328,25 +344,18 @@ target.firefox = function() { cp('-R', FIREFOX_EXTENSION_FILES_TO_COPY, ROOT_DIR + FIREFOX_BUILD_DIR); cd(ROOT_DIR); - // Copy a standalone version of pdf.js inside the content directory - cp(BUILD_TARGET, FIREFOX_BUILD_CONTENT_DIR + BUILD_DIR); - cp('-R', EXTENSION_WEB_FILES, FIREFOX_BUILD_CONTENT_DIR + '/web'); - rm(FIREFOX_BUILD_CONTENT_DIR + '/web/viewer-production.html'); - - // Copy over the firefox extension snippet so we can inline pdf.js in it - cp('web/viewer-snippet-firefox-extension.html', FIREFOX_BUILD_CONTENT_DIR + '/web'); + var setup = { + defines: defines, + copy: [ + [COMMON_WEB_FILES, FIREFOX_BUILD_CONTENT_DIR + '/web'], + ['extensions/firefox/tools/l10n.js', FIREFOX_BUILD_CONTENT_DIR + '/web'] + ], + preprocess: [ + [COMMON_WEB_FILES_PREPROCESS, FIREFOX_BUILD_CONTENT_DIR + '/web'] + ] + }; + builder.build(setup); - // Modify the viewer so it does all the extension-only stuff. - cd(FIREFOX_BUILD_CONTENT_DIR + '/web'); - sed('-i', /.*PDFJSSCRIPT_INCLUDE_BUNDLE.*\n/, cat(ROOT_DIR + BUILD_TARGET), 'viewer-snippet-firefox-extension.html'); - sed('-i', /.*PDFJSSCRIPT_REMOVE_CORE.*\n/g, '', 'viewer.html'); - sed('-i', /.*PDFJSSCRIPT_REMOVE_FIREFOX_EXTENSION.*\n/g, '', 'viewer.html'); - sed('-i', /.*PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION.*\n/, cat('viewer-snippet-firefox-extension.html'), 'viewer.html'); - cd(ROOT_DIR); - - // We don't need pdf.js anymore since its inlined - rm('-Rf', FIREFOX_BUILD_CONTENT_DIR + BUILD_DIR); - rm(FIREFOX_BUILD_CONTENT_DIR + '/web/viewer-snippet-firefox-extension.html'); // Remove '.DS_Store' and other hidden files find(FIREFOX_BUILD_DIR).forEach(function(file) { if (file.match(/^\./)) @@ -388,6 +397,7 @@ target.mozcentral = function() { cd(ROOT_DIR); echo(); echo('### Building mozilla-central extension'); + var defines = builder.merge(DEFINES, {MOZCENTRAL: true}); var MOZCENTRAL_DIR = BUILD_DIR + 'mozcentral/', MOZCENTRAL_EXTENSION_DIR = MOZCENTRAL_DIR + 'browser/extensions/pdfjs/', @@ -412,7 +422,7 @@ target.mozcentral = function() { 'content', 'LICENSE']; - target.production(); + target.bundle(); target.buildnumber(); cd(ROOT_DIR); @@ -432,25 +442,18 @@ target.mozcentral = function() { ROOT_DIR + MOZCENTRAL_EXTENSION_DIR + '/chrome.manifest') cd(ROOT_DIR); - // Copy a standalone version of pdf.js inside the content directory - cp(BUILD_TARGET, MOZCENTRAL_CONTENT_DIR + BUILD_DIR); - cp('-R', EXTENSION_WEB_FILES, MOZCENTRAL_CONTENT_DIR + '/web'); - rm(MOZCENTRAL_CONTENT_DIR + '/web/viewer-production.html'); + var setup = { + defines: defines, + copy: [ + [COMMON_WEB_FILES, MOZCENTRAL_CONTENT_DIR + '/web'], + ['extensions/firefox/tools/l10n.js', MOZCENTRAL_CONTENT_DIR + '/web'] + ], + preprocess: [ + [COMMON_WEB_FILES_PREPROCESS, MOZCENTRAL_CONTENT_DIR + '/web'] + ] + }; + builder.build(setup); - // Copy over the firefox extension snippet so we can inline pdf.js in it - cp('web/viewer-snippet-firefox-extension.html', MOZCENTRAL_CONTENT_DIR + '/web'); - - // Modify the viewer so it does all the extension-only stuff. - cd(MOZCENTRAL_CONTENT_DIR + '/web'); - sed('-i', /.*PDFJSSCRIPT_INCLUDE_BUNDLE.*\n/, cat(ROOT_DIR + BUILD_TARGET), 'viewer-snippet-firefox-extension.html'); - sed('-i', /.*PDFJSSCRIPT_REMOVE_CORE.*\n/g, '', 'viewer.html'); - sed('-i', /.*PDFJSSCRIPT_REMOVE_FIREFOX_EXTENSION.*\n/g, '', 'viewer.html'); - sed('-i', /.*PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION.*\n/, cat('viewer-snippet-firefox-extension.html'), 'viewer.html'); - cd(ROOT_DIR); - - // We don't need pdf.js anymore since its inlined - rm('-Rf', MOZCENTRAL_CONTENT_DIR + BUILD_DIR); - rm(MOZCENTRAL_CONTENT_DIR + '/web/viewer-snippet-firefox-extension.html'); // Remove '.DS_Store' and other hidden files find(MOZCENTRAL_DIR).forEach(function(file) { if (file.match(/^\./)) @@ -482,6 +485,36 @@ target.mozcentral = function() { cp('-Rf', 'test/mozcentral/*', MOZCENTRAL_TEST_DIR); }; +target.b2g = function() { + echo(); + echo('### Building B2G (Firefox OS App)'); + var B2G_BUILD_DIR = BUILD_DIR + '/b2g/', + B2G_BUILD_CONTENT_DIR = B2G_BUILD_DIR + '/content/'; + var defines = builder.merge(DEFINES, { B2G: true }); + target.bundle(); + + // Clear out everything in the b2g build directory + cd(ROOT_DIR); + rm('-Rf', B2G_BUILD_DIR); + mkdir('-p', B2G_BUILD_CONTENT_DIR); + mkdir('-p', B2G_BUILD_CONTENT_DIR + BUILD_DIR); + mkdir('-p', B2G_BUILD_CONTENT_DIR + '/web'); + + var setup = { + defines: defines, + copy: [ + [COMMON_WEB_FILES, B2G_BUILD_CONTENT_DIR + '/web'], + ['web/locale.properties', B2G_BUILD_CONTENT_DIR + '/web'], + ['external/webL10n/l10n.js', B2G_BUILD_CONTENT_DIR + '/web'] + ], + preprocess: [ + [COMMON_WEB_FILES_PREPROCESS, B2G_BUILD_CONTENT_DIR + '/web'], + [BUILD_TARGET, B2G_BUILD_CONTENT_DIR + BUILD_TARGET] + ] + }; + builder.build(setup); +}; + // // make chrome // @@ -489,15 +522,12 @@ target.chrome = function() { cd(ROOT_DIR); echo(); echo('### Building Chrome extension'); + var defines = builder.merge(DEFINES, {CHROME: true}); var CHROME_BUILD_DIR = BUILD_DIR + '/chrome/', - CHROME_CONTENT_DIR = EXTENSION_SRC_DIR + '/chrome/content/', - CHROME_BUILD_CONTENT_DIR = CHROME_BUILD_DIR + '/content/', - CHROME_EXTENSION_FILES = - ['extensions/chrome/*.json', - 'extensions/chrome/*.html']; + CHROME_BUILD_CONTENT_DIR = CHROME_BUILD_DIR + '/content/'; - target.production(); + target.bundle(); target.buildnumber(); cd(ROOT_DIR); @@ -507,18 +537,20 @@ target.chrome = function() { mkdir('-p', CHROME_BUILD_CONTENT_DIR + BUILD_DIR); mkdir('-p', CHROME_BUILD_CONTENT_DIR + '/web'); - // Copy extension files - cp('-R', CHROME_EXTENSION_FILES, CHROME_BUILD_DIR); - - // Copy a standalone version of pdf.js inside the content directory - cp(BUILD_TARGET, CHROME_BUILD_CONTENT_DIR + BUILD_DIR); - cp('-R', EXTENSION_WEB_FILES, CHROME_BUILD_CONTENT_DIR + '/web'); - // Replacing the l10n.js file with regular gh-pages one - rm(CHROME_BUILD_CONTENT_DIR + '/web/l10n.js'); - cp('external/webL10n/l10n.js', CHROME_BUILD_CONTENT_DIR + '/web'); - cp('web/locale.properties', CHROME_BUILD_CONTENT_DIR + '/web'); - mv('-f', CHROME_BUILD_CONTENT_DIR + '/web/viewer-production.html', - CHROME_BUILD_CONTENT_DIR + '/web/viewer.html'); + var setup = { + defines: defines, + copy: [ + [COMMON_WEB_FILES, CHROME_BUILD_CONTENT_DIR + '/web'], + [['extensions/chrome/*.json', 'extensions/chrome/*.html'], CHROME_BUILD_DIR], + [BUILD_TARGET, CHROME_BUILD_CONTENT_DIR + BUILD_TARGET], + ['external/webL10n/l10n.js', CHROME_BUILD_CONTENT_DIR + '/web'] + ], + preprocess: [ + [COMMON_WEB_FILES_PREPROCESS, CHROME_BUILD_CONTENT_DIR + '/web'], + ['web/locale.properties', CHROME_BUILD_CONTENT_DIR + '/web'] + ] + }; + builder.build(setup); }; diff --git a/src/api.js b/src/api.js index 3a5b8678d..4e7ce4e76 100644 --- a/src/api.js +++ b/src/api.js @@ -430,19 +430,18 @@ var WorkerTransport = (function WorkerTransportClosure() { try { var worker; - if (PDFJS.isFirefoxExtension) { - // The firefox extension can't load the worker from the resource:// - // url so we have to inline the script and then use the blob loader. - var bb = new MozBlobBuilder(); - bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent); - var blobUrl = window.URL.createObjectURL(bb.getBlob()); - worker = new Worker(blobUrl); - } else { - // Some versions of FF can't create a worker on localhost, see: - // https://bugzilla.mozilla.org/show_bug.cgi?id=683280 - worker = new Worker(workerSrc); - } - +//#if !(FIREFOX || MOZCENTRAL) + // Some versions of FF can't create a worker on localhost, see: + // https://bugzilla.mozilla.org/show_bug.cgi?id=683280 + worker = new Worker(workerSrc); +//#else +// // The firefox extension can't load the worker from the resource:// +// // url so we have to inline the script and then use the blob loader. +// var bb = new MozBlobBuilder(); +// bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent); +// var blobUrl = window.URL.createObjectURL(bb.getBlob()); +// worker = new Worker(blobUrl); +//#endif var messageHandler = new MessageHandler('main', worker); this.messageHandler = messageHandler; diff --git a/src/canvas.js b/src/canvas.js index 32c171216..9f61ba77b 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -157,6 +157,7 @@ var CanvasExtraState = (function CanvasExtraStateClosure() { this.wordSpacing = 0; this.textHScale = 1; this.textRenderingMode = TextRenderingMode.FILL; + this.textRise = 0; // Color spaces this.fillColorSpace = new DeviceGrayCS(); this.fillColorSpaceObj = null; @@ -601,7 +602,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.current.textRenderingMode = mode; }, setTextRise: function CanvasGraphics_setTextRise(rise) { - TODO('text rise: ' + rise); + this.current.textRise = rise; }, moveText: function CanvasGraphics_moveText(x, y) { this.current.x = this.current.lineX += x; @@ -628,7 +629,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { ctx.transform.apply(ctx, current.textMatrix); ctx.scale(1, -1); - ctx.translate(current.x, -1 * current.y); + ctx.translate(current.x, -current.y - current.textRise); ctx.transform.apply(ctx, fontMatrix); ctx.scale(textHScale, 1); }, @@ -1257,7 +1258,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { return CanvasGraphics; })(); -if (!isWorker) { +function checkPutBinaryImageDataCompatibility() { // Feature detection if the browser can use an Uint8Array directly as imgData. var canvas = document.createElement('canvas'); canvas.width = 1; @@ -1278,17 +1279,23 @@ if (!isWorker) { } catch (e) { CanvasGraphics.prototype.putBinaryImageData = function CanvasGraphicsPutBinaryImageDataShim(ctx, imgData, w, h) { - var tmpImgData = ctx.getImageData(0, 0, w, h); + var tmpImgData = 'createImageData' in ctx ? ctx.createImageData(w, h) : + ctx.getImageData(0, 0, w, h); - // Copy over the imageData pixel by pixel. var tmpImgDataPixels = tmpImgData.data; - var len = tmpImgDataPixels.length; - - while (len--) { - tmpImgDataPixels[len] = imgData.data[len]; + var data = imgData.data; + if ('set' in tmpImgDataPixels) + tmpImgDataPixels.set(data); + else { + // Copy over the imageData pixel by pixel. + for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++) + tmpImgDataPixels[i] = data[i]; } ctx.putImageData(tmpImgData, 0, 0); }; } } +if (!isWorker) { + checkPutBinaryImageDataCompatibility(); +} diff --git a/src/core.js b/src/core.js index e8d008074..aa366a6db 100644 --- a/src/core.js +++ b/src/core.js @@ -29,9 +29,11 @@ function getPdf(arg, callback) { var params = arg; if (typeof arg === 'string') params = { url: arg }; - +//#if !B2G var xhr = new XMLHttpRequest(); - +//#else +//var xhr = new XMLHttpRequest({mozSystem: true}); +//#endif xhr.open('GET', params.url); var headers = params.headers; @@ -403,6 +405,24 @@ var PDFDocument = (function PDFDocumentClosure() { return true; /* found */ } + var DocumentInfoValidators = { + get entries() { + // Lazily build this since all the validation functions below are not + // defined until after this file loads. + return shadow(this, 'entries', { + Title: isString, + Author: isString, + Subject: isString, + Keywords: isString, + Creator: isString, + Producer: isString, + CreationDate: isString, + ModDate: isString, + Trapped: isName + }); + } + }; + PDFDocument.prototype = { get linearization() { var length = this.stream.length; @@ -495,18 +515,27 @@ var PDFDocument = (function PDFDocumentClosure() { return shadow(this, 'numPages', num); }, getDocumentInfo: function PDFDocument_getDocumentInfo() { - var info; + var docInfo; if (this.xref.trailer.has('Info')) { var infoDict = this.xref.trailer.get('Info'); - info = {}; - infoDict.forEach(function(key, value) { - info[key] = typeof value !== 'string' ? value : - stringToPDFString(value); - }); + docInfo = {}; + var validEntries = DocumentInfoValidators.entries; + // Only fill the document info with valid entries from the spec. + for (var key in validEntries) { + if (infoDict.has(key)) { + var value = infoDict.get(key); + // Make sure the value conforms to the spec. + if (validEntries[key](value)) { + docInfo[key] = typeof value !== 'string' ? value : + stringToPDFString(value); + } else { + info('Bad value in document info for "' + key + '"'); + } + } + } } - - return shadow(this, 'getDocumentInfo', info); + return shadow(this, 'getDocumentInfo', docInfo); }, getFingerprint: function PDFDocument_getFingerprint() { var xref = this.xref, fileID; diff --git a/src/evaluator.js b/src/evaluator.js index 059ff113f..fe369db27 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -765,7 +765,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (widths) { var start = 0, end = 0; for (var i = 0, ii = widths.length; i < ii; i++) { - var code = widths[i]; + var code = xref.fetchIfRef(widths[i]); if (isArray(code)) { for (var j = 0, jj = code.length; j < jj; j++) glyphsWidths[start++] = code[j]; diff --git a/src/fonts.js b/src/fonts.js index 7b2dfbd4a..8cac29b24 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -418,7 +418,8 @@ var FontLoader = { document.documentElement.removeEventListener( 'pdfjsFontLoad', checkFontsLoaded, false); - callback(); + // Use timeout to fix chrome intermittent failures on font loading. + setTimeout(callback, 0); return true; } @@ -3292,6 +3293,14 @@ var Font = (function FontClosure() { return Font; })(); +var CallothersubrCmd = (function CallothersubrCmdClosure() { + function CallothersubrCmd(index) { + this.index = index; + } + + return CallothersubrCmd; +})(); + /* * Type1Parser encapsulate the needed code for parsing a Type1 font * program. Some of its logic depends on the Type2 charstrings @@ -3497,6 +3506,10 @@ var Type1Parser = function type1Parser() { i++; continue; } + + assert(argc == 0, 'callothersubr with arguments is not supported'); + charstring.push(new CallothersubrCmd(index)); + continue; } else if (escape == 17 || escape == 33) { // pop or setcurrentpoint commands can be ignored // since we are not doing callothersubr @@ -4016,23 +4029,23 @@ Type1Font.prototype = { }, getType2Charstrings: function Type1Font_getType2Charstrings( - type1Charstrings) { + type1Subrs) { var type2Charstrings = []; - var count = type1Charstrings.length; - for (var i = 0; i < count; i++) { - var charstring = type1Charstrings[i].charstring; - type2Charstrings.push(this.flattenCharstring(charstring.slice(), - this.commandsMap)); - } + var count = type1Subrs.length; + var type1Charstrings = []; + for (var i = 0; i < count; i++) + type1Charstrings.push(type1Subrs[i].charstring.slice()); + for (var i = 0; i < count; i++) + type2Charstrings.push(this.flattenCharstring(type1Charstrings, i)); return type2Charstrings; }, getType2Subrs: function Type1Font_getType2Subrs(type1Subrs) { var bias = 0; var count = type1Subrs.length; - if (count < 1240) + if (count < 1133) bias = 107; - else if (count < 33900) + else if (count < 33769) bias = 1131; else bias = 32768; @@ -4043,11 +4056,7 @@ Type1Font.prototype = { type2Subrs.push([0x0B]); for (var i = 0; i < count; i++) { - var subr = type1Subrs[i]; - if (!subr) - subr = [0x0B]; - - type2Subrs.push(this.flattenCharstring(subr, this.commandsMap)); + type2Subrs.push(this.flattenCharstring(type1Subrs, i)); } return type2Subrs; @@ -4079,7 +4088,11 @@ Type1Font.prototype = { 'hvcurveto': 31 }, - flattenCharstring: function Type1Font_flattenCharstring(charstring, map) { + flattenCharstring: function Type1Font_flattenCharstring(charstrings, index) { + var charstring = charstrings[index]; + if (!charstring) + return [0x0B]; + var map = this.commandsMap; // charstring changes size - can't cache .length in loop for (var i = 0; i < charstring.length; i++) { var command = charstring[i]; @@ -4091,6 +4104,17 @@ Type1Font.prototype = { charstring.splice(i++, 1, cmd[0], cmd[1]); else charstring[i] = cmd; + } else if (command instanceof CallothersubrCmd) { + var otherSubrCharstring = charstrings[command.index]; + if (otherSubrCharstring) { + var lastCommand = otherSubrCharstring.indexOf('return'); + if (lastCommand >= 0) + otherSubrCharstring = otherSubrCharstring.slice(0, lastCommand); + charstring.splice.apply(charstring, + [i, 1].concat(otherSubrCharstring)); + } else + charstring.splice(i, 1); // ignoring empty subr call + i--; } else { // Type1 charstring use a division for number above 32000 if (command > 32000) { diff --git a/src/function.js b/src/function.js index 56b405ede..2088ee219 100644 --- a/src/function.js +++ b/src/function.js @@ -103,11 +103,12 @@ var PDFFunction = (function PDFFunctionClosure() { var size = dict.get('Size'); var bps = dict.get('BitsPerSample'); - var order = dict.get('Order'); - if (!order) - order = 1; - if (order !== 1) - error('No support for cubic spline interpolation: ' + order); + var order = dict.get('Order') || 1; + if (order !== 1) { + // No description how cubic spline interpolation works in PDF32000:2008 + // As in poppler, ignoring order, linear interpolation may work as good + TODO('No support for cubic spline interpolation: ' + order); + } var encode = dict.get('Encode'); if (!encode) { diff --git a/src/obj.js b/src/obj.js index c01ffab58..d04234f07 100644 --- a/src/obj.js +++ b/src/obj.js @@ -624,7 +624,7 @@ var XRef = (function XRefClosure() { var e = this.entries[i]; if (e === null) return null; - return e.free ? null : e; // returns null is the entry is free + return e.free || !e.offset ? null : e; // returns null if entry is free }, fetchIfRef: function XRef_fetchIfRef(obj) { if (!isRef(obj)) diff --git a/src/pdf.js b/src/pdf.js index 1042a651b..cd8085764 100644 --- a/src/pdf.js +++ b/src/pdf.js @@ -7,9 +7,13 @@ var PDFJS = {}; // Use strict in our context only - users might not want it 'use strict'; - PDFJS.build = 'PDFJSSCRIPT_BUNDLE_VER'; + PDFJS.build = +//#if !BUNDLE_VERSION + 'PDFJSSCRIPT_BUNDLE_VER'; +//#else +//#expand '__BUNDLE_VERSION__'; +//#endif - // Files are inserted below - see Makefile - /* PDFJSSCRIPT_INCLUDE_ALL */ +//#expand __BUNDLE__ }).call((typeof window === 'undefined') ? this : window); diff --git a/test/pdfs/issue1001.pdf.link b/test/pdfs/issue1001.pdf.link deleted file mode 100644 index 24e1bebc2..000000000 --- a/test/pdfs/issue1001.pdf.link +++ /dev/null @@ -1 +0,0 @@ -http://www.myhillsapartment.com/island_club/floorplans/images/links/Island_IC_brochure.pdf diff --git a/test/pdfs/issue1655.pdf.link b/test/pdfs/issue1655.pdf.link new file mode 100644 index 000000000..ee983a687 --- /dev/null +++ b/test/pdfs/issue1655.pdf.link @@ -0,0 +1 @@ +http://bblum.net/thesis-draft.pdf diff --git a/test/pdfs/issue1729.pdf.link b/test/pdfs/issue1729.pdf.link new file mode 100644 index 000000000..a6a73d5a7 --- /dev/null +++ b/test/pdfs/issue1729.pdf.link @@ -0,0 +1 @@ +http://www.environmentallights.com/files/documents/ir_light_hazard.pdf diff --git a/test/pdfs/issue845.pdf.link b/test/pdfs/issue845.pdf.link new file mode 100644 index 000000000..b315abebb --- /dev/null +++ b/test/pdfs/issue845.pdf.link @@ -0,0 +1 @@ +http://www.mediafire.com/?k8t1he9aa1pt840 diff --git a/test/pdfs/lista_preliminar.pdf.link b/test/pdfs/lista_preliminar.pdf.link deleted file mode 100644 index 54102b3b1..000000000 --- a/test/pdfs/lista_preliminar.pdf.link +++ /dev/null @@ -1 +0,0 @@ -http://www.lfg.com.br/concursodebolsas/lista_preliminar_classificao.pdf diff --git a/test/pdfs/wdsg_fitc.pdf.link b/test/pdfs/wdsg_fitc.pdf.link index 77d3b590d..bd22b86ba 100644 --- a/test/pdfs/wdsg_fitc.pdf.link +++ b/test/pdfs/wdsg_fitc.pdf.link @@ -1 +1 @@ -http://www.airgid.com/book/wdsg_fitc.pdf +http://www.airgid.com/wp-content/uploads/2012/06/wdsg_fitc.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index 1b372e906..51a5fd8cf 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -334,14 +334,6 @@ "skipPages": [1], "type": "eq" }, - { "id": "lista_preliminar", - "file": "pdfs/lista_preliminar.pdf", - "md5": "4eff251319eeb660ba8a7a5cfac7787d", - "rounds": 1, - "link": true, - "pageLimit": 3, - "type": "eq" - }, { "id": "issue919", "file": "pdfs/issue919.pdf", "md5": "3a1716a512aca4d7a8d6106bd4885d14", @@ -356,13 +348,6 @@ "rounds": 1, "type": "eq" }, - { "id": "issue1001", - "file": "pdfs/issue1001.pdf", - "md5": "0f1496e80a82a923e91d9e74c55ad94e", - "rounds": 1, - "link": true, - "type": "eq" - }, { "id": "issue1586", "file": "pdfs/pdfjsbad1586.pdf", "md5": "793d0870f0b0c613799b0677d64daca4", @@ -421,7 +406,7 @@ "file": "pdfs/issue1096.pdf", "md5": "7f75d2b4b93c78d401ff39e8c1b00612", "rounds": 1, - "pageLimit": 10, + "pageLimit": 9, "link": true, "type": "eq" }, @@ -530,6 +515,15 @@ "link": true, "type": "eq" }, + { "id": "issue1655", + "file": "pdfs/issue1655.pdf", + "md5": "696ef6de6f4f71643771419ef04fc968", + "rounds": 1, + "skipPages": [1, 2, 3, 4, 5, 6, 7, 8], + "pageLimit": 9, + "link": true, + "type": "eq" + }, { "id": "issue1133", "file": "pdfs/issue1133.pdf", "md5": "d1b61580cb100e3df93d33703af1773a", @@ -634,6 +628,13 @@ "link": true, "type": "eq" }, + { "id": "issue845", + "file": "pdfs/issue845.pdf", + "md5": "89ddf9e63cac4fa2dedcfe32a62e5407", + "rounds": 1, + "link": true, + "type": "eq" + }, { "id": "issue818", "file": "pdfs/issue818.pdf", "md5": "dd2f8a5bd65164ad74da2b45a6ca90cc", @@ -641,5 +642,13 @@ "pageLimit": 1, "link": true, "type": "eq" + }, + { "id": "issue1729", + "file": "pdfs/issue1729.pdf", + "md5": "29b0eddc3e1dcb23a44384037032d470", + "rounds": 1, + "pageLimit": 1, + "link": true, + "type": "load" } ] diff --git a/web/compatibility.js b/web/compatibility.js index 4b7119c63..cb2941042 100644 --- a/web/compatibility.js +++ b/web/compatibility.js @@ -124,24 +124,63 @@ }; })(); +// No readAsArrayBuffer ? +(function checkFileReaderReadAsArrayBuffer() { + if (typeof FileReader === 'undefined') + return; // FileReader is not implemented + var frPrototype = FileReader.prototype; + // Older versions of Firefox might not have readAsArrayBuffer + if ('readAsArrayBuffer' in frPrototype) + return; // readAsArrayBuffer is implemented + Object.defineProperty(frPrototype, 'readAsArrayBuffer', { + value: function fileReaderReadAsArrayBuffer(blob) { + var fileReader = new FileReader(); + var originalReader = this; + fileReader.onload = function fileReaderOnload(evt) { + var data = evt.target.result; + var buffer = new ArrayBuffer(data.length); + var uint8Array = new Uint8Array(buffer); + + for (var i = 0, ii = data.length; i < ii; i++) + uint8Array[i] = data.charCodeAt(i); + + Object.defineProperty(originalReader, 'result', { + value: buffer, + enumerable: true, + writable: false, + configurable: true + }); + + var event = document.createEvent('HTMLEvents'); + event.initEvent('load', false, false); + originalReader.dispatchEvent(event); + }; + fileReader.readAsBinaryString(blob); + } + }); +})(); + // No XMLHttpRequest.response ? (function checkXMLHttpRequestResponseCompatibility() { var xhrPrototype = XMLHttpRequest.prototype; + if (!('overrideMimeType' in xhrPrototype)) { + // IE10 might have response, but not overrideMimeType + Object.defineProperty(xhrPrototype, 'overrideMimeType', { + value: function xmlHttpRequestOverrideMimeType(mimeType) {} + }); + } if ('response' in xhrPrototype || 'mozResponseArrayBuffer' in xhrPrototype || 'mozResponse' in xhrPrototype || 'responseArrayBuffer' in xhrPrototype) return; - // IE ? + // IE9 ? if (typeof VBArray !== 'undefined') { Object.defineProperty(xhrPrototype, 'response', { get: function xmlHttpRequestResponseGet() { return new Uint8Array(new VBArray(this.responseBody).toArray()); } }); - Object.defineProperty(xhrPrototype, 'overrideMimeType', { - value: function xmlHttpRequestOverrideMimeType(mimeType) {} - }); return; } diff --git a/web/debugger.js b/web/debugger.js index aeb3ab4ce..0cfe26355 100644 --- a/web/debugger.js +++ b/web/debugger.js @@ -44,7 +44,7 @@ var FontInspector = (function FontInspectorClosure() { } } return { - // Poperties/functions needed by PDFBug. + // Properties/functions needed by PDFBug. id: 'FontInspector', name: 'Font Inspector', panel: null, @@ -140,7 +140,7 @@ var StepperManager = (function StepperManagerClosure() { var stepperChooser = null; var breakPoints = {}; return { - // Poperties/functions needed by PDFBug. + // Properties/functions needed by PDFBug. id: 'Stepper', name: 'Stepper', panel: null, @@ -207,7 +207,7 @@ var StepperManager = (function StepperManagerClosure() { var Stepper = (function StepperClosure() { function Stepper(panel, pageIndex, initialBreakPoints) { this.panel = panel; - this.len; + this.len = 0; this.breakPoint = 0; this.nextBreakPoint = null; this.pageIndex = pageIndex; @@ -236,6 +236,7 @@ var Stepper = (function StepperClosure() { headerRow.appendChild(c('th', 'fn')); headerRow.appendChild(c('th', 'args')); + var self = this; for (var i = 0; i < IRQueue.fnArray.length; i++) { var line = c('tr'); line.className = 'line'; @@ -249,7 +250,6 @@ var Stepper = (function StepperClosure() { cbox.type = 'checkbox'; cbox.className = 'points'; cbox.checked = checked; - var self = this; cbox.onclick = (function(x) { return function() { if (this.checked) @@ -298,7 +298,7 @@ var Stepper = (function StepperClosure() { callback(); break; } - } + }; dom.addEventListener('keydown', listener, false); self.goTo(idx); }, @@ -331,7 +331,7 @@ var Stats = (function Stats() { return false; } return { - // Poperties/functions needed by PDFBug. + // Properties/functions needed by PDFBug. id: 'Stats', name: 'Stats', panel: null, @@ -429,12 +429,12 @@ var PDFBug = (function PDFBugClosure() { // Initialize all the debugging tools. var tools = this.tools; + var self = this; for (var i = 0; i < tools.length; ++i) { var tool = tools[i]; var panel = document.createElement('div'); var panelButton = document.createElement('button'); panelButton.textContent = tool.name; - var self = this; panelButton.addEventListener('click', (function(selected) { return function(event) { event.preventDefault(); diff --git a/web/firefoxcom.js b/web/firefoxcom.js new file mode 100644 index 000000000..f60c26aed --- /dev/null +++ b/web/firefoxcom.js @@ -0,0 +1,60 @@ +var FirefoxCom = (function FirefoxComClosure() { + return { + /** + * Creates an event that the extension is listening for and will + * synchronously respond to. + * NOTE: It is reccomended to use request() instead since one day we may not + * be able to synchronously reply. + * @param {String} action The action to trigger. + * @param {String} data Optional data to send. + * @return {*} The response. + */ + requestSync: function(action, data) { + var request = document.createTextNode(''); + request.setUserData('action', action, null); + request.setUserData('data', data, null); + request.setUserData('sync', true, null); + document.documentElement.appendChild(request); + + var sender = document.createEvent('Events'); + sender.initEvent('pdf.js.message', true, false); + request.dispatchEvent(sender); + var response = request.getUserData('response'); + document.documentElement.removeChild(request); + return response; + }, + /** + * Creates an event that the extension is listening for and will + * asynchronously respond by calling the callback. + * @param {String} action The action to trigger. + * @param {String} data Optional data to send. + * @param {Function} callback Optional response callback that will be called + * with one data argument. + */ + request: function(action, data, callback) { + var request = document.createTextNode(''); + request.setUserData('action', action, null); + request.setUserData('data', data, null); + request.setUserData('sync', false, null); + if (callback) { + request.setUserData('callback', callback, null); + + document.addEventListener('pdf.js.response', function listener(event) { + var node = event.target, + callback = node.getUserData('callback'), + response = node.getUserData('response'); + + document.documentElement.removeChild(node); + + document.removeEventListener('pdf.js.response', listener, false); + return callback(response); + }, false); + } + document.documentElement.appendChild(request); + + var sender = document.createEvent('HTMLEvents'); + sender.initEvent('pdf.js.message', true, false); + return request.dispatchEvent(sender); + } + }; +})(); diff --git a/web/viewer-snippet-b2g.html b/web/viewer-snippet-b2g.html new file mode 100644 index 000000000..86a09d255 --- /dev/null +++ b/web/viewer-snippet-b2g.html @@ -0,0 +1,9 @@ +<!-- This snippet is used in b2g, see Makefile --> +<link rel="resource" type="application/l10n" href="locale.properties"/> +<script type="text/javascript" src="l10n.js"></script> +<script type="text/javascript" src="../build/pdf.js"></script> +<script type="text/javascript"> + // This specifies the location of the pdf.js file. + PDFJS.workerSrc = "../build/pdf.js"; + PDFJS.disableTextLayer = true; +</script> diff --git a/web/viewer-snippet-firefox-extension.html b/web/viewer-snippet-firefox-extension.html index 57d7875eb..5b8cc9eaf 100644 --- a/web/viewer-snippet-firefox-extension.html +++ b/web/viewer-snippet-firefox-extension.html @@ -1,14 +1,11 @@ <!-- This snippet is used in firefox extension, see Makefile --> <base href="resource://pdf.js/web/" /> -<script type="application/l10n"> -<!-- PDFJSSCRIPT_LOCALE_DATA --> -</script> <script type="text/javascript" src="l10n.js"></script> <script type="text/javascript" id="PDFJS_SCRIPT_TAG"> <!-- // pdf.js is inlined here because resource:// urls won't work // for loading workers. -/* PDFJSSCRIPT_INCLUDE_BUNDLE */ +//#include ../build/pdf.js --> </script> <script type="text/javascript"> diff --git a/web/viewer.html b/web/viewer.html index 58b35464f..daec51172 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -4,40 +4,60 @@ <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <title>PDF.js viewer</title> - <!-- PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION --> + +<!--#if FIREFOX || MOZCENTRAL--> +<!--#include viewer-snippet-firefox-extension.html--> +<!--#endif--> <link rel="stylesheet" href="viewer.css"/> - <link rel="resource" type="application/l10n" href="locale.properties"/><!-- PDFJSSCRIPT_REMOVE_CORE --> - - <script type="text/javascript" src="compatibility.js"></script> <!-- PDFJSSCRIPT_REMOVE_FIREFOX_EXTENSION --> - <script type="text/javascript" src="../external/webL10n/l10n.js"></script><!-- PDFJSSCRIPT_REMOVE_CORE --> - - <!-- PDFJSSCRIPT_INCLUDE_BUILD --> - <script type="text/javascript" src="../src/core.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/util.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/api.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/metadata.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/canvas.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/obj.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/function.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/charsets.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/cidmaps.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/colorspace.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/crypto.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/evaluator.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/fonts.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/glyphlist.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/image.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/metrics.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/parser.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/pattern.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/stream.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/worker.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../external/jpgjs/jpg.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/jpx.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/jbig2.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript" src="../src/bidi.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> - <script type="text/javascript">PDFJS.workerSrc = '../src/worker_loader.js';</script> <!-- PDFJSSCRIPT_REMOVE_CORE --> +<!--#if !PRODUCTION--> + <link rel="resource" type="application/l10n" href="locale.properties"/> +<!--#endif--> + +<!--#if !(FIREFOX || MOZCENTRAL)--> + <script type="text/javascript" src="compatibility.js"></script> +<!--#endif--> + +<!--#if !PRODUCTION--> + <script type="text/javascript" src="../external/webL10n/l10n.js"></script> +<!--#endif--> + +<!--#if !PRODUCTION--> + <script type="text/javascript" src="../src/core.js"></script> + <script type="text/javascript" src="../src/util.js"></script> + <script type="text/javascript" src="../src/api.js"></script> + <script type="text/javascript" src="../src/metadata.js"></script> + <script type="text/javascript" src="../src/canvas.js"></script> + <script type="text/javascript" src="../src/obj.js"></script> + <script type="text/javascript" src="../src/function.js"></script> + <script type="text/javascript" src="../src/charsets.js"></script> + <script type="text/javascript" src="../src/cidmaps.js"></script> + <script type="text/javascript" src="../src/colorspace.js"></script> + <script type="text/javascript" src="../src/crypto.js"></script> + <script type="text/javascript" src="../src/evaluator.js"></script> + <script type="text/javascript" src="../src/fonts.js"></script> + <script type="text/javascript" src="../src/glyphlist.js"></script> + <script type="text/javascript" src="../src/image.js"></script> + <script type="text/javascript" src="../src/metrics.js"></script> + <script type="text/javascript" src="../src/parser.js"></script> + <script type="text/javascript" src="../src/pattern.js"></script> + <script type="text/javascript" src="../src/stream.js"></script> + <script type="text/javascript" src="../src/worker.js"></script> + <script type="text/javascript" src="../external/jpgjs/jpg.js"></script> + <script type="text/javascript" src="../src/jpx.js"></script> + <script type="text/javascript" src="../src/jbig2.js"></script> + <script type="text/javascript" src="../src/bidi.js"></script> + <script type="text/javascript">PDFJS.workerSrc = '../src/worker_loader.js';</script> +<!--#endif--> + +<!--#if GENERIC || CHROME--> +<!--#include viewer-snippet.html--> +<!--#endif--> + +<!--#if B2G--> +<!--#include viewer-snippet-b2g.html--> +<!--#endif--> + <script type="text/javascript" src="debugger.js"></script> <script type="text/javascript" src="viewer.js"></script> </head> diff --git a/web/viewer.js b/web/viewer.js index 13b260a64..ad327924f 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -90,66 +90,9 @@ var ProgressBar = (function ProgressBarClosure() { return ProgressBar; })(); -var FirefoxCom = (function FirefoxComClosure() { - return { - /** - * Creates an event that the extension is listening for and will - * synchronously respond to. - * NOTE: It is reccomended to use request() instead since one day we may not - * be able to synchronously reply. - * @param {String} action The action to trigger. - * @param {String} data Optional data to send. - * @return {*} The response. - */ - requestSync: function(action, data) { - var request = document.createTextNode(''); - request.setUserData('action', action, null); - request.setUserData('data', data, null); - request.setUserData('sync', true, null); - document.documentElement.appendChild(request); - - var sender = document.createEvent('Events'); - sender.initEvent('pdf.js.message', true, false); - request.dispatchEvent(sender); - var response = request.getUserData('response'); - document.documentElement.removeChild(request); - return response; - }, - /** - * Creates an event that the extension is listening for and will - * asynchronously respond by calling the callback. - * @param {String} action The action to trigger. - * @param {String} data Optional data to send. - * @param {Function} callback Optional response callback that will be called - * with one data argument. - */ - request: function(action, data, callback) { - var request = document.createTextNode(''); - request.setUserData('action', action, null); - request.setUserData('data', data, null); - request.setUserData('sync', false, null); - if (callback) { - request.setUserData('callback', callback, null); - - document.addEventListener('pdf.js.response', function listener(event) { - var node = event.target, - callback = node.getUserData('callback'), - response = node.getUserData('response'); - - document.documentElement.removeChild(node); - - document.removeEventListener('pdf.js.response', listener, false); - return callback(response); - }, false); - } - document.documentElement.appendChild(request); - - var sender = document.createEvent('HTMLEvents'); - sender.initEvent('pdf.js.message', true, false); - return request.dispatchEvent(sender); - } - }; -})(); +//#if FIREFOX || MOZCENTRAL +//#include firefoxcom.js +//#endif // Settings Manager - This is a utility for saving settings // First we see if localStorage is available @@ -167,17 +110,17 @@ var Settings = (function SettingsClosure() { } })(); - var isFirefoxExtension = PDFJS.isFirefoxExtension; - function Settings(fingerprint) { var database = null; var index; - if (isFirefoxExtension) - database = FirefoxCom.requestSync('getDatabase', null) || '{}'; - else if (isLocalStorageEnabled) +//#if !(FIREFOX || MOZCENTRAL) + if (isLocalStorageEnabled) database = localStorage.getItem('database') || '{}'; else - return false; + return; +//#else +// database = FirefoxCom.requestSync('getDatabase', null) || '{}'; +//#endif database = JSON.parse(database); if (!('files' in database)) @@ -205,10 +148,12 @@ var Settings = (function SettingsClosure() { var file = this.file; file[name] = val; var database = JSON.stringify(this.database); - if (isFirefoxExtension) - FirefoxCom.requestSync('setDatabase', database); - else if (isLocalStorageEnabled) +//#if !(FIREFOX || MOZCENTRAL) + if (isLocalStorageEnabled) localStorage.setItem('database', database); +//#else +// FirefoxCom.requestSync('setDatabase', database); +//#endif }, get: function settingsGet(name, defaultValue) { @@ -351,9 +296,10 @@ var PDFView = { set page(val) { var pages = this.pages; var input = document.getElementById('pageNumber'); + var event = document.createEvent('UIEvents'); + event.initUIEvent('pagechange', false, false, window, 0); + if (!(0 < val && val <= pages.length)) { - var event = document.createEvent('UIEvents'); - event.initUIEvent('pagechange', false, false, window, 0); event.pageNumber = this.page; window.dispatchEvent(event); return; @@ -361,8 +307,6 @@ var PDFView = { pages[val - 1].updateStats(); currentPageNumber = val; - var event = document.createEvent('UIEvents'); - event.initUIEvent('pagechange', false, false, window, 0); event.pageNumber = val; window.dispatchEvent(event); @@ -486,52 +430,54 @@ var PDFView = { } var url = this.url.split('#')[0]; - if (PDFJS.isFirefoxExtension) { - // Document isn't ready just try to download with the url. - if (!this.pdfDocument) { - noData(); - return; - } - this.pdfDocument.getData().then( - function getDataSuccess(data) { - var bb = new MozBlobBuilder(); - bb.append(data.buffer); - var blobUrl = window.URL.createObjectURL( - bb.getBlob('application/pdf')); - - FirefoxCom.request('download', { blobUrl: blobUrl, originalUrl: url }, - function response(err) { - if (err) { - // This error won't really be helpful because it's likely the - // fallback won't work either (or is already open). - PDFView.error('PDF failed to download.'); - } - window.URL.revokeObjectURL(blobUrl); - } - ); - }, - noData // Error ocurred try downloading with just the url. - ); - } else { - url += '#pdfjs.action=download', '_parent'; - window.open(url, '_parent'); - } +//#if !(FIREFOX || MOZCENTRAL) + url += '#pdfjs.action=download'; + window.open(url, '_parent'); +//#else +// // Document isn't ready just try to download with the url. +// if (!this.pdfDocument) { +// noData(); +// return; +// } +// this.pdfDocument.getData().then( +// function getDataSuccess(data) { +// var bb = new MozBlobBuilder(); +// bb.append(data.buffer); +// var blobUrl = window.URL.createObjectURL( +// bb.getBlob('application/pdf')); +// +// FirefoxCom.request('download', { blobUrl: blobUrl, originalUrl: url }, +// function response(err) { +// if (err) { +// // This error won't really be helpful because it's likely the +// // fallback won't work either (or is already open). +// PDFView.error('PDF failed to download.'); +// } +// window.URL.revokeObjectURL(blobUrl); +// } +// ); +// }, +// noData // Error occurred try downloading with just the url. +// ); +//#endif }, fallback: function pdfViewFallback() { - if (!PDFJS.isFirefoxExtension) - return; - // Only trigger the fallback once so we don't spam the user with messages - // for one PDF. - if (this.fellback) - return; - this.fellback = true; - var url = this.url.split('#')[0]; - FirefoxCom.request('fallback', url, function response(download) { - if (!download) - return; - PDFView.download(); - }); +//#if !(FIREFOX || MOZCENTRAL) +// return; +//#else +// // Only trigger the fallback once so we don't spam the user with messages +// // for one PDF. +// if (this.fellback) +// return; +// this.fellback = true; +// var url = this.url.split('#')[0]; +// FirefoxCom.request('fallback', url, function response(download) { +// if (!download) +// return; +// PDFView.download(); +// }); +//#endif }, navigateTo: function pdfViewNavigateTo(dest) { @@ -583,9 +529,11 @@ var PDFView = { * @param {String} anchor The anchor hash include the #. */ getAnchorUrl: function getAnchorUrl(anchor) { - if (PDFJS.isFirefoxExtension) - return this.url.split('#')[0] + anchor; +//#if !(FIREFOX || MOZCENTRAL) return anchor; +//#else +// return this.url.split('#')[0] + anchor; +//#endif }, /** @@ -619,11 +567,7 @@ var PDFView = { } } } - if (PDFJS.isFirefoxExtension) { - console.error(message + '\n' + moreInfoText); - this.fallback(); - return; - } +//#if !(FIREFOX || MOZCENTRAL) var errorWrapper = document.getElementById('errorWrapper'); errorWrapper.removeAttribute('hidden'); @@ -653,6 +597,10 @@ var PDFView = { errorMoreInfo.value = moreInfoText; errorMoreInfo.rows = moreInfoText.split('\n').length - 1; +//#else +// console.error(message + '\n' + moreInfoText); +// this.fallback(); +//#endif }, progress: function pdfViewProgress(level) { @@ -811,7 +759,7 @@ var PDFView = { } }, - getHighestPriority: function pdfViewGetHighestPriority(visibleViews, views, + getHighestPriority: function pdfViewGetHighestPriority(visible, views, scrolledDown) { // The state has changed figure out which page has the highest priority to // render next (if any). @@ -819,9 +767,10 @@ var PDFView = { // 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) { - info('No visible views.'); return false; } for (var i = 0; i < numVisible; ++i) { @@ -832,13 +781,12 @@ var PDFView = { // All the visible views have rendered, try to render next/previous pages. if (scrolledDown) { - var lastVisible = visibleViews[visibleViews.length - 1]; - var nextPageIndex = lastVisible.id; + 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 = visibleViews[0].id - 2; + var previousPageIndex = visible.first.id - 2; if (views[previousPageIndex] && !this.isViewFinished(views[previousPageIndex])) return views[previousPageIndex]; @@ -876,14 +824,14 @@ var PDFView = { search: function pdfViewStartSearch() { // Limit this function to run every <SEARCH_TIMEOUT>ms. var SEARCH_TIMEOUT = 250; - var lastSeach = this.lastSearch; + var lastSearch = this.lastSearch; var now = Date.now(); - if (lastSeach && (now - lastSeach) < SEARCH_TIMEOUT) { + if (lastSearch && (now - lastSearch) < SEARCH_TIMEOUT) { if (!this.searchTimer) { this.searchTimer = setTimeout(function resumeSearch() { PDFView.search(); }, - SEARCH_TIMEOUT - (now - lastSeach) + SEARCH_TIMEOUT - (now - lastSearch) ); } return; @@ -952,7 +900,6 @@ var PDFView = { } if ('page' in params) { var pageNumber = (params.page | 0) || 1; - this.page = pageNumber; if ('zoom' in params) { var zoomArgs = params.zoom.split(','); // scale,left,top // building destination array @@ -968,9 +915,9 @@ var PDFView = { (zoomArgs[2] | 0), zoomArg]; var currentPage = this.pages[pageNumber - 1]; currentPage.scrollIntoView(dest); - } else - this.page = params.page; // simple page - return; + } else { + this.page = pageNumber; // simple page + } } } else if (/^\d+$/.test(hash)) // page number this.page = hash; @@ -1041,13 +988,13 @@ var PDFView = { extractPageText(pageIndex + 1); } ); - }; + } extractPageText(0); }, getVisiblePages: function pdfViewGetVisiblePages() { return this.getVisibleElements(this.container, - this.pages); + this.pages, true); }, getVisibleThumbs: function pdfViewGetVisibleThumbs() { @@ -1056,11 +1003,12 @@ var PDFView = { }, // Generic helper to find out what elements are visible within a scroll pane. - getVisibleElements: function pdfViewGetVisibleElements(scrollEl, views) { + getVisibleElements: function pdfViewGetVisibleElements( + scrollEl, views, sortByVisibility) { var currentHeight = 0, view; var top = scrollEl.scrollTop; - for (var i = 1; i <= views.length; ++i) { + for (var i = 1, ii = views.length; i <= ii; ++i) { view = views[i - 1]; currentHeight = view.el.offsetTop; if (currentHeight + view.el.clientHeight > top) @@ -1078,19 +1026,38 @@ var PDFView = { view: currentPage }); - return visible; + return { first: currentPage, last: currentPage, views: visible}; } var bottom = top + scrollEl.clientHeight; - for (; i <= views.length && currentHeight < bottom; ++i) { + var nextHeight, hidden, percent, viewHeight; + for (; i <= ii && currentHeight < bottom; ++i) { view = views[i - 1]; + viewHeight = view.el.clientHeight; currentHeight = view.el.offsetTop; + nextHeight = currentHeight + viewHeight; + hidden = Math.max(0, top - currentHeight) + + Math.max(0, nextHeight - bottom); + percent = Math.floor((viewHeight - hidden) * 100.0 / viewHeight); visible.push({ id: view.id, y: currentHeight, - view: view }); - currentHeight += view.el.clientHeight; + view: view, percent: percent }); + currentHeight = nextHeight; } - return visible; + 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}; }, // Helper function to parse query string (e.g. ?param1=value&parm2=...). @@ -1727,13 +1694,13 @@ var CustomStyle = (function CustomStyleClosure() { //if all fails then set to undefined return (_cache[propName] = 'undefined'); - } + }; CustomStyle.setProp = function set(propName, element, str) { var prop = this.getProp(propName); if (prop != 'undefined') element.style[prop] = str; - } + }; return CustomStyle; })(); @@ -1763,6 +1730,7 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { if (textDivs.length === 0) { clearInterval(renderTimer); renderingDone = true; + self.textLayerDiv = textLayerDiv = canvas = ctx = null; return; } var textDiv = textDivs.shift(); @@ -1802,7 +1770,7 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { // Resume rendering renderTimer = setInterval(renderTextLayer, renderInterval); }, resumeInterval); - }; // textLayerOnScroll + } // textLayerOnScroll window.addEventListener('scroll', textLayerOnScroll, false); }; // endLayout @@ -1830,15 +1798,21 @@ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) { PDFView.initialize(); var params = PDFView.parseQueryString(document.location.search.substring(1)); - var file = PDFJS.isFirefoxExtension ? - window.location.toString() : params.file || kDefaultURL; +//#if !(FIREFOX || MOZCENTRAL) + var file = params.file || kDefaultURL; +//#else +//var file = window.location.toString() +//#endif - if (PDFJS.isFirefoxExtension || !window.File || !window.FileReader || - !window.FileList || !window.Blob) { +//#if !(FIREFOX || MOZCENTRAL) + if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { document.getElementById('openFile').setAttribute('hidden', 'true'); } else { document.getElementById('fileInput').value = null; } +//#else +//document.getElementById('openFile').setAttribute('hidden', 'true'); +//#endif // Special debugging flags in the hash section of the URL. var hash = document.location.hash.substring(1); @@ -1847,18 +1821,21 @@ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) { if ('disableWorker' in hashParams) PDFJS.disableWorker = (hashParams['disableWorker'] === 'true'); - if (!PDFJS.isFirefoxExtension) { - var locale = navigator.language; - if ('locale' in hashParams) - locale = hashParams['locale']; - mozL10n.language.code = locale; - } +//#if !(FIREFOX || MOZCENTRAL) + var locale = navigator.language; + if ('locale' in hashParams) + locale = hashParams['locale']; + mozL10n.language.code = locale; +//#endif if ('disableTextLayer' in hashParams) PDFJS.disableTextLayer = (hashParams['disableTextLayer'] === 'true'); - if ('pdfBug' in hashParams && - (!PDFJS.isFirefoxExtension || FirefoxCom.requestSync('pdfBugEnabled'))) { +//#if !(FIREFOX || MOZCENTRAL) + if ('pdfBug' in hashParams) { +//#else +//if ('pdfBug' in hashParams && FirefoxCom.requestSync('pdfBugEnabled')) { +//#endif PDFJS.pdfBug = true; var pdfBug = hashParams['pdfBug']; var enabled = pdfBug.split(','); @@ -1866,10 +1843,12 @@ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) { PDFBug.init(); } - if (!PDFJS.isFirefoxExtension || - (PDFJS.isFirefoxExtension && FirefoxCom.requestSync('searchEnabled'))) { - document.querySelector('#viewSearch').classList.remove('hidden'); - } +//#if !(FIREFOX || MOZCENTRAL) +//#else +//if (FirefoxCom.requestSync('searchEnabled')) { +// document.querySelector('#viewSearch').classList.remove('hidden'); +//} +//#endif if (!PDFView.supportsPrinting) { document.getElementById('print').classList.add('hidden'); @@ -1907,27 +1886,50 @@ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) { PDFView.renderHighestPriority(); }); - if (PDFJS.isFirefoxExtension && - FirefoxCom.requestSync('getLoadingType') == 'passive') { - PDFView.setTitleUsingUrl(file); - PDFView.initPassiveLoading(); - } else - PDFView.open(file, 0); +//#if (FIREFOX || MOZCENTRAL) +//if (FirefoxCom.requestSync('getLoadingType') == 'passive') { +// PDFView.setTitleUsingUrl(file); +// PDFView.initPassiveLoading(); +//} +//#endif + +//#if !B2G + PDFView.open(file, 0); +//#endif }, true); function updateViewarea() { + if (!PDFView.initialized) return; - var visiblePages = PDFView.getVisiblePages(); + var visible = PDFView.getVisiblePages(); + var visiblePages = visible.views; PDFView.renderHighestPriority(); var currentId = PDFView.page; - var firstPage = visiblePages[0]; + 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 === PDFView.page) { + stillFullyVisible = true; + break; + } + } + + if (!stillFullyVisible) { + currentId = visiblePages[0].id; + } if (!PDFView.isFullscreen) { updateViewarea.inProgress = true; // used in "set page" - PDFView.page = firstPage.id; + PDFView.page = currentId; updateViewarea.inProgress = false; } @@ -1975,20 +1977,13 @@ window.addEventListener('change', function webViewerChange(evt) { // Read the local file into a Uint8Array. var fileReader = new FileReader(); fileReader.onload = function webViewerChangeFileReaderOnload(evt) { - var data = evt.target.result; - var buffer = new ArrayBuffer(data.length); + var buffer = evt.target.result; var uint8Array = new Uint8Array(buffer); - - for (var i = 0; i < data.length; i++) - uint8Array[i] = data.charCodeAt(i); - PDFView.open(uint8Array, 0); }; - // Read as a binary string since "readAsArrayBuffer" is not yet - // implemented in Firefox. var file = files[0]; - fileReader.readAsBinaryString(file); + fileReader.readAsArrayBuffer(file); PDFView.setTitleUsingUrl(file.name); // URL does not reflect proper document location - hiding some icons. @@ -2046,13 +2041,13 @@ window.addEventListener('pagechange', function pagechange(evt) { var thumbnail = document.getElementById('thumbnailContainer' + page); thumbnail.classList.add('selected'); var visibleThumbs = PDFView.getVisibleThumbs(); - var numVisibleThumbs = visibleThumbs.length; + var numVisibleThumbs = visibleThumbs.views.length; // If the thumbnail isn't currently visible scroll it into view. if (numVisibleThumbs > 0) { - var first = visibleThumbs[0].id; + var first = visibleThumbs.first.id; // Account for only one thumbnail being visible. var last = numVisibleThumbs > 1 ? - visibleThumbs[numVisibleThumbs - 1].id : first; + visibleThumbs.last.id : first; if (page <= first || page >= last) thumbnail.scrollIntoView(); } @@ -2171,3 +2166,21 @@ window.addEventListener('afterprint', function afterPrint(evt) { window.addEventListener('mozfullscreenchange', fullscreenChange, false); window.addEventListener('webkitfullscreenchange', fullscreenChange, false); })(); + +//#if B2G +//window.navigator.mozSetMessageHandler('activity', function(activity) { +// var url = activity.source.data.url; +// // Temporarily get the data here since the cross domain xhr is broken in +// // the worker currently, see bug 761227. +// var params = { +// url: url, +// error: function(e) { +// PDFView.error(mozL10n.get('loading_error', null, +// 'An error occurred while loading the PDF.'), e); +// } +// }; +// PDFJS.getPdf(params, function successCallback(data) { +// PDFView.open(data, 0); +// }); +//}); +//#endif