From 65ea07a3a264f5620049d24f0969b05639adc0ce Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 19 Oct 2011 11:14:13 -0700 Subject: [PATCH] Initial merge of master. --- Makefile | 18 +- charsets.js | 4 + .../firefox/components/pdfContentHandler.js | 125 +-------- fonts.js | 246 ++++++++++++------ metrics.js | 2 + pdf.js | 133 ++++------ test/driver.js | 9 +- test/pdfs/extgstate.pdf | 33 ++- test/test_manifest.json | 19 +- web/compatibility.js | 2 +- web/viewer.css | 8 + web/viewer.html | 29 ++- web/viewer.js | 139 ++++++---- 13 files changed, 419 insertions(+), 348 deletions(-) diff --git a/Makefile b/Makefile index a9cd2793f..ebffb374c 100644 --- 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 \ diff --git a/charsets.js b/charsets.js index 59fcdf5cf..7f54ab327 100644 --- a/charsets.js +++ b/charsets.js @@ -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', diff --git a/extensions/firefox/components/pdfContentHandler.js b/extensions/firefox/components/pdfContentHandler.js index 879924047..7746e41b6 100644 --- a/extensions/firefox/components/pdfContentHandler.js +++ b/extensions/firefox/components/pdfContentHandler.js @@ -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]); - diff --git a/fonts.js b/fonts.js index cd0e0d103..a604fd970 100644 --- 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 differences = properties.differences; - var index = properties.firstChar || 0; - for (var i = 1; i < charsets.length; i++) { - var code = -1; + var firstChar = properties.firstChar; + var glyphMap = {}; + for (var i = 0; i < charsets.length; i++) { var glyph = charsets[i]; - for (var j = 0; j < differences.length; j++) { - if (differences[j] == glyph) { - index = j; - code = differences.indexOf(glyph); - break; - } + for (var charcode in encoding) { + if (encoding[charcode] == i) + glyphMap[glyph] = charcode | 0; } + } - var mapping = - properties.glyphs[glyph] || properties.glyphs[index] || {}; - if (code == -1) - index = code = mapping.unicode || index; + var differences = properties.differences; + 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; + } - if (code <= 0x1f || (code >= 127 && code <= 255)) - code += kCmapGlyphOffset; + var glyphs = properties.glyphs; + for (var i = 1; i < charsets.length; i++) { + var glyph = charsets[i]; + var code = glyphMap[glyph] || 0; - var width = mapping.width; - properties.glyphs[glyph] = properties.encoding[index] = { - unicode: code, - width: isNum(width) ? width : defaultWidth + var mapping = glyphs[code] || glyphs[glyph] || { width: defaultWidth }; + var unicode = mapping.unicode; + + if (unicode <= 0x1f || (unicode >= 127 && unicode <= 255)) + unicode += kCmapGlyphOffset; + + 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 = []; diff --git a/metrics.js b/metrics.js index 9cb8eb0e6..d4d07ec0d 100644 --- a/metrics.js +++ b/metrics.js @@ -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 54098c202..e7db0ddea 100644 --- 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(); diff --git a/test/driver.js b/test/driver.js index fddb4e124..5418b3eb9 100644 --- a/test/driver.js +++ b/test/driver.js @@ -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() { diff --git a/test/pdfs/extgstate.pdf b/test/pdfs/extgstate.pdf index 711c45147..3392d9a66 100644 --- a/test/pdfs/extgstate.pdf +++ b/test/pdfs/extgstate.pdf @@ -36,8 +36,9 @@ endobj /Length 8 0 R >> stream + /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 0000000015 00000 n 0000000078 00000 n 0000000135 00000 n 0000000239 00000 n 0000000304 00000 n -0000000441 00000 n -0000000473 00000 n -0000000505 00000 n -0000000523 00000 n -0000000653 00000 n +0000000452 00000 n +0000000484 00000 n +0000000527 00000 n +0000000545 00000 n +0000000675 00000 n +0000000771 00000 n trailer << /Root 1 0 R -/ID [ ] -/Size 11 +/ID [ ] +/Size 12 >> startxref -749 +872 %%EOF diff --git a/test/test_manifest.json b/test/test_manifest.json index 43b799bad..d7ac34cef 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -100,7 +100,7 @@ { "id": "rotation", "file": "pdfs/rotation.pdf", "rounds": 1, - "type": "load" + "type": "eq" }, { "id": "ecma262-pdf", "file": "pdfs/ecma262.pdf", @@ -144,11 +144,18 @@ "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", @@ -172,7 +179,7 @@ "file": "pdfs/extgstate.pdf", "link": false, "rounds": 1, - "type": "load" + "type": "eq" }, { "id": "usmanm-bad", "file": "pdfs/usmanm-bad.pdf", @@ -199,6 +206,12 @@ "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, diff --git a/web/compatibility.js b/web/compatibility.js index 36df0e2a5..ad4c8f8a9 100644 --- a/web/compatibility.js +++ b/web/compatibility.js @@ -163,7 +163,7 @@ // 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( diff --git a/web/viewer.css b/web/viewer.css index e72bdc286..040b87bdf 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -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; } diff --git a/web/viewer.html b/web/viewer.html index beab965df..e79539359 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -20,12 +20,12 @@
- - @@ -39,16 +39,16 @@
- -
- @@ -62,17 +62,26 @@
- -
- - +
+ + +
+ + + Bookmark + + --
@@ -81,7 +90,7 @@
-