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
# 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
#
fi;
@mkdir -p $(GH_PAGES)/web;
@mkdir -p $(GH_PAGES)/web/images;
+ @mkdir -p $(GH_PAGES)/$(EXTENSION_SRC);
$(GH_PAGES)/%.js: %.js
@cp $< $@
$(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
# 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 \
+/* -*- 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',
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}'),
var NSGetFactory = XPCOMUtils.generateNSGetFactory([pdfContentHandler]);
-
*/
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 = {
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);
};
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;
'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() {
var charstring = [];
var lsb = 0;
var width = 0;
+ var flexState = 0;
var value = '';
var count = array.length;
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;
}
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;
'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,
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
// 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
// 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
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;
},
charset) {
var encoding = {};
var bytes = this.bytes;
+ var result = {
+ encoding: encoding,
+ hasSupplement: false
+ };
function readSupplement() {
var supplementsCount = bytes[pos++];
var glyphsCount = bytes[pos++];
for (var i = 1; i <= glyphsCount; i++)
encoding[bytes[pos++]] = i;
-
- if (format & 0x80) {
- readSupplement();
- return true;
- }
break;
case 1:
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) {
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 = [];
/* -*- 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,
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;
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);
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);
}
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};
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};
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)) {
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
};
}
};
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
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 = {
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();
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();
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();
}
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();
cleanup();
if (currentTaskIdx == manifest.length) {
- return done();
+ done();
+ return;
}
var task = manifest[currentTaskIdx];
task.round = 0;
}
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() {
/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
7 0 obj
<<
/F0 10 0 R
+/F1 11 0 R
>>
endobj
8 0 obj
-82
+93
endobj
9 0 obj
<<
/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
{ "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,
// 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(
padding: 0px;
}
+[hidden] {
+ display: none;
+}
+
/* === Toolbar === */
#controls {
background-color: #eee;
margin: 4px;
}
+#controls > a > img {
+ margin: 2px;
+}
+
#controls > button {
line-height: 32px;
}
<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>
<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">
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();
},
},
open: function pdfViewOpen(url, scale) {
- if (url.indexOf('http') == 0)
- return;
-
- document.title = url;
+ document.title = this.url = url;
getPdf(
{
});
},
+ download: function pdfViewDownload() {
+ window.open(this.url + '?pdfjs.action=download', '_parent');
+ },
+
navigateTo: function pdfViewNavigateTo(dest) {
if (typeof dest === 'string')
dest = this.destinations[dest];
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 '';
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())
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
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;
}
}
+ 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);
this.content.rotatePoint(x + width, y + height)
];
- if (scale)
+ if (scale && scale !== PDFView.currentScale)
PDFView.setScale(scale, true);
setTimeout(function pageViewScrollIntoViewRelayout() {
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++) {
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) {
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) {
}, 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) {