]> git.parisson.com Git - pdf.js.git/commitdiff
Initial merge of master.
author= <=>
Wed, 19 Oct 2011 18:14:13 +0000 (11:14 -0700)
committer= <=>
Wed, 19 Oct 2011 18:14:13 +0000 (11:14 -0700)
13 files changed:
Makefile
charsets.js
extensions/firefox/components/pdfContentHandler.js
fonts.js
metrics.js
pdf.js
test/driver.js
test/pdfs/extgstate.pdf
test/test_manifest.json
web/compatibility.js
web/viewer.css
web/viewer.html
web/viewer.js

index a9cd2793f97f03754f23be126a3a9ed5e8496302..ebffb374c7d2a640357e646fefe6f885d94028ba 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,9 @@ BUILD_DIR := build
 DEFAULT_BROWSERS := resources/browser_manifests/browser_manifest.json
 DEFAULT_TESTS := test_manifest.json
 
+EXTENSION_SRC := ./extensions/firefox
+EXTENSION_NAME := pdf.js.xpi
+
 # Let folks define custom rules for their clones.
 -include local.mk
 
@@ -103,14 +106,17 @@ lint:
 # TODO: Use the Closure compiler to optimize the pdf.js files.
 #
 GH_PAGES = $(BUILD_DIR)/gh-pages
-web: | compiler pages-repo \
+web: | extension compiler pages-repo \
        $(addprefix $(GH_PAGES)/, $(PDF_JS_FILES)) \
        $(addprefix $(GH_PAGES)/, $(wildcard web/*.*)) \
-       $(addprefix $(GH_PAGES)/, $(wildcard web/images/*.*))
+       $(addprefix $(GH_PAGES)/, $(wildcard web/images/*.*)) \
+       $(addprefix $(GH_PAGES)/, $(wildcard $(EXTENSION_SRC)/*.xpi))
 
        @cp $(GH_PAGES)/web/index.html.template $(GH_PAGES)/index.html;
        @cd $(GH_PAGES); git add -A;
+       @echo
        @echo "Website built in $(GH_PAGES)."
+       @echo "Don't forget to cd into $(GH_PAGES)/ and issue 'git commit' to push changes."
 
 # make pages-repo
 #
@@ -126,6 +132,7 @@ pages-repo: | $(BUILD_DIR)
        fi;
        @mkdir -p $(GH_PAGES)/web;
        @mkdir -p $(GH_PAGES)/web/images;
+       @mkdir -p $(GH_PAGES)/$(EXTENSION_SRC);
 
 $(GH_PAGES)/%.js: %.js
        @cp $< $@
@@ -136,6 +143,9 @@ $(GH_PAGES)/web/%: web/%
 $(GH_PAGES)/web/images/%: web/images/%
        @cp $< $@
 
+$(GH_PAGES)/$(EXTENSION_SRC)/%: $(EXTENSION_SRC)/%
+       @cp -R $< $@
+
 # # make compiler
 # #
 # # This target downloads the Closure compiler, and places it in the
@@ -149,13 +159,11 @@ $(GH_PAGES)/web/images/%: web/images/%
 #   curl $(COMPILER_URL) > $(BUILD_DIR)/compiler.zip;
 #   cd $(BUILD_DIR); unzip compiler.zip compiler.jar;
 
-# make firefox-extension
+# make extension
 #
 # This target produce a restartless firefox extension containing a
 # copy of the pdf.js source.
 CONTENT_DIR := content
-EXTENSION_SRC := ./extensions/firefox
-EXTENSION_NAME := pdf.js.xpi
 PDF_WEB_FILES = \
        web/images \
        web/compatibility.js \
index 59fcdf5cf7cde8d7be73d73f3d0a1d3cb6e0e0c8..7f54ab32753156dffacc855c9c4a00b87646db1f 100644 (file)
@@ -1,3 +1,7 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+
+'use strict';
 
 var ISOAdobeCharset = [
   '.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar',
index 8799240473eed8bd34b36682736974db57f64aa9..7746e41b65a53cf9b8eb5c51156b136524067967 100644 (file)
@@ -20,136 +20,40 @@ function log(aMsg) {
   dump(msg + '\n');
 }
 
-function fireEventTo(aName, aData, aWindow) {
-  let window = aWindow.wrappedJSObject;
-  let evt = window.document.createEvent('CustomEvent');
-  evt.initCustomEvent('pdf' + aName, false, false, aData);
-  window.document.dispatchEvent(evt);
-}
-
-function loadDocument(aWindow, aDocumentUrl) {
-  let xhr = Cc['@mozilla.org/xmlextras/xmlhttprequest;1']
-              .createInstance(Ci.nsIXMLHttpRequest);
-  xhr.onprogress = function updateProgress(evt) {
-    if (evt.lengthComputable)
-      fireEventTo(evt.type, evt.loaded / evt.total, aWindow);
-  };
-
-  xhr.onerror = function error(evt) {
-    fireEventTo(evt.type, false, aWindow);
-  };
-
-  xhr.onload = function load(evt) {
-    let data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
-                xhr.responseArrayBuffer || xhr.response);
-    try {
-      let view = new Uint8Array(data);
-
-      let window = aWindow.wrappedJSObject;
-      let arrayBuffer = new window.ArrayBuffer(data.byteLength);
-      let view2 = new window.Uint8Array(arrayBuffer);
-      view2.set(view);
-
-      fireEventTo(evt.type, arrayBuffer, aWindow);
-    } catch (e) {
-      log('Error - ' + e);
-    }
-  };
-
-  xhr.open('GET', aDocumentUrl);
-  xhr.responseType = 'arraybuffer';
-  xhr.send(null);
-}
-
-let WebProgressListener = {
-  init: function WebProgressListenerInit(aWindow, aUrl) {
-    this._locationHasChanged = false;
-    this._documentUrl = aUrl;
-
-    let flags = Ci.nsIWebProgress.NOTIFY_LOCATION |
-                Ci.nsIWebProgress.NOTIFY_STATE_NETWORK |
-                Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT;
-
-    let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIWebNavigation)
-                          .QueryInterface(Ci.nsIDocShell);
-    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIWebProgress);
-    try {
-      webProgress.removeProgressListener(this);
-    } catch (e) {}
-    webProgress.addProgressListener(this, flags);
-  },
-
-  onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags,
-                                        aStatus) {
-    const complete = Ci.nsIWebProgressListener.STATE_IS_WINDOW +
-                     Ci.nsIWebProgressListener.STATE_STOP;
-    if ((aStateFlags & complete) == complete && this._locationHasChanged) {
-      aWebProgress.removeProgressListener(this);
-      loadDocument(aWebProgress.DOMWindow, this._documentUrl);
-    }
-  },
-
-  onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf,
-                                              aMaxSelf, aCurTotal, aMaxTotal) {
-  },
-
-  onLocationChange: function onLocationChange(aWebProgress, aRequest,
-                                              aLocationURI) {
-    this._locationHasChanged = true;
-  },
-
-  onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus,
-                                          aMessage) {
-  },
-
-  onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
-  },
-
-  QueryInterface: function QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsIWebProgressListener) ||
-        aIID.equals(Ci.nsISupportsWeakReference) ||
-        aIID.equals(Ci.nsISupports)) {
-        return this;
-    }
-
-    throw Components.results.NS_ERROR_NO_INTERFACE;
-  }
-};
-
-
+const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
 function pdfContentHandler() {
 }
 
 pdfContentHandler.prototype = {
   handleContent: function handleContent(aMimetype, aContext, aRequest) {
     if (aMimetype != PDF_CONTENT_TYPE)
-      throw Cr.NS_ERROR_WONT_HANDLE_CONTENT;
+      throw NS_ERROR_WONT_HANDLE_CONTENT;
 
     if (!(aRequest instanceof Ci.nsIChannel))
-      throw Cr.NS_ERROR_WONT_HANDLE_CONTENT;
+      throw NS_ERROR_WONT_HANDLE_CONTENT;
 
     let window = null;
-    let callbacks = aRequest.notificationCallbacks ?
-                    aRequest.notificationCallbacks :
+    let callbacks = aRequest.notificationCallbacks ||
                     aRequest.loadGroup.notificationCallbacks;
     if (!callbacks)
       return;
 
-    aRequest.cancel(Cr.NS_BINDING_ABORTED);
-    let uri = aRequest.URI;
-
     window = callbacks.getInterface(Ci.nsIDOMWindow);
-    WebProgressListener.init(window, uri.spec);
 
+    let url = null;
     try {
-      let url = Services.prefs.getCharPref('extensions.pdf.js.url');
-      url = url.replace('%s', uri.spec);
-      window.location = url;
+      url = Services.prefs.getCharPref('extensions.pdf.js.url');
     } catch (e) {
       log('Error retrieving the pdf.js base url - ' + e);
+      throw NS_ERROR_WONT_HANDLE_CONTENT;
     }
+
+    let targetUrl = aRequest.URI.spec;
+    if (targetUrl.indexOf('?pdfjs.action=download') >= 0)
+      throw NS_ERROR_WONT_HANDLE_CONTENT;
+
+    aRequest.cancel(Cr.NS_BINDING_ABORTED);
+    window.location = url.replace('%s', targetUrl);
   },
 
   classID: Components.ID('{2278dfd0-b75c-11e0-8257-1ba3d93c9f1a}'),
@@ -158,4 +62,3 @@ pdfContentHandler.prototype = {
 
 var NSGetFactory = XPCOMUtils.generateNSGetFactory([pdfContentHandler]);
 
-
index cd0e0d103dea92dd267c12fb359605ef59e145d5..a604fd970e75732b0d0ae6af8828a29434a73490 100644 (file)
--- a/fonts.js
+++ b/fonts.js
@@ -26,53 +26,53 @@ var kHintingEnabled = false;
  */
 var stdFontMap = {
   'ArialNarrow': 'Helvetica',
-  'ArialNarrow_Bold': 'Helvetica-Bold',
-  'ArialNarrow_BoldItalic': 'Helvetica-BoldOblique',
-  'ArialNarrow_Italic': 'Helvetica-Oblique',
+  'ArialNarrow-Bold': 'Helvetica-Bold',
+  'ArialNarrow-BoldItalic': 'Helvetica-BoldOblique',
+  'ArialNarrow-Italic': 'Helvetica-Oblique',
   'ArialBlack': 'Helvetica',
-  'ArialBlack_Bold': 'Helvetica-Bold',
-  'ArialBlack_BoldItalic': 'Helvetica-BoldOblique',
-  'ArialBlack_Italic': 'Helvetica-Oblique',
+  'ArialBlack-Bold': 'Helvetica-Bold',
+  'ArialBlack-BoldItalic': 'Helvetica-BoldOblique',
+  'ArialBlack-Italic': 'Helvetica-Oblique',
   'Arial': 'Helvetica',
-  'Arial_Bold': 'Helvetica-Bold',
-  'Arial_BoldItalic': 'Helvetica-BoldOblique',
-  'Arial_Italic': 'Helvetica-Oblique',
-  'Arial_BoldItalicMT': 'Helvetica-BoldOblique',
-  'Arial_BoldMT': 'Helvetica-Bold',
-  'Arial_ItalicMT': 'Helvetica-Oblique',
+  'Arial-Bold': 'Helvetica-Bold',
+  'Arial-BoldItalic': 'Helvetica-BoldOblique',
+  'Arial-Italic': 'Helvetica-Oblique',
+  'Arial-BoldItalicMT': 'Helvetica-BoldOblique',
+  'Arial-BoldMT': 'Helvetica-Bold',
+  'Arial-ItalicMT': 'Helvetica-Oblique',
   'ArialMT': 'Helvetica',
-  'Courier_Bold': 'Courier-Bold',
-  'Courier_BoldItalic': 'Courier-BoldOblique',
-  'Courier_Italic': 'Courier-Oblique',
+  'Courier-Bold': 'Courier-Bold',
+  'Courier-BoldItalic': 'Courier-BoldOblique',
+  'Courier-Italic': 'Courier-Oblique',
   'CourierNew': 'Courier',
-  'CourierNew_Bold': 'Courier-Bold',
-  'CourierNew_BoldItalic': 'Courier-BoldOblique',
-  'CourierNew_Italic': 'Courier-Oblique',
-  'CourierNewPS_BoldItalicMT': 'Courier-BoldOblique',
-  'CourierNewPS_BoldMT': 'Courier-Bold',
-  'CourierNewPS_ItalicMT': 'Courier-Oblique',
+  'CourierNew-Bold': 'Courier-Bold',
+  'CourierNew-BoldItalic': 'Courier-BoldOblique',
+  'CourierNew-Italic': 'Courier-Oblique',
+  'CourierNewPS-BoldItalicMT': 'Courier-BoldOblique',
+  'CourierNewPS-BoldMT': 'Courier-Bold',
+  'CourierNewPS-ItalicMT': 'Courier-Oblique',
   'CourierNewPSMT': 'Courier',
-  'Helvetica_Bold': 'Helvetica-Bold',
-  'Helvetica_BoldItalic': 'Helvetica-BoldOblique',
-  'Helvetica_Italic': 'Helvetica-Oblique',
-  'Symbol_Bold': 'Symbol',
-  'Symbol_BoldItalic': 'Symbol',
-  'Symbol_Italic': 'Symbol',
+  'Helvetica-Bold': 'Helvetica-Bold',
+  'Helvetica-BoldItalic': 'Helvetica-BoldOblique',
+  'Helvetica-Italic': 'Helvetica-Oblique',
+  'Symbol-Bold': 'Symbol',
+  'Symbol-BoldItalic': 'Symbol',
+  'Symbol-Italic': 'Symbol',
   'TimesNewRoman': 'Times-Roman',
-  'TimesNewRoman_Bold': 'Times-Bold',
-  'TimesNewRoman_BoldItalic': 'Times-BoldItalic',
-  'TimesNewRoman_Italic': 'Times-Italic',
+  'TimesNewRoman-Bold': 'Times-Bold',
+  'TimesNewRoman-BoldItalic': 'Times-BoldItalic',
+  'TimesNewRoman-Italic': 'Times-Italic',
   'TimesNewRomanPS': 'Times-Roman',
-  'TimesNewRomanPS_Bold': 'Times-Bold',
-  'TimesNewRomanPS_BoldItalic': 'Times-BoldItalic',
-  'TimesNewRomanPS_BoldItalicMT': 'Times-BoldItalic',
-  'TimesNewRomanPS_BoldMT': 'Times-Bold',
-  'TimesNewRomanPS_Italic': 'Times-Italic',
-  'TimesNewRomanPS_ItalicMT': 'Times-Italic',
+  'TimesNewRomanPS-Bold': 'Times-Bold',
+  'TimesNewRomanPS-BoldItalic': 'Times-BoldItalic',
+  'TimesNewRomanPS-BoldItalicMT': 'Times-BoldItalic',
+  'TimesNewRomanPS-BoldMT': 'Times-Bold',
+  'TimesNewRomanPS-Italic': 'Times-Italic',
+  'TimesNewRomanPS-ItalicMT': 'Times-Italic',
   'TimesNewRomanPSMT': 'Times-Roman',
-  'TimesNewRomanPSMT_Bold': 'Times-Bold',
-  'TimesNewRomanPSMT_BoldItalic': 'Times-BoldItalic',
-  'TimesNewRomanPSMT_Italic': 'Times-Italic'
+  'TimesNewRomanPSMT-Bold': 'Times-Bold',
+  'TimesNewRomanPSMT-BoldItalic': 'Times-BoldItalic',
+  'TimesNewRomanPSMT-Italic': 'Times-Italic'
 };
 
 var serifFonts = {
@@ -453,7 +453,9 @@ var Font = (function Font() {
     if (!file) {
       // The file data is not specified. Trying to fix the font name
       // to be used with the canvas.font.
-      var fontName = stdFontMap[name] || name.replace('_', '-');
+      var fontName = name.replace(/[,_]/g, '-');
+      fontName = stdFontMap[fontName] || fontName;
+
       this.bold = (fontName.search(/bold/gi) != -1);
       this.italic = (fontName.search(/oblique/gi) != -1) ||
                     (fontName.search(/italic/gi) != -1);
@@ -720,7 +722,13 @@ var Font = (function Font() {
   };
 
   function createOS2Table(properties, override) {
-    var override = override || {};
+    override = override || {
+      unitsPerEm: 0,
+      yMax: 0,
+      yMin: 0,
+      ascent: 0,
+      descent: 0
+    };
 
     var ulUnicodeRange1 = 0;
     var ulUnicodeRange2 = 0;
@@ -1332,7 +1340,8 @@ var Font = (function Font() {
         'OS/2': stringToArray(createOS2Table(properties)),
 
         // Character to glyphs mapping
-        'cmap': createCMapTable(charstrings.slice(), font.glyphIds),
+        'cmap': createCMapTable(charstrings.slice(),
+                                ('glyphIds' in font) ? font.glyphIds : null),
 
         // Font header
         'head': (function fontFieldsHead() {
@@ -1727,6 +1736,7 @@ var Type1Parser = function type1Parser() {
     var charstring = [];
     var lsb = 0;
     var width = 0;
+    var flexState = 0;
 
     var value = '';
     var count = array.length;
@@ -1760,7 +1770,11 @@ var Type1Parser = function type1Parser() {
               i++;
               continue;
             }
-          } else if (!kHintingEnabled && (value == 1 || value == 2)) {
+          } else if (escape == 17 || escape == 33) {
+            // pop or setcurrentpoint commands can be ignored
+            // since we are not doing callothersubr
+            continue;
+          } else if (!kHintingEnabled && (escape == 1 || escape == 2)) {
             charstring.push('drop', 'drop', 'drop', 'drop', 'drop', 'drop');
             continue;
           }
@@ -1787,6 +1801,29 @@ var Type1Parser = function type1Parser() {
 
             charstring.push(lsb, 'hmoveto');
             continue;
+          } else if (value == 10) { // callsubr
+            if (charstring[charstring.length - 1] < 3) { // subr #0..2
+              var subrNumber = charstring.pop();
+              switch (subrNumber) {
+                case 1:
+                  flexState = 1; // prepare for flex coordinates
+                  break;
+                case 2:
+                  flexState = 2; // flex in progress
+                  break;
+                case 0:
+                  // type2 flex command does not need final coords
+                  charstring.push('exch', 'drop', 'exch', 'drop');
+                  charstring.push('flex');
+                  flexState = 0;
+                  break;
+              }
+              continue;
+            }
+          } else if (value == 21 && flexState > 0) {
+            if (flexState > 1)
+              continue; // ignoring rmoveto
+            value = 5; // first segment replacing with rlineto
           } else if (!kHintingEnabled && (value == 1 || value == 3)) {
             charstring.push('drop', 'drop');
             continue;
@@ -2283,7 +2320,8 @@ CFF.prototype = {
     'return': 11,
     'sub': [12, 11],
     'div': [12, 12],
-    'pop': [1, 12, 18],
+    'exch': [12, 28],
+    'flex': [12, 35],
     'drop' : [12, 18],
     'endchar': 14,
     'rmoveto': 21,
@@ -2500,7 +2538,7 @@ var Type2CFF = (function type2CFF() {
       var charStrings = this.parseIndex(topDict.CharStrings);
       var charset = this.parseCharsets(topDict.charset,
                                        charStrings.length, strings);
-      var hasSupplement = this.parseEncoding(topDict.Encoding, properties,
+      var encoding = this.parseEncoding(topDict.Encoding, properties,
                                              strings, charset);
 
       // The font sanitizer does not support CFF encoding with a
@@ -2508,8 +2546,8 @@ var Type2CFF = (function type2CFF() {
       // between gid to glyph, let's overwrite what is declared in
       // the top dictionary to let the sanitizer think the font use
       // StandardEncoding, that's a lie but that's ok.
-      if (hasSupplement)
-        bytes[topDict.Encoding] = 0;
+      if (encoding.hasSupplement)
+        bytes[topDict.Encoding] &= 0x7F;
 
       // The CFF specification state that the 'dotsection' command
       // (12, 0) is deprecated and treated as a no-op, but all Type2
@@ -2540,7 +2578,7 @@ var Type2CFF = (function type2CFF() {
 
       // charstrings contains info about glyphs (one element per glyph
       // containing mappings for {unicode, width})
-      var charstrings = this.getCharStrings(charset, charStrings,
+      var charstrings = this.getCharStrings(charset, encoding.encoding,
                                             privateDict, this.properties);
 
       // create the mapping between charstring and glyph id
@@ -2557,49 +2595,85 @@ var Type2CFF = (function type2CFF() {
       return data;
     },
 
-    getCharStrings: function cff_charstrings(charsets, charStrings,
+    getCharStrings: function cff_charstrings(charsets, encoding,
                                              privateDict, properties) {
       var defaultWidth = privateDict['defaultWidthX'];
       var charstrings = [];
+      var firstChar = properties.firstChar;
+      var glyphMap = {};
+      for (var i = 0; i < charsets.length; i++) {
+        var glyph = charsets[i];
+        for (var charcode in encoding) {
+          if (encoding[charcode] == i)
+            glyphMap[glyph] = charcode | 0;
+        }
+      }
+
       var differences = properties.differences;
-      var index = properties.firstChar || 0;
+      for (var i = 0; i < differences.length; ++i) {
+        var glyph = differences[i];
+        if (!glyph)
+          continue;
+        var oldGlyph = charsets[i];
+        if (oldGlyph)
+          delete glyphMap[oldGlyph];
+        glyphMap[differences[i]] = i;
+      }
+
+      var glyphs = properties.glyphs;
       for (var i = 1; i < charsets.length; i++) {
-        var code = -1;
         var glyph = charsets[i];
-        for (var j = 0; j < differences.length; j++) {
-          if (differences[j] == glyph) {
-            index = j;
-            code = differences.indexOf(glyph);
-            break;
-          }
-        }
+        var code = glyphMap[glyph] || 0;
 
-        var mapping =
-          properties.glyphs[glyph] || properties.glyphs[index] || {};
-        if (code == -1)
-          index = code = mapping.unicode || index;
+        var mapping = glyphs[code] || glyphs[glyph] || { width: defaultWidth };
+        var unicode = mapping.unicode;
 
-        if (code <= 0x1f || (code >= 127 && code <= 255))
-          code += kCmapGlyphOffset;
+        if (unicode <= 0x1f || (unicode >= 127 && unicode <= 255))
+          unicode += kCmapGlyphOffset;
 
-        var width = mapping.width;
-        properties.glyphs[glyph] = properties.encoding[index] = {
-          unicode: code,
-          width: isNum(width) ? width : defaultWidth
+        var width = isNum(mapping.width) ? mapping.width : defaultWidth;
+        properties.encoding[code] = {
+          unicode: unicode,
+          width: width
         };
 
         charstrings.push({
-          unicode: code,
+          unicode: unicode,
           width: width,
+          code: code,
           gid: i
         });
-        index++;
       }
 
       // sort the array by the unicode value
       charstrings.sort(function type2CFFGetCharStringsSort(a, b) {
         return a.unicode - b.unicode;
       });
+
+      // remove duplicates -- they might appear during selection:
+      //   properties.glyphs[code] || properties.glyphs[glyph]
+      var nextUnusedUnicode = kCmapGlyphOffset + 0x0020;
+      var lastUnicode = charstrings[0].unicode, wasModified = false;
+      for (var i = 1; i < charstrings.length; ++i) {
+        if (lastUnicode != charstrings[i].unicode) {
+          lastUnicode = charstrings[i].unicode;
+          continue;
+        }
+        // duplicate found -- keeping the item that has
+        // different code and unicode, that one created
+        // as result of modification of the base encoding
+        var duplicateIndex =
+          charstrings[i].unicode == charstrings[i].code ? i : i - 1;
+        charstrings[duplicateIndex].unicode = nextUnusedUnicode++;
+        wasModified = true;
+      }
+      if (!wasModified)
+        return charstrings;
+
+      // sort the array by the unicode value (again)
+      charstrings.sort(function type2CFFGetCharStringsSort(a, b) {
+        return a.unicode - b.unicode;
+      });
       return charstrings;
     },
 
@@ -2607,6 +2681,10 @@ var Type2CFF = (function type2CFF() {
                                               charset) {
       var encoding = {};
       var bytes = this.bytes;
+      var result = {
+        encoding: encoding,
+        hasSupplement: false
+      };
 
       function readSupplement() {
         var supplementsCount = bytes[pos++];
@@ -2633,11 +2711,6 @@ var Type2CFF = (function type2CFF() {
             var glyphsCount = bytes[pos++];
             for (var i = 1; i <= glyphsCount; i++)
               encoding[bytes[pos++]] = i;
-
-            if (format & 0x80) {
-              readSupplement();
-              return true;
-            }
             break;
 
           case 1:
@@ -2649,19 +2722,18 @@ var Type2CFF = (function type2CFF() {
               for (var j = start; j <= start + count; j++)
                 encoding[j] = gid++;
             }
-
-            if (format & 0x80) {
-              readSupplement();
-              return true;
-            }
             break;
 
           default:
             error('Unknow encoding format: ' + format + ' in CFF');
             break;
         }
+        if (format & 0x80) {
+          readSupplement();
+          result.hasSupplement = true;
+        }
       }
-      return false;
+      return result;
     },
 
     parseCharsets: function cff_parsecharsets(pos, length, strings) {
@@ -2871,7 +2943,15 @@ var Type2CFF = (function type2CFF() {
         if (b <= 21) {
           if (b === 12) {
             ++pos;
-            var b = (b << 8) | dict[pos];
+            var op = dict[pos];
+            if ((op > 14 && op < 17) ||
+                (op > 23 && op < 30) || op > 38) {
+              warn('Invalid CFF dictionary key: ' + op);
+              // trying to replace it with initialRandomSeed
+              // to pass sanitizer
+              dict[pos] = 19;
+            }
+            var b = (b << 8) | op;
           }
           entries.push([b, operands]);
           operands = [];
index 9cb8eb0e61e7a2c5109351192f7b6290b6f81ced..d4d07ec0d036230411419e69d0094cf1d0c7f5f9 100644 (file)
@@ -1,6 +1,8 @@
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 
+'use strict';
+
 var Metrics = {
   'Courier': 600,
   'Courier-Bold': 600,
diff --git a/pdf.js b/pdf.js
index 54098c20271bacf2f0417042bc16a46eb0d0d623..e7db0ddea88b5e2b8773bf051e498eff5aa7d619 100644 (file)
--- a/pdf.js
+++ b/pdf.js
@@ -2002,6 +2002,25 @@ var CCITTFaxStream = (function ccittFaxStream() {
     return EOF;
   };
 
+  var findTableCode = function ccittFaxStreamFindTableCode(start, end, table,
+                                                           limit) {
+    for (var i = start; i <= end; ++i) {
+      var code = this.lookBits(i);
+      if (code == EOF)
+        return [true, 1];
+      if (i < end)
+        code <<= end - i;
+      if (code >= limit) {
+        var p = table[code - ((limit == ccittEOL) ? 0 : limit)];
+        if (p[0] == i) {
+          this.eatBits(i);
+          return [true, p[1]];
+        }
+      }
+    }
+    return [false, 0];
+  };
+
   constructor.prototype.getWhiteCode = function ccittFaxStreamGetWhiteCode() {
     var code = 0;
     var p;
@@ -2021,31 +2040,13 @@ var CCITTFaxStream = (function ccittFaxStream() {
         return p[1];
       }
     } else {
-      for (var n = 1; n <= 9; ++n) {
-        code = this.lookBits(n);
-        if (code == EOF)
-          return 1;
+      var result = findTableCode(1, 9, whiteTable2, ccittEOL);
+      if (result[0])
+        return result[1];
 
-        if (n < 9)
-          code <<= 9 - n;
-        p = whiteTable2[code];
-        if (p[0] == n) {
-          this.eatBits(n);
-          return p[0];
-        }
-      }
-      for (var n = 11; n <= 12; ++n) {
-        code = this.lookBits(n);
-        if (code == EOF)
-          return 1;
-        if (n < 12)
-          code <<= 12 - n;
-        p = whiteTable1[code];
-        if (p[0] == n) {
-          this.eatBits(n);
-          return p[1];
-        }
-      }
+      result = findTableCode(11, 12, whiteTable1, ccittEOL);
+      if (result[0])
+        return result[1];
     }
     warn('bad white code');
     this.eatBits(1);
@@ -2070,45 +2071,17 @@ var CCITTFaxStream = (function ccittFaxStream() {
         return p[1];
       }
     } else {
-      var n;
-      for (n = 2; n <= 6; ++n) {
-        code = this.lookBits(n);
-        if (code == EOF)
-          return 1;
-        if (n < 6)
-          code <<= 6 - n;
-        p = blackTable3[code];
-        if (p[0] == n) {
-          this.eatBits(n);
-          return p[1];
-        }
-      }
-      for (n = 7; n <= 12; ++n) {
-        code = this.lookBits(n);
-        if (code == EOF)
-          return 1;
-        if (n < 12)
-          code <<= 12 - n;
-        if (code >= 64) {
-          p = blackTable2[code - 64];
-          if (p[0] == n) {
-            this.eatBits(n);
-            return p[1];
-          }
-        }
-      }
-      for (n = 10; n <= 13; ++n) {
-        code = this.lookBits(n);
-        if (code == EOF)
-          return 1;
-        if (n < 13)
-          code <<= 13 - n;
-        p = blackTable1[code];
-        if (p[0] == n) {
-          this.eatBits(n);
-          return p[1];
-        }
-      }
+      var result = findTableCode(2, 6, blackTable3, ccittEOL);
+      if (result[0])
+        return result[1];
+
+      result = findTableCode(7, 12, blackTable2, 64);
+      if (result[0])
+        return result[1];
+
+      result = findTableCode(10, 13, blackTable1, ccittEOL);
+      if (result[0])
+        return result[1];
     }
     warn('bad black code');
     this.eatBits(1);
@@ -3636,8 +3609,8 @@ var Page = (function pagePage() {
       }
       next();
     },
-    rotatePoint: function pageRotatePoint(x, y) {
-      var rotate = this.rotate;
+    rotatePoint: function pageRotatePoint(x, y, reverse) {
+      var rotate = reverse ? (360 - this.rotate) : this.rotate;
       switch (rotate) {
         case 180:
           return {x: this.width - x, y: y};
@@ -3645,6 +3618,7 @@ var Page = (function pagePage() {
           return {x: this.width - y, y: this.height - x};
         case 270:
           return {x: y, y: x};
+        case 360:
         case 0:
         default:
           return {x: x, y: this.height - y};
@@ -4698,8 +4672,12 @@ var PartialEvaluator = (function partialEvaluator() {
       var xobjs = xref.fetchIfRef(resources.get('XObject')) || new Dict();
       var patterns = xref.fetchIfRef(resources.get('Pattern')) || new Dict();
       var parser = new Parser(new Lexer(stream), false);
-      var args = [], obj;
       var res = resources;
+      var args = [], argsArray = [], fnArray = [], obj;
+      var getObjBt = function getObjBt() {
+        parser = this.oldParser;
+        return { name: 'BT' };
+      };
 
       while (!isEOF(obj = parser.getObj())) {
         if (isCmd(obj)) {
@@ -4711,10 +4689,7 @@ var PartialEvaluator = (function partialEvaluator() {
               fn = OP_MAP[cmd.substr(0, cmd.length - 2)];
               // feeding 'BT' on next interation
               parser = {
-                getObj: function() {
-                  parser = this.oldParser;
-                  return { name: 'BT' };
-                },
+                getObj: getObjBt,
                 oldParser: parser
               };
             }
@@ -5042,7 +5017,9 @@ var PartialEvaluator = (function partialEvaluator() {
         };
 
         if (replaceGlyph || !glyphs[glyph])
-            glyphs[glyph] = map[i];
+          glyphs[glyph] = map[i];
+        if (replaceGlyph || !glyphs[index])
+          glyphs[index] = map[i];
 
         // If there is no file, the character mapping can't be modified
         // but this is unlikely that there is any standard encoding with
@@ -5216,7 +5193,7 @@ var PartialEvaluator = (function partialEvaluator() {
             return null;
 
           // Using base font name as a font name.
-          baseFontName = baseFontName.name.replace(/,/g, '_');
+          baseFontName = baseFontName.name.replace(/[,_]/g, '-');
           var metricsAndMap = this.getBaseFontMetricsAndMap(baseFontName);
 
           var properties = {
@@ -5593,7 +5570,8 @@ var CanvasGraphics = (function canvasGraphics() {
     stroke: function canvasGraphicsStroke() {
       var ctx = this.ctx;
       var strokeColor = this.current.strokeColor;
-      if (strokeColor && strokeColor.type === 'Pattern') {
+      if (strokeColor && strokeColor.hasOwnProperty('type') &&
+          strokeColor.type === 'Pattern') {
         // for patterns, we transform to pattern space, calculate
         // the pattern, call stroke, and restore to user space
         ctx.save();
@@ -5614,7 +5592,8 @@ var CanvasGraphics = (function canvasGraphics() {
       var ctx = this.ctx;
       var fillColor = this.current.fillColor;
 
-      if (fillColor && fillColor.type === 'Pattern') {
+      if (fillColor && fillColor.hasOwnProperty('type') &&
+          fillColor.type === 'Pattern') {
         ctx.save();
         ctx.fillStyle = fillColor.getPattern(ctx);
         ctx.fill();
@@ -5634,7 +5613,8 @@ var CanvasGraphics = (function canvasGraphics() {
       var ctx = this.ctx;
 
       var fillColor = this.current.fillColor;
-      if (fillColor && fillColor.type === 'Pattern') {
+      if (fillColor && fillColor.hasOwnProperty('type') &&
+          fillColor.type === 'Pattern') {
         ctx.save();
         ctx.fillStyle = fillColor.getPattern(ctx);
         ctx.fill();
@@ -5644,7 +5624,8 @@ var CanvasGraphics = (function canvasGraphics() {
       }
 
       var strokeColor = this.current.strokeColor;
-      if (strokeColor && strokeColor.type === 'Pattern') {
+      if (strokeColor && strokeColor.hasOwnProperty('type') &&
+          strokeColor.type === 'Pattern') {
         ctx.save();
         ctx.strokeStyle = strokeColor.getPattern(ctx);
         ctx.stroke();
index fddb4e124e8d2ff7053a80bbea57300cce3851f1..5418b3eb98af54eca5fb02fdf4ea1281ebe8ace1 100644 (file)
@@ -71,7 +71,8 @@ function nextTask() {
   cleanup();
 
   if (currentTaskIdx == manifest.length) {
-    return done();
+    done();
+    return;
   }
   var task = manifest[currentTaskIdx];
   task.round = 0;
@@ -91,7 +92,11 @@ function nextTask() {
 }
 
 function isLastPage(task) {
-  return task.pageNum > task.pdfDoc.numPages || task.pageNum > task.pageLimit;
+  var limit = task.pageLimit || 0;
+  if (!limit || limit > task.pdfDoc.numPages)
+   limit = task.pdfDoc.numPages;
+
+  return task.pageNum > limit;
 }
 
 function canvasToDataURL() {
index 711c45147452e9cf5b1de49cec6535d547df6b20..3392d9a66e44075ad02f09e6dabfd998bbe7ef44 100644 (file)
@@ -36,8 +36,9 @@ endobj
 /Length 8 0 R
 >>
 stream\r
+ /F0 12 Tf
+/F1 12 Tf
 /GS1 gs
-/F0 12 Tf
 BT
 100 700 Td
 (I should be courier!) Tj
@@ -56,10 +57,11 @@ endobj
 7 0 obj
 <<
 /F0 10 0 R
+/F1 11 0 R
 >>
 endobj
 8 0 obj
-82
+93
 endobj
 9 0 obj
 <<
@@ -81,25 +83,34 @@ endobj
 /Encoding /WinAnsiEncoding
 >>
 endobj
+11 0 obj
+<<
+/Type /Font
+/Subtype /Type1
+/BaseFont /Times-Italic
+/Encoding /WinAnsiEncoding
+>>
+endobj
 xref
-0 11
+0 12
 0000000000 65535 f\r
 0000000015 00000 n\r
 0000000078 00000 n\r
 0000000135 00000 n\r
 0000000239 00000 n\r
 0000000304 00000 n\r
-0000000441 00000 n\r
-0000000473 00000 n\r
-0000000505 00000 n\r
-0000000523 00000 n\r
-0000000653 00000 n\r
+0000000452 00000 n\r
+0000000484 00000 n\r
+0000000527 00000 n\r
+0000000545 00000 n\r
+0000000675 00000 n\r
+0000000771 00000 n\r
 trailer
 <<
 /Root 1 0 R
-/ID [<BFFF29B7D1C75EC69AC080682C2AFC5B> <BFFF29B7D1C75EC69AC080682C2AFC5B>]
-/Size 11
+/ID [<FCE2529ACCE848A953BDB5D497D71C36> <FCE2529ACCE848A953BDB5D497D71C36>]
+/Size 12
 >>
 startxref
-749
+872
 %%EOF
index 43b799badc25dd82e8b5db97957697478b8e8722..d7ac34cef1a20c50c0759bb62cecd6100e82d67f 100644 (file)
     {  "id": "rotation",
        "file": "pdfs/rotation.pdf",
        "rounds": 1,
-       "type": "load"
+       "type": "eq"
     },
     {  "id": "ecma262-pdf",
        "file": "pdfs/ecma262.pdf",
        "rounds": 1,
        "type": "eq"
     },
+    {  "id": "fit11-talk",
+       "file": "pdfs/fit11-talk.pdf",
+       "link": true,
+       "rounds": 1,
+       "skipPages": [12,31],
+       "type": "eq"
+    },
     {  "id": "fips197",
        "file": "pdfs/fips197.pdf",
        "link": true,
        "rounds": 1,
-       "type": "load"
+       "type": "eq"
     },
     {  "id": "txt2pdf",
        "file": "pdfs/txt2pdf.pdf",
        "file": "pdfs/extgstate.pdf",
        "link": false,
        "rounds": 1,
-       "type": "load"
+       "type": "eq"
     },
     {  "id": "usmanm-bad",
        "file": "pdfs/usmanm-bad.pdf",
        "rounds": 1,
        "type": "eq"
     },
+    {  "id": "pal-o47",
+       "file": "pdfs/pal-o47.pdf",
+       "link": true,
+       "rounds": 1,
+       "type": "eq"
+    },
     {  "id": "simpletype3font",
        "file": "pdfs/simpletype3font.pdf",
        "link": false,
index 36df0e2a5d631a6b419256b5caf4b4615019b854..ad4c8f8a997e3ee5a215d847f20f9702fea36ca9 100644 (file)
 
 // IE9 text/html data URI
 (function checkDocumentDocumentModeCompatibility() {
-  if (document.documentMode !== 9)
+  if (!('documentMode' in document) || document.documentMode !== 9)
     return;
   // overriding the src property
   var originalSrcDescriptor = Object.getOwnPropertyDescriptor(
index e72bdc2861ceefc961d6debd7a82c17f9c727ee0..040b87bdf8ee01c3cab542b4b879065620353563 100644 (file)
@@ -8,6 +8,10 @@ body {
   padding: 0px;
 }
 
+[hidden] {
+  display: none;
+}
+
 /* === Toolbar === */
 #controls {
   background-color: #eee;
@@ -34,6 +38,10 @@ body {
   margin: 4px;
 }
 
+#controls > a > img {
+  margin: 2px;
+}
+
 #controls > button {
   line-height: 32px;
 }
index beab965df629b606db43db048968c5409fe1dd98..e79539359715453a0d158c14c42fbb080ede151c 100644 (file)
 
   <body>
     <div id="controls">
-      <button id="previous" onclick="PDFView.page--;">
+      <button id="previous" onclick="PDFView.page--;" oncontextmenu="return false;">
         <img src="images/go-up.svg" align="top" height="32"/>
         Previous
       </button>
 
-      <button id="next" onclick="PDFView.page++;">
+      <button id="next" onclick="PDFView.page++;" oncontextmenu="return false;">
         <img src="images/go-down.svg" align="top" height="32"/>
         Next
       </button>
 
       <div class="separator"></div>
 
-      <button id="next" title="Zoom Out" onclick="PDFView.zoomOut();">
+      <button id="zoomOut" title="Zoom Out" onclick="PDFView.zoomOut();" oncontextmenu="return false;">
         <img src="images/zoom-out.svg" align="top" height="32"/>
       </button>
-      <button id="next" title="Zoom In" onclick="PDFView.zoomIn();">
+      <button id="zoomIn" title="Zoom In" onclick="PDFView.zoomIn();" oncontextmenu="return false;">
         <img src="images/zoom-in.svg" align="top" height="32"/>
       </button>
 
       <div class="separator"></div>
 
-      <select id="scaleSelect" onchange="PDFView.parseScale(this.value);">
+      <select id="scaleSelect" onchange="PDFView.parseScale(this.value);" oncontextmenu="return false;">
         <option id="customScaleOption" value="custom"></option>
         <option value="0.5">50%</option>
         <option value="0.75">75%</option>
 
       <div class="separator"></div>
 
-      <button id="print" onclick="window.print();">
+      <button id="print" onclick="window.print();" oncontextmenu="return false;">
         <img src="images/document-print.svg" align="top" height="32"/>
         Print
       </button>
 
+      <button id="download" title="Download" onclick="PDFView.download();" oncontextmenu="return false;">
+        <img src="images/download.svg" align="top" height="32"/>
+        Download
+      </button>
+
       <div class="separator"></div>
 
-      <input id="fileInput" type="file"/>
+      <input id="fileInput" type="file" oncontextmenu="return false;"/>
 
       <div class="separator"></div>
 
+      <a href="#" id="viewBookmark" title="Current View (bookmark or copy the location)">
+        <img src="images/bookmark.svg" alt="Bookmark" align="top" height="32"/>
+      </a>
+
       <span id="info">--</span>
     </div>
 
@@ -81,7 +90,7 @@
         <div id="sidebarScrollView">
           <div id="sidebarView"></div>
         </div>
-        <div id="outlineScrollView" style="display:none">
+        <div id="outlineScrollView" hidden='true'>
           <div id="outlineView"></div>
         </div>
         <div id="sidebarControls">
index 93f6acd315051573e1faa2e7a612aba64ee7cf57..12d0a3f37dd9c08df38b013cfe4533a8bd851ac6 100644 (file)
@@ -89,16 +89,23 @@ var PDFView = {
     var pages = this.pages;
     var input = document.getElementById('pageNumber');
     if (!(0 < val && val <= pages.length)) {
-      input.value = this.page;
+      var event = document.createEvent('UIEvents');
+      event.initUIEvent('pagechange', false, false, window, 0);
+      event.pageNumber = this.page;
+      window.dispatchEvent(event);
       return;
     }
 
     currentPageNumber = val;
-    document.getElementById('previous').disabled = (val == 1);
-    document.getElementById('next').disabled = (val == pages.length);
-    if (input.value != val) {
-      input.value = val;
-    }
+    var event = document.createEvent('UIEvents');
+    event.initUIEvent('pagechange', false, false, window, 0);
+    event.pageNumber = val;
+    window.dispatchEvent(event);
+
+    // checking if the this.page was called from the updateViewarea function:
+    // avoiding the creation of two "set page" method (internal and public)
+    if (updateViewarea.inProgress)
+      return;
 
     pages[val - 1].scrollIntoView();
   },
@@ -108,10 +115,7 @@ var PDFView = {
   },
 
   open: function pdfViewOpen(url, scale) {
-    if (url.indexOf('http') == 0)
-      return;
-
-    document.title = url;
+    document.title = this.url = url;
 
     getPdf(
       {
@@ -127,6 +131,10 @@ var PDFView = {
       });
   },
 
+  download: function pdfViewDownload() {
+    window.open(this.url + '?pdfjs.action=download', '_parent');
+  },
+
   navigateTo: function pdfViewNavigateTo(dest) {
     if (typeof dest === 'string')
       dest = this.destinations[dest];
@@ -147,12 +155,20 @@ var PDFView = {
     if (typeof dest === 'string')
       return '#' + escape(dest);
     if (dest instanceof Array) {
-      var destRef = dest[0]; // see nevigateTo method for dest format
+      var destRef = dest[0]; // see navigateTo method for dest format
       var pageNumber = destRef instanceof Object ?
         this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
         (destRef + 1);
       if (pageNumber) {
-        return '#page=' + pageNumber + '&dest=' + dest.slice(1).join(',');
+        var pdfOpenParams = '#page=' + pageNumber;
+        if (isName(dest[1], 'XYZ')) {
+          var scale = (dest[4] || this.currentScale);
+          pdfOpenParams += '&zoom=' + (scale * 100);
+          if (dest[2] || dest[3]) {
+            pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0);
+          }
+        }
+        return pdfOpenParams;
       }
     }
     return '';
@@ -171,14 +187,16 @@ var PDFView = {
 
   load: function pdfViewLoad(data, scale) {
     var loadingIndicator = document.getElementById('loading');
-    loadingIndicator.style.display = 'none';
+    loadingIndicator.setAttribute('hidden', 'true');
 
     var sidebar = document.getElementById('sidebarView');
     sidebar.parentNode.scrollTop = 0;
 
     while (sidebar.hasChildNodes())
       sidebar.removeChild(sidebar.lastChild);
-    clearInterval(sidebar._loadingInterval);
+
+    if ('_loadingInterval' in sidebar)
+      clearInterval(sidebar._loadingInterval);
 
     var container = document.getElementById('viewer');
     while (container.hasChildNodes())
@@ -226,10 +244,32 @@ var PDFView = {
       return;
 
     if (hash.indexOf('=') >= 0) {
-      // TODO more complex hashes, for now catching page=XX only
-      var m = /\bpage=(\d+)/.exec(hash);
-      if (m && m[1] > 0)
-        this.page = m[1];
+      // parsing query string
+      var paramsPairs = hash.split('&');
+      var params = {};
+      for (var i = 0; i < paramsPairs.length; ++i) {
+        var paramPair = paramsPairs[i].split('=');
+        params[paramPair[0]] = paramPair[1];
+      }
+      // borrowing syntax from "Parameters for Opening PDF Files"
+      if ('nameddest' in params) {
+        PDFView.navigateTo(params.nameddest);
+        return;
+      }
+      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
+          var dest = [null, new Name('XYZ'), (zoomArgs[1] | 0),
+            (zoomArgs[2] | 0), (zoomArgs[0] | 0) / 100];
+          var currentPage = this.pages[pageNumber - 1];
+          currentPage.scrollIntoView(dest);
+        } else
+          this.page = page; // simple page
+        return;
+      }
     } else if (/^\d+$/.test(hash)) // page number
       this.page = hash;
     else // named destination
@@ -243,14 +283,14 @@ var PDFView = {
     var outlineSwitchButton = document.getElementById('outlineSwitch');
     switch (view) {
       case 'thumbs':
-        thumbsScrollView.style.display = 'block';
-        outlineScrollView.style.display = 'none';
+        thumbsScrollView.removeAttribute('hidden');
+        outlineScrollView.setAttribute('hidden', 'true');
         thumbsSwitchButton.setAttribute('data-selected', true);
         outlineSwitchButton.removeAttribute('data-selected');
         break;
       case 'outline':
-        thumbsScrollView.style.display = 'none';
-        outlineScrollView.style.display = 'block';
+        thumbsScrollView.setAttribute('hidden', 'true');
+        outlineScrollView.removeAttribute('hidden');
         thumbsSwitchButton.removeAttribute('data-selected');
         outlineSwitchButton.setAttribute('data-selected', true);
         break;
@@ -339,6 +379,11 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
     }
   }
 
+  this.getPagePoint = function pageViewGetPagePoint(x, y) {
+    var scale = PDFView.currentScale;
+    return this.content.rotatePoint(x / scale, y / scale);
+  };
+
   this.scrollIntoView = function pageViewScrollIntoView(dest) {
       if (!dest) {
         div.scrollIntoView(true);
@@ -388,7 +433,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
         this.content.rotatePoint(x + width, y + height)
       ];
 
-      if (scale)
+      if (scale && scale !== PDFView.currentScale)
         PDFView.setScale(scale, true);
 
       setTimeout(function pageViewScrollIntoViewRelayout() {
@@ -544,26 +589,15 @@ window.addEventListener('load', function webViewerLoad(evt) {
     params[unescape(param[0])] = unescape(param[1]);
   }
 
-  PDFView.open(params.file || kDefaultURL, parseFloat(params.scale));
+  var scale = ('scale' in params) ? params.scale : kDefaultScale;
+  PDFView.open(params.file || kDefaultURL, parseFloat(scale));
 
   if (!window.File || !window.FileReader || !window.FileList || !window.Blob)
-    document.getElementById('fileInput').style.display = 'none';
+    document.getElementById('fileInput').setAttribute('hidden', 'true');
   else
     document.getElementById('fileInput').value = null;
 }, true);
 
-window.addEventListener('pdfload', function webViewerPdfload(evt) {
-  PDFView.load(evt.detail);
-}, true);
-
-window.addEventListener('pdfprogress', function webViewerPdfProgress(evt) {
-  PDFView.progress(evt.detail);
-}, true);
-
-window.addEventListener('pdferror', function webViewerPdfError(evt) {
-  PDFView.error();
-}, true);
-
 function updateViewarea() {
   var visiblePages = PDFView.getVisiblePages();
   for (var i = 0; i < visiblePages.length; i++) {
@@ -575,13 +609,21 @@ function updateViewarea() {
   if (!visiblePages.length)
     return;
 
+  updateViewarea.inProgress = true; // used in "set page"
   var currentId = PDFView.page;
   var firstPage = visiblePages[0];
-  var lastPage = visiblePages[visiblePages.length - 1];
-  if (currentId > lastPage.id && lastPage.y > window.pageYOffset)
-    PDFView.page = lastPage.id;
-  else if (currentId < firstPage.id)
-    PDFView.page = firstPage.id;
+  PDFView.page = firstPage.id;
+  updateViewarea.inProgress = false;
+
+  var kViewerTopMargin = 52;
+  var pageNumber = firstPage.id;
+  var pdfOpenParams = '#page=' + pageNumber;
+  pdfOpenParams += '&zoom=' + Math.round(PDFView.currentScale * 100);
+  var currentPage = PDFView.pages[pageNumber - 1];
+  var topLeft = currentPage.getPagePoint(window.pageXOffset,
+    window.pageYOffset - firstPage.y - kViewerTopMargin);
+  pdfOpenParams += ',' + Math.round(topLeft.x) + ',' + Math.round(topLeft.y);
+  document.getElementById('viewBookmark').href = pdfOpenParams;
 }
 
 window.addEventListener('scroll', function webViewerScroll(evt) {
@@ -622,6 +664,10 @@ window.addEventListener('change', function webViewerChange(evt) {
   fileReader.readAsBinaryString(file);
 
   document.title = file.name;
+
+  // URL does not reflect proper document location - hiding some icons.
+  document.getElementById('viewBookmark').setAttribute('hidden', 'true');
+  document.getElementById('download').setAttribute('hidden', 'true');
 }, true);
 
 window.addEventListener('transitionend', function webViewerTransitionend(evt) {
@@ -672,10 +718,11 @@ window.addEventListener('scalechange', function scalechange(evt) {
 }, true);
 
 window.addEventListener('pagechange', function pagechange(evt) {
-  var page = evt.detail;
-  document.getElementById('pageNumber').value = page;
-  document.getElementById('previous').disabled = (page == 1);
-  document.getElementById('next').disabled = (page == PDFView.pages.length);
+  var page = evt.pageNumber;
+  if (document.getElementById('pageNumber').value != page)
+    document.getElementById('pageNumber').value = page;
+  document.getElementById('previous').disabled = (page <= 1);
+  document.getElementById('next').disabled = (page >= PDFView.pages.length);
 }, true);
 
 window.addEventListener('keydown', function keydown(evt) {