diff --git a/README b/README
deleted file mode 100644
index ee537f0a5..000000000
--- a/README
+++ /dev/null
@@ -1,12 +0,0 @@
-pdf.js is a technology demonstrator prototype to explore whether the HTML5
-platform is complete enough to faithfully and efficiently render the ISO
-32000-1:2008 Portable Document Format (PDF) without native code assistance.
-
-You can read more about pdf.js here:
-
-http://andreasgal.com/2011/06/15/pdf-js/
-http://blog.mozilla.com/cjones/2011/06/15/overview-of-pdf-js-guts/
-
-Or follow us on twitter: @pdfjs
-
-http://twitter.com/#!/pdfjs
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..b6ff6c19f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,27 @@
+# pdf.js
+
+pdf.js is a technology demonstrator prototype to explore whether the HTML5
+platform is complete enough to faithfully and efficiently render the ISO
+32000-1:2008 Portable Document Format (PDF) without native code assistance.
+
+pdf.js is not currently part of the Mozilla project, and there is no plan
+yet to integrate it into Firefox. We will explore that possibility once
+pdf.js is production ready. Until then we aim to publish a Firefox
+PDF reader extension powered by pdf.js.
+
+You can read more about pdf.js here:
+
+ http://andreasgal.com/2011/06/15/pdf-js/
+ http://blog.mozilla.com/cjones/2011/06/15/overview-of-pdf-js-guts/
+
+follow us on twitter: @pdfjs
+
+ http://twitter.com/#!/pdfjs
+
+join our mailing list:
+
+ dev-pdf-js@lists.mozilla.org
+
+and talk to us on IRC:
+
+ #pdfjs on irc.mozilla.org
diff --git a/crypto.js b/crypto.js
index 14cc21902..e888d0212 100644
--- a/crypto.js
+++ b/crypto.js
@@ -1,5 +1,5 @@
-/* -*- Mode: Java; tab-width: s; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
-/* vim: set shiftwidth=s tabstop=2 autoindent cindent expandtab: */
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
"use strict";
@@ -45,12 +45,12 @@ var ARCFourCipher = (function() {
})();
var md5 = (function() {
- const r = new Uint8Array([
+ var r = new Uint8Array([
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]);
- const k = new Int32Array([
+ var k = new Int32Array([
-680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426,
-1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162,
1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632,
@@ -149,7 +149,7 @@ var CipherTransform = (function() {
var CipherTransformFactory = (function() {
function prepareKeyData(fileId, password, ownerPassword, userPassword, flags, revision, keyLength) {
- const defaultPasswordBytes = new Uint8Array([
+ var defaultPasswordBytes = new Uint8Array([
0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]);
var hashData = new Uint8Array(88), i = 0, j, n;
diff --git a/fonts.js b/fonts.js
index 728bc5c68..9782fc9a1 100644
--- a/fonts.js
+++ b/fonts.js
@@ -3,6 +3,8 @@
"use strict";
+var isWorker = (typeof window == "undefined");
+
/**
* Maximum file size of the font.
*/
@@ -26,69 +28,109 @@ var fontName = "";
*/
var kDisableFonts = false;
+
/**
* Hold a map of decoded fonts and of the standard fourteen Type1 fonts and
* their acronyms.
* TODO Add the standard fourteen Type1 fonts list by default
* http://cgit.freedesktop.org/poppler/poppler/tree/poppler/GfxFont.cc#n65
*/
-var Fonts = {
- _active: null,
- get active() {
- return this._active;
- },
+var Fonts = (function () {
+ var kScalePrecision = 40;
+ var fonts = Object.create(null);
- set active(name) {
- this._active = this[name];
- },
-
- charsToUnicode: function fonts_chars2Unicode(chars) {
- var active = this._active;
- if (!active)
- return chars;
-
- // if we translated this string before, just grab it from the cache
- var str = active.cache[chars];
- if (str)
- return str;
-
- // translate the string using the font's encoding
- var encoding = active.properties.encoding;
- if (!encoding)
- return chars;
-
- str = "";
- for (var i = 0; i < chars.length; ++i) {
- var charcode = chars.charCodeAt(i);
- var unicode = encoding[charcode];
-
- // Check if the glyph has already been converted
- if (unicode instanceof Name)
- unicode = encoding[unicode] = GlyphsUnicode[unicode.name];
-
- // Handle surrogate pairs
- if (unicode > 0xFFFF) {
- str += String.fromCharCode(unicode & 0xFFFF);
- unicode >>= 16;
- }
- str += String.fromCharCode(unicode);
- }
-
- // Enter the translated string into the cache
- return active.cache[chars] = str;
+ if (!isWorker) {
+ var ctx = document.createElement("canvas").getContext("2d");
+ ctx.scale(1 / kScalePrecision, 1);
}
-};
+
+ function Font(name, data, properties) {
+ this.name = name;
+ this.data = data;
+ this.properties = properties;
+ this.loading = true;
+ this.charsCache = Object.create(null);
+ this.sizes = [];
+ }
+
+ var current;
+ var charsCache;
+ var measureCache;
+
+ return {
+ registerFont: function fonts_registerFont(fontName, data, properties) {
+ fonts[fontName] = new Font(fontName, data, properties);
+ },
+ blacklistFont: function fonts_blacklistFont(fontName) {
+ registerFont(fontName, null, {});
+ markLoaded(fontName);
+ },
+ lookup: function fonts_lookup(fontName) {
+ return fonts[fontName];
+ },
+ setActive: function fonts_setActive(fontName, size) {
+ current = fonts[fontName];
+ charsCache = current.charsCache;
+ var sizes = current.sizes;
+ if (!(measureCache = sizes[size]))
+ measureCache = sizes[size] = Object.create(null);
+ ctx.font = (size * kScalePrecision) + 'px "' + fontName + '"';
+ },
+ charsToUnicode: function fonts_chars2Unicode(chars) {
+ if (!charsCache)
+ return chars;
+
+ // if we translated this string before, just grab it from the cache
+ var str = charsCache[chars];
+ if (str)
+ return str;
+
+ // translate the string using the font's encoding
+ var encoding = current.properties.encoding;
+ if (!encoding)
+ return chars;
+
+ str = "";
+ for (var i = 0; i < chars.length; ++i) {
+ var charcode = chars.charCodeAt(i);
+ var unicode = encoding[charcode];
+
+ // Check if the glyph has already been converted
+ if (!IsNum(unicode))
+ unicode = encoding[unicode] = GlyphsUnicode[unicode.name];
+
+ // Handle surrogate pairs
+ if (unicode > 0xFFFF) {
+ str += String.fromCharCode(unicode & 0xFFFF);
+ unicode >>= 16;
+ }
+ str += String.fromCharCode(unicode);
+ }
+
+ // Enter the translated string into the cache
+ return charsCache[chars] = str;
+ },
+ measureText: function fonts_measureText(text) {
+ var width;
+ if (measureCache && (width = measureCache[text]))
+ return width;
+ width = ctx.measureText(text).width / kScalePrecision;
+ if (measureCache)
+ measureCache[text] = width;
+ return width;
+ }
+ }
+})();
var FontLoader = {
bind: function(fonts) {
- var worker = (typeof window == "undefined");
var ready = true;
for (var i = 0; i < fonts.length; i++) {
var font = fonts[i];
- if (Fonts[font.name]) {
- ready = ready && !Fonts[font.name].loading;
+ if (Fonts.lookup(font.name)) {
+ ready = ready && !Fonts.lookup(font.name).loading;
continue;
}
@@ -97,18 +139,152 @@ var FontLoader = {
var obj = new Font(font.name, font.file, font.properties);
var str = "";
- var data = Fonts[font.name].data;
+ var data = Fonts.lookup(font.name).data;
var length = data.length;
for (var j = 0; j < length; j++)
str += String.fromCharCode(data[j]);
- worker ? obj.bindWorker(str) : obj.bindDOM(str);
+ isWorker ? obj.bindWorker(str) : obj.bindDOM(str);
}
return ready;
}
};
+var UnicodeRanges = [
+ { "begin": 0x0000, "end": 0x007F }, // Basic Latin
+ { "begin": 0x0080, "end": 0x00FF }, // Latin-1 Supplement
+ { "begin": 0x0100, "end": 0x017F }, // Latin Extended-A
+ { "begin": 0x0180, "end": 0x024F }, // Latin Extended-B
+ { "begin": 0x0250, "end": 0x02AF }, // IPA Extensions
+ { "begin": 0x02B0, "end": 0x02FF }, // Spacing Modifier Letters
+ { "begin": 0x0300, "end": 0x036F }, // Combining Diacritical Marks
+ { "begin": 0x0370, "end": 0x03FF }, // Greek and Coptic
+ { "begin": 0x2C80, "end": 0x2CFF }, // Coptic
+ { "begin": 0x0400, "end": 0x04FF }, // Cyrillic
+ { "begin": 0x0530, "end": 0x058F }, // Armenian
+ { "begin": 0x0590, "end": 0x05FF }, // Hebrew
+ { "begin": 0xA500, "end": 0xA63F }, // Vai
+ { "begin": 0x0600, "end": 0x06FF }, // Arabic
+ { "begin": 0x07C0, "end": 0x07FF }, // NKo
+ { "begin": 0x0900, "end": 0x097F }, // Devanagari
+ { "begin": 0x0980, "end": 0x09FF }, // Bengali
+ { "begin": 0x0A00, "end": 0x0A7F }, // Gurmukhi
+ { "begin": 0x0A80, "end": 0x0AFF }, // Gujarati
+ { "begin": 0x0B00, "end": 0x0B7F }, // Oriya
+ { "begin": 0x0B80, "end": 0x0BFF }, // Tamil
+ { "begin": 0x0C00, "end": 0x0C7F }, // Telugu
+ { "begin": 0x0C80, "end": 0x0CFF }, // Kannada
+ { "begin": 0x0D00, "end": 0x0D7F }, // Malayalam
+ { "begin": 0x0E00, "end": 0x0E7F }, // Thai
+ { "begin": 0x0E80, "end": 0x0EFF }, // Lao
+ { "begin": 0x10A0, "end": 0x10FF }, // Georgian
+ { "begin": 0x1B00, "end": 0x1B7F }, // Balinese
+ { "begin": 0x1100, "end": 0x11FF }, // Hangul Jamo
+ { "begin": 0x1E00, "end": 0x1EFF }, // Latin Extended Additional
+ { "begin": 0x1F00, "end": 0x1FFF }, // Greek Extended
+ { "begin": 0x2000, "end": 0x206F }, // General Punctuation
+ { "begin": 0x2070, "end": 0x209F }, // Superscripts And Subscripts
+ { "begin": 0x20A0, "end": 0x20CF }, // Currency Symbol
+ { "begin": 0x20D0, "end": 0x20FF }, // Combining Diacritical Marks For Symbols
+ { "begin": 0x2100, "end": 0x214F }, // Letterlike Symbols
+ { "begin": 0x2150, "end": 0x218F }, // Number Forms
+ { "begin": 0x2190, "end": 0x21FF }, // Arrows
+ { "begin": 0x2200, "end": 0x22FF }, // Mathematical Operators
+ { "begin": 0x2300, "end": 0x23FF }, // Miscellaneous Technical
+ { "begin": 0x2400, "end": 0x243F }, // Control Pictures
+ { "begin": 0x2440, "end": 0x245F }, // Optical Character Recognition
+ { "begin": 0x2460, "end": 0x24FF }, // Enclosed Alphanumerics
+ { "begin": 0x2500, "end": 0x257F }, // Box Drawing
+ { "begin": 0x2580, "end": 0x259F }, // Block Elements
+ { "begin": 0x25A0, "end": 0x25FF }, // Geometric Shapes
+ { "begin": 0x2600, "end": 0x26FF }, // Miscellaneous Symbols
+ { "begin": 0x2700, "end": 0x27BF }, // Dingbats
+ { "begin": 0x3000, "end": 0x303F }, // CJK Symbols And Punctuation
+ { "begin": 0x3040, "end": 0x309F }, // Hiragana
+ { "begin": 0x30A0, "end": 0x30FF }, // Katakana
+ { "begin": 0x3100, "end": 0x312F }, // Bopomofo
+ { "begin": 0x3130, "end": 0x318F }, // Hangul Compatibility Jamo
+ { "begin": 0xA840, "end": 0xA87F }, // Phags-pa
+ { "begin": 0x3200, "end": 0x32FF }, // Enclosed CJK Letters And Months
+ { "begin": 0x3300, "end": 0x33FF }, // CJK Compatibility
+ { "begin": 0xAC00, "end": 0xD7AF }, // Hangul Syllables
+ { "begin": 0xD800, "end": 0xDFFF }, // Non-Plane 0 *
+ { "begin": 0x10900, "end": 0x1091F }, // Phoenicia
+ { "begin": 0x4E00, "end": 0x9FFF }, // CJK Unified Ideographs
+ { "begin": 0xE000, "end": 0xF8FF }, // Private Use Area (plane 0)
+ { "begin": 0x31C0, "end": 0x31EF }, // CJK Strokes
+ { "begin": 0xFB00, "end": 0xFB4F }, // Alphabetic Presentation Forms
+ { "begin": 0xFB50, "end": 0xFDFF }, // Arabic Presentation Forms-A
+ { "begin": 0xFE20, "end": 0xFE2F }, // Combining Half Marks
+ { "begin": 0xFE10, "end": 0xFE1F }, // Vertical Forms
+ { "begin": 0xFE50, "end": 0xFE6F }, // Small Form Variants
+ { "begin": 0xFE70, "end": 0xFEFF }, // Arabic Presentation Forms-B
+ { "begin": 0xFF00, "end": 0xFFEF }, // Halfwidth And Fullwidth Forms
+ { "begin": 0xFFF0, "end": 0xFFFF }, // Specials
+ { "begin": 0x0F00, "end": 0x0FFF }, // Tibetan
+ { "begin": 0x0700, "end": 0x074F }, // Syriac
+ { "begin": 0x0780, "end": 0x07BF }, // Thaana
+ { "begin": 0x0D80, "end": 0x0DFF }, // Sinhala
+ { "begin": 0x1000, "end": 0x109F }, // Myanmar
+ { "begin": 0x1200, "end": 0x137F }, // Ethiopic
+ { "begin": 0x13A0, "end": 0x13FF }, // Cherokee
+ { "begin": 0x1400, "end": 0x167F }, // Unified Canadian Aboriginal Syllabics
+ { "begin": 0x1680, "end": 0x169F }, // Ogham
+ { "begin": 0x16A0, "end": 0x16FF }, // Runic
+ { "begin": 0x1780, "end": 0x17FF }, // Khmer
+ { "begin": 0x1800, "end": 0x18AF }, // Mongolian
+ { "begin": 0x2800, "end": 0x28FF }, // Braille Patterns
+ { "begin": 0xA000, "end": 0xA48F }, // Yi Syllables
+ { "begin": 0x1700, "end": 0x171F }, // Tagalog
+ { "begin": 0x10300, "end": 0x1032F }, // Old Italic
+ { "begin": 0x10330, "end": 0x1034F }, // Gothic
+ { "begin": 0x10400, "end": 0x1044F }, // Deseret
+ { "begin": 0x1D000, "end": 0x1D0FF }, // Byzantine Musical Symbols
+ { "begin": 0x1D400, "end": 0x1D7FF }, // Mathematical Alphanumeric Symbols
+ { "begin": 0xFF000, "end": 0xFFFFD }, // Private Use (plane 15)
+ { "begin": 0xFE00, "end": 0xFE0F }, // Variation Selectors
+ { "begin": 0xE0000, "end": 0xE007F }, // Tags
+ { "begin": 0x1900, "end": 0x194F }, // Limbu
+ { "begin": 0x1950, "end": 0x197F }, // Tai Le
+ { "begin": 0x1980, "end": 0x19DF }, // New Tai Lue
+ { "begin": 0x1A00, "end": 0x1A1F }, // Buginese
+ { "begin": 0x2C00, "end": 0x2C5F }, // Glagolitic
+ { "begin": 0x2D30, "end": 0x2D7F }, // Tifinagh
+ { "begin": 0x4DC0, "end": 0x4DFF }, // Yijing Hexagram Symbols
+ { "begin": 0xA800, "end": 0xA82F }, // Syloti Nagri
+ { "begin": 0x10000, "end": 0x1007F }, // Linear B Syllabary
+ { "begin": 0x10140, "end": 0x1018F }, // Ancient Greek Numbers
+ { "begin": 0x10380, "end": 0x1039F }, // Ugaritic
+ { "begin": 0x103A0, "end": 0x103DF }, // Old Persian
+ { "begin": 0x10450, "end": 0x1047F }, // Shavian
+ { "begin": 0x10480, "end": 0x104AF }, // Osmanya
+ { "begin": 0x10800, "end": 0x1083F }, // Cypriot Syllabary
+ { "begin": 0x10A00, "end": 0x10A5F }, // Kharoshthi
+ { "begin": 0x1D300, "end": 0x1D35F }, // Tai Xuan Jing Symbols
+ { "begin": 0x12000, "end": 0x123FF }, // Cuneiform
+ { "begin": 0x1D360, "end": 0x1D37F }, // Counting Rod Numerals
+ { "begin": 0x1B80, "end": 0x1BBF }, // Sundanese
+ { "begin": 0x1C00, "end": 0x1C4F }, // Lepcha
+ { "begin": 0x1C50, "end": 0x1C7F }, // Ol Chiki
+ { "begin": 0xA880, "end": 0xA8DF }, // Saurashtra
+ { "begin": 0xA900, "end": 0xA92F }, // Kayah Li
+ { "begin": 0xA930, "end": 0xA95F }, // Rejang
+ { "begin": 0xAA00, "end": 0xAA5F }, // Cham
+ { "begin": 0x10190, "end": 0x101CF }, // Ancient Symbols
+ { "begin": 0x101D0, "end": 0x101FF }, // Phaistos Disc
+ { "begin": 0x102A0, "end": 0x102DF }, // Carian
+ { "begin": 0x1F030, "end": 0x1F09F } // Domino Tiles
+];
+
+function getUnicodeRangeFor(value) {
+ for (var i = 0; i < UnicodeRanges.length; i++) {
+ var range = UnicodeRanges[i];
+ if (value >= range.begin && value < range.end)
+ return i;
+ }
+ return -1;
+};
/**
* 'Font' is the class the outside world should use, it encapsulate all the font
@@ -124,8 +300,8 @@ var Font = (function () {
this.encoding = properties.encoding;
// If the font has already been decoded simply return it
- if (Fonts[name]) {
- this.font = Fonts[name].data;
+ if (Fonts.lookup(name)) {
+ this.font = Fonts.lookup(name).data;
return;
}
fontCount++;
@@ -134,12 +310,7 @@ var Font = (function () {
// If the font is to be ignored, register it like an already loaded font
// to avoid the cost of waiting for it be be loaded by the platform.
if (properties.ignore || kDisableFonts) {
- Fonts[name] = {
- data: file,
- loading: false,
- properties: {},
- cache: Object.create(null)
- }
+ Fonts.blacklistFont(name);
return;
}
@@ -165,13 +336,8 @@ var Font = (function () {
warn("Font " + properties.type + " is not supported");
break;
}
-
- Fonts[name] = {
- data: data,
- properties: properties,
- loading: true,
- cache: Object.create(null)
- };
+ this.data = data;
+ Fonts.registerFont(name, data, properties);
};
function stringToArray(str) {
@@ -221,6 +387,9 @@ var Font = (function () {
// offset
var offset = offsets.virtualOffset;
+ // length
+ var length = data.length;
+
// Per spec tables must be 4-bytes align so add padding as needed
while (data.length & 3)
data.push(0x00);
@@ -228,16 +397,10 @@ var Font = (function () {
while (offsets.virtualOffset & 3)
offsets.virtualOffset++;
- // length
- var length = data.length;
-
// checksum
- var checksum = tag.charCodeAt(0) +
- tag.charCodeAt(1) +
- tag.charCodeAt(2) +
- tag.charCodeAt(3) +
- offset +
- length;
+ var checksum = 0;
+ for (var i = 0; i < length; i+=4)
+ checksum += FontsUtils.bytesToInteger([data[i], data[i+1], data[i+2], data[i+3]]);
var tableEntry = tag + string32(checksum) + string32(offset) + string32(length);
tableEntry = stringToArray(tableEntry);
@@ -271,6 +434,7 @@ var Font = (function () {
};
function createCMapTable(glyphs) {
+ glyphs.push({ unicode: 0x0000 });
var ranges = getRanges(glyphs);
var headerSize = (12 * 2 + (ranges.length * 4 * 2));
@@ -304,13 +468,13 @@ var Font = (function () {
var range = ranges[i];
var start = range[0];
var end = range[1];
- var delta = (((start - 1) - bias) ^ 0xffff) + 1;
+ var delta = (((start - 1) - bias) ^ 0xffff);
bias += (end - start + 1);
startCount += string16(start);
endCount += string16(end);
idDeltas += string16(delta);
- idRangeOffsets += string16(0);
+ idRangeOffsets += string16(0);
for (var j = 0; j < range.length; j++)
glyphsIds += String.fromCharCode(range[j]);
@@ -326,11 +490,43 @@ var Font = (function () {
};
function createOS2Table(properties) {
+ var ulUnicodeRange1 = 0;
+ var ulUnicodeRange2 = 0;
+ var ulUnicodeRange3 = 0;
+ var ulUnicodeRange4 = 0;
+
+ var charset = properties.charset;
+ if (charset && charset.length) {
+ var firstCharIndex = null;
+ var lastCharIndex = 0;
+
+ for (var i = 1; i < charset.length; i++) {
+ var code = GlyphsUnicode[charset[i]];
+ if (firstCharIndex > code || !firstCharIndex)
+ firstCharIndex = code;
+ if (lastCharIndex < code)
+ lastCharIndex = code;
+
+ var position = getUnicodeRangeFor(code);
+ if (position < 32) {
+ ulUnicodeRange1 |= 1 << position;
+ } else if (position < 64) {
+ ulUnicodeRange2 |= 1 << position - 32;
+ } else if (position < 96) {
+ ulUnicodeRange3 |= 1 << position - 64;
+ } else if (position < 123) {
+ ulUnicodeRange4 |= 1 << position - 96;
+ } else {
+ error("Unicode ranges Bits > 123 are reserved for internal usage");
+ }
+ }
+ }
+
return "\x00\x03" + // version
"\x02\x24" + // xAvgCharWidth
"\x01\xF4" + // usWeightClass
"\x00\x05" + // usWidthClass
- "\x00\x00" + // fstype
+ "\x00\x02" + // fstype
"\x02\x8A" + // ySubscriptXSize
"\x02\xBB" + // ySubscriptYSize
"\x00\x00" + // ySubscriptXOffset
@@ -342,41 +538,41 @@ var Font = (function () {
"\x00\x31" + // yStrikeOutSize
"\x01\x02" + // yStrikeOutPosition
"\x00\x00" + // sFamilyClass
- "\x02\x00\x06\x03\x00\x00\x00\x00\x00\x00" + // Panose
- "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 0-31)
- "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 32-63)
- "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 64-95)
- "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 96-127)
+ "\x00\x00\x06" + String.fromCharCode(properties.fixedPitch ? 0x09 : 0x00) +
+ "\x00\x00\x00\x00\x00\x00" + // Panose
+ string32(ulUnicodeRange1) + // ulUnicodeRange1 (Bits 0-31)
+ string32(ulUnicodeRange2) + // ulUnicodeRange2 (Bits 32-63)
+ string32(ulUnicodeRange3) + // ulUnicodeRange3 (Bits 64-95)
+ string32(ulUnicodeRange4) + // ulUnicodeRange4 (Bits 96-127)
"\x2A\x32\x31\x2A" + // achVendID
- "\x00\x20" + // fsSelection
- "\x00\x2D" + // usFirstCharIndex
- "\x00\x7A" + // usLastCharIndex
- "\x00\x03" + // sTypoAscender
- "\x00\x20" + // sTypeDescender
- "\x00\x38" + // sTypoLineGap
+ string16(properties.italicAngle ? 1 : 0) + // fsSelection
+ string16(firstCharIndex || properties.firstChar) + // usFirstCharIndex
+ string16(lastCharIndex || properties.lastChar) + // usLastCharIndex
+ string16(properties.ascent) + // sTypoAscender
+ string16(properties.descent) + // sTypoDescender
+ "\x00\x64" + // sTypoLineGap (7%-10% of the unitsPerEM value)
string16(properties.ascent) + // usWinAscent
- string16(properties.descent) + // usWinDescent
- "\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31)
- "\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63)
+ string16(-properties.descent) + // usWinDescent
+ "\x00\x00\x00\x00" + // ulCodePageRange1 (Bits 0-31)
+ "\x00\x00\x00\x00" + // ulCodePageRange2 (Bits 32-63)
string16(properties.xHeight) + // sxHeight
string16(properties.capHeight) + // sCapHeight
- "\x00\x01" + // usDefaultChar
- "\x00\xCD" + // usBreakChar
- "\x00\x02"; // usMaxContext
+ string16(0) + // usDefaultChar
+ string16(firstCharIndex || properties.firstChar) + // usBreakChar
+ "\x00\x03"; // usMaxContext
};
function createPostTable(properties) {
- TODO("Fill with real values from the font dict");
-
- return "\x00\x03\x00\x00" + // Version number
- string32(properties.italicAngle) + // italicAngle
- "\x00\x00" + // underlinePosition
- "\x00\x00" + // underlineThickness
- "\x00\x00\x00\x00" + // isFixedPitch
- "\x00\x00\x00\x00" + // minMemType42
- "\x00\x00\x00\x00" + // maxMemType42
- "\x00\x00\x00\x00" + // minMemType1
- "\x00\x00\x00\x00"; // maxMemType1
+ var angle = Math.floor(properties.italicAngle * (Math.pow(2, 16)));
+ return "\x00\x03\x00\x00" + // Version number
+ string32(angle) + // italicAngle
+ "\x00\x00" + // underlinePosition
+ "\x00\x00" + // underlineThickness
+ string32(properties.fixedPitch) + // isFixedPitch
+ "\x00\x00\x00\x00" + // minMemType42
+ "\x00\x00\x00\x00" + // maxMemType42
+ "\x00\x00\x00\x00" + // minMemType1
+ "\x00\x00\x00\x00"; // maxMemType1
};
constructor.prototype = {
@@ -604,44 +800,75 @@ var Font = (function () {
var otf = new Uint8Array(kMaxFontFileSize);
function createNameTable(name) {
- var names = [
- "See original licence", // Copyright
- fontName, // Font family
- "undefined", // Font subfamily (font weight)
- "uniqueID", // Unique ID
- fontName, // Full font name
- "0.1", // Version
- "undefined", // Postscript name
- "undefined", // Trademark
- "undefined", // Manufacturer
- "undefined" // Designer
+ // All the strings of the name table should be an odd number of bytes
+ if (name.length % 2)
+ name = name.slice(0, name.length - 1);
+
+ var strings = [
+ "Original licence", // 0.Copyright
+ name, // 1.Font family
+ "Unknown", // 2.Font subfamily (font weight)
+ "uniqueID", // 3.Unique ID
+ name, // 4.Full font name
+ "Version 0.11", // 5.Version
+ "Unknown", // 6.Postscript name
+ "Unknown", // 7.Trademark
+ "Unknown", // 8.Manufacturer
+ "Unknown" // 9.Designer
];
+ // Mac want 1-byte per character strings while Windows want
+ // 2-bytes per character, so duplicate the names table
+ var stringsUnicode = [];
+ for (var i = 0; i < strings.length; i++) {
+ var str = strings[i];
+
+ var strUnicode = "";
+ for (var j = 0; j < str.length; j++)
+ strUnicode += string16(str.charCodeAt(j));
+ stringsUnicode.push(strUnicode);
+ }
+
+ var names = [strings, stringsUnicode];
+ var platforms = ["\x00\x01", "\x00\x03"];
+ var encodings = ["\x00\x00", "\x00\x01"];
+ var languages = ["\x00\x00", "\x04\x09"];
+
+ var namesRecordCount = strings.length * platforms.length;
var nameTable =
- "\x00\x00" + // format
- "\x00\x0A" + // Number of names Record
- "\x00\x7E"; // Storage
+ "\x00\x00" + // format
+ string16(namesRecordCount) + // Number of names Record
+ string16(namesRecordCount * 12 + 6); // Storage
// Build the name records field
var strOffset = 0;
- for (var i = 0; i < names.length; i++) {
- var str = names[i];
-
- var nameRecord =
- "\x00\x01" + // platform ID
- "\x00\x00" + // encoding ID
- "\x00\x00" + // language ID
- "\x00\x00" + // name ID
- string16(str.length) +
- string16(strOffset);
- nameTable += nameRecord;
-
- strOffset += str.length;
+ for (var i = 0; i < platforms.length; i++) {
+ var strs = names[i];
+ for (var j = 0; j < strs.length; j++) {
+ var str = strs[j];
+ var nameRecord =
+ platforms[i] + // platform ID
+ encodings[i] + // encoding ID
+ languages[i] + // language ID
+ string16(i) + // name ID
+ string16(str.length) +
+ string16(strOffset);
+ nameTable += nameRecord;
+ strOffset += str.length;
+ }
}
- nameTable += names.join("");
+ nameTable += strings.join("") + stringsUnicode.join("");
return nameTable;
}
+
+ function isFixedPitch(glyphs) {
+ for (var i = 0; i < glyphs.length - 1; i++) {
+ if (glyphs[i] != glyphs[i+1])
+ return false;
+ }
+ return true;
+ };
// Required Tables
var CFF =
@@ -672,30 +899,31 @@ var Font = (function () {
createTableEntry(otf, offsets, "CFF ", CFF);
/** OS/2 */
+ var charstrings = font.charstrings;
+ properties.fixedPitch = isFixedPitch(charstrings);
OS2 = stringToArray(createOS2Table(properties));
createTableEntry(otf, offsets, "OS/2", OS2);
/** CMAP */
- var charstrings = font.charstrings;
- cmap = createCMapTable(charstrings);
+ cmap = createCMapTable(charstrings.slice());
createTableEntry(otf, offsets, "cmap", cmap);
/** HEAD */
head = stringToArray(
"\x00\x01\x00\x00" + // Version number
- "\x00\x00\x50\x00" + // fontRevision
+ "\x00\x00\x10\x00" + // fontRevision
"\x00\x00\x00\x00" + // checksumAdjustement
"\x5F\x0F\x3C\xF5" + // magicNumber
"\x00\x00" + // Flags
"\x03\xE8" + // unitsPerEM (defaulting to 1000)
- "\x00\x00\x00\x00\x00\x00\x00\x00" + // creation date
- "\x00\x00\x00\x00\x00\x00\x00\x00" + // modifification date
+ "\x00\x00\x00\x00\x9e\x0b\x7e\x27" + // creation date
+ "\x00\x00\x00\x00\x9e\x0b\x7e\x27" + // modifification date
"\x00\x00" + // xMin
- "\x00\x00" + // yMin
- "\x00\x00" + // xMax
- "\x00\x00" + // yMax
- "\x00\x00" + // macStyle
- "\x00\x00" + // lowestRecPPEM
+ string16(properties.descent) + // yMin
+ "\x0F\xFF" + // xMax
+ string16(properties.ascent) + // yMax
+ string16(properties.italicAngle ? 2 : 0) + // macStyle
+ "\x00\x11" + // lowestRecPPEM
"\x00\x00" + // fontDirectionHint
"\x00\x00" + // indexToLocFormat
"\x00\x00" // glyphDataFormat
@@ -705,22 +933,22 @@ var Font = (function () {
/** HHEA */
hhea = stringToArray(
"\x00\x01\x00\x00" + // Version number
- "\x00\x00" + // Typographic Ascent
- "\x00\x00" + // Typographic Descent
+ string16(properties.ascent) + // Typographic Ascent
+ string16(properties.descent) + // Typographic Descent
"\x00\x00" + // Line Gap
"\xFF\xFF" + // advanceWidthMax
"\x00\x00" + // minLeftSidebearing
"\x00\x00" + // minRightSidebearing
"\x00\x00" + // xMaxExtent
- "\x00\x00" + // caretSlopeRise
- "\x00\x00" + // caretSlopeRun
+ string16(properties.capHeight) + // caretSlopeRise
+ string16(Math.tan(properties.italicAngle) * properties.xHeight) + // caretSlopeRun
"\x00\x00" + // caretOffset
"\x00\x00" + // -reserved-
"\x00\x00" + // -reserved-
"\x00\x00" + // -reserved-
"\x00\x00" + // -reserved-
"\x00\x00" + // metricDataFormat
- string16(charstrings.length)
+ string16(charstrings.length + 1) // Number of HMetrics
);
createTableEntry(otf, offsets, "hhea", hhea);
@@ -730,23 +958,21 @@ var Font = (function () {
* while Windows use this data. So be careful if you hack on Linux and
* have to touch the 'hmtx' table
*/
- hmtx = "\x01\xF4\x00\x00"; // Fake .notdef
- var width = 0, lsb = 0;
+ hmtx = "\x00\x00\x00\x00"; // Fake .notdef
for (var i = 0; i < charstrings.length; i++) {
- width = charstrings[i].charstring[1];
- hmtx += string16(width) + string16(lsb);
+ hmtx += string16(charstrings[i].width) + string16(0);
}
hmtx = stringToArray(hmtx);
createTableEntry(otf, offsets, "hmtx", hmtx);
/** MAXP */
maxp = "\x00\x00\x50\x00" + // Version number
- string16(charstrings.length + 1); // Num of glyphs (+1 to pass the sanitizer...)
+ string16(charstrings.length + 1); // Num of glyphs
maxp = stringToArray(maxp);
createTableEntry(otf, offsets, "maxp", maxp);
/** NAME */
- name = stringToArray(createNameTable(name));
+ name = stringToArray(createNameTable(fontName));
createTableEntry(otf, offsets, "name", name);
/** POST */
@@ -778,9 +1004,18 @@ var Font = (function () {
});
},
- bindDOM: function font_bindDom(data) {
+ bindDOM: function font_bindDom(data, callback) {
var fontName = this.name;
+ // Just adding the font-face to the DOM doesn't make it load. It
+ // seems it's loaded once Gecko notices it's used. Therefore,
+ // add a div on the page using the loaded font.
+ var div = document.createElement("div");
+ var style = 'font-family:"' + name +
+ '";position: absolute;top:-99999;left:-99999;z-index:-99999';
+ div.setAttribute("style", style);
+ document.body.appendChild(div);
+
/** Hack begin */
// Actually there is not event when a font has finished downloading so
// the following code are a dirty hack to 'guess' when a font is ready
@@ -800,15 +1035,19 @@ var Font = (function () {
// For some reasons the font has not loaded, so mark it loaded for the
// page to proceed but cry
- if ((Date.now() - this.start) >= kMaxWaitForFontFace) {
- window.clearInterval(interval);
- Fonts[fontName].loading = false;
- warn("Is " + fontName + " loaded?");
- this.start = 0;
- } else if (textWidth != ctx.measureText(testString).width) {
- window.clearInterval(interval);
- Fonts[fontName].loading = false;
- this.start = 0;
+ if (textWidth == ctx.measureText(testString).width) {
+ if ((Date.now() - this.start) < kMaxWaitForFontFace) {
+ return;
+ } else {
+ warn("Is " + fontName + " loaded?");
+ }
+ }
+
+ window.clearInterval(interval);
+ Fonts.lookup(fontName).loading = false;
+ this.start = 0;
+ if (callback) {
+ callback();
}
}, 30, this);
@@ -839,7 +1078,7 @@ var FontsUtils = {
bytes.set([value]);
return bytes[0];
} else if (bytesCount == 2) {
- bytes.set([value >> 8, value]);
+ bytes.set([value >> 8, value & 0xff]);
return [bytes[0], bytes[1]];
} else if (bytesCount == 4) {
bytes.set([value >> 24, value >> 16, value >> 8, value]);
@@ -980,16 +1219,8 @@ var Type1Parser = function() {
"12": "div",
// callothersubr is a mechanism to make calls on the postscript
- // interpreter.
- // TODO When decodeCharstring encounter such a command it should
- // directly do:
- // - pop the previous charstring[] command into 'index'
- // - pop the previous charstring[] command and ignore it, it is
- // normally the number of element to push on the stack before
- // the command but since everything will be pushed on the stack
- // by the PS interpreter when it will read them that is safe to
- // ignore this command
- // - push the content of the OtherSubrs[index] inside charstring[]
+ // interpreter, this is not supported by Type2 charstring but hopefully
+ // most of the default commands can be ignored safely.
"16": "callothersubr",
"17": "pop",
@@ -1009,8 +1240,13 @@ var Type1Parser = function() {
"31": "hvcurveto"
};
+ var kEscapeCommand = 12;
+
function decodeCharString(array) {
- var charString = [];
+ var charstring = [];
+ var lsb = 0;
+ var width = 0;
+ var used = false;
var value = "";
var count = array.length;
@@ -1019,10 +1255,48 @@ var Type1Parser = function() {
if (value < 32) {
var command = null;
- if (value == 12) {
+ if (value == kEscapeCommand) {
var escape = array[++i];
+
+ // TODO Clean this code
+ if (escape == 16) {
+ var index = charstring.pop();
+ var argc = charstring.pop();
+ var data = charstring.pop();
+
+ // If the flex mechanishm is not used in a font program, Adobe
+ // state that that entries 0, 1 and 2 can simply be replace by
+ // {}, which means that we can simply ignore them.
+ if (index < 3) {
+ continue;
+ }
+
+ // This is the same things about hint replacement, if it is not used
+ // entry 3 can be replaced by {3}
+ if (index == 3) {
+ charstring.push(3);
+ i++;
+ continue;
+ }
+ }
+
command = charStringDictionary["12"][escape];
} else {
+ // TODO Clean this code
+ if (value == 13) {
+ if (charstring.length == 2) {
+ width = charstring[1];
+ } else if (charstring.length == 4 && charstring[3] == "div") {
+ width = charstring[1] / charstring[2];
+ } else {
+ error("Unsupported hsbw format: " + charstring);
+ }
+
+ lsb = charstring[0];
+ charstring.push(lsb, "hmoveto");
+ charstring.splice(0, 1);
+ continue;
+ }
command = charStringDictionary[value];
}
@@ -1044,16 +1318,14 @@ var Type1Parser = function() {
} else if (value <= 254) {
value = -((value - 251) * 256) - parseInt(array[++i]) - 108;
} else {
- var byte = array[++i];
- var high = (byte >> 1);
- value = (byte - high) << 24 | array[++i] << 16 |
- array[++i] << 8 | array[++i];
+ value = (array[++i] & 0xff) << 24 | (array[++i] & 0xff) << 16 |
+ (array[++i] & 0xff) << 8 | (array[++i] & 0xff) << 0;
}
- charString.push(value);
+ charstring.push(value);
}
- return charString;
+ return { charstring: charstring, width: width, lsb: lsb };
};
/**
@@ -1080,19 +1352,21 @@ var Type1Parser = function() {
length = parseInt(length);
var data = eexecString.slice(i + 3, i + 3 + length);
var encodedSubr = decrypt(data, kCharStringsEncryptionKey, 4);
- var subr = decodeCharString(encodedSubr);
+ var str = decodeCharString(encodedSubr);
- subrs.push(subr);
+ subrs.push(str.charstring);
i += 3 + length;
} else if (inGlyphs && c == 0x52) {
length = parseInt(length);
var data = eexecString.slice(i + 3, i + 3 + length);
var encodedCharstring = decrypt(data, kCharStringsEncryptionKey, 4);
- var subr = decodeCharString(encodedCharstring);
+ var str = decodeCharString(encodedCharstring);
glyphs.push({
glyph: glyph,
- data: subr
+ data: str.charstring,
+ lsb: str.lsb,
+ width: str.width
});
i += 3 + length;
} else if (inGlyphs && c == 0x2F) {
@@ -1254,16 +1528,18 @@ CFF.prototype = {
var charstrings = [];
for (var i = 0; i < glyphs.length; i++) {
- var glyph = glyphs[i].glyph;
- var unicode = GlyphsUnicode[glyph];
+ var glyph = glyphs[i];
+ var unicode = GlyphsUnicode[glyph.glyph];
if (!unicode) {
- if (glyph != ".notdef")
+ if (glyph.glyph != ".notdef")
warn(glyph + " does not have an entry in the glyphs unicode dictionary");
} else {
charstrings.push({
glyph: glyph,
unicode: unicode,
- charstring: glyphs[i].data
+ charstring: glyph.data,
+ width: glyph.width,
+ lsb: glyph.lsb
});
}
};
@@ -1305,46 +1581,11 @@ CFF.prototype = {
var i = 0;
while (true) {
var obj = charstring[i];
- if (obj == null)
- return [];
-
+ if (obj == undefined) {
+ error("unknow charstring command for " + i + " in " + charstring);
+ }
if (obj.charAt) {
switch (obj) {
- case "callothersubr":
- var index = charstring[i - 1];
- var count = charstring[i - 2];
- var data = charstring[i - 3];
-
- // If the flex mechanishm is not used in a font program, Adobe
- // state that that entries 0, 1 and 2 can simply be replace by
- // {}, which means that we can simply ignore them.
- if (index < 3) {
- i -= 3;
- continue;
- }
-
- // This is the same things about hint replacment, if it is not used
- // entry 3 can be replaced by {}
- if (index == 3) {
- if (!data) {
- charstring.splice(i - 2, 4, 3);
- i -= 3;
- } else {
- // 5 to remove the arguments, the callothersubr call and the pop command
- charstring.splice(i - 3, 5, 3);
- i -= 3;
- }
- }
- break;
-
- case "hsbw":
- var charWidthVector = charstring[1];
- var leftSidebearing = charstring[0];
-
- charstring.splice(i, 1, leftSidebearing, "hmoveto");
- charstring.splice(0, 1);
- break;
-
case "endchar":
case "return":
// CharString is ready to be re-encode to commands number at this point
@@ -1356,7 +1597,7 @@ CFF.prototype = {
} else if (command.charAt) {
var cmd = this.commandsMap[command];
if (!cmd)
- error(command);
+ error("Unknow command: " + command);
if (IsArray(cmd)) {
charstring.splice(j, 1, cmd[0], cmd[1]);
@@ -1428,7 +1669,7 @@ CFF.prototype = {
charset.push(bytes[1]);
}
- var charstringsIndex = this.createCFFIndexHeader([[0x40, 0x0E]].concat(glyphs), true);
+ var charstringsIndex = this.createCFFIndexHeader([[0x8B, 0x0E]].concat(glyphs), true);
//Top Dict Index
var topDictIndex = [
diff --git a/multi_page_viewer.css b/multi_page_viewer.css
index 2eaca4870..17b2537be 100644
--- a/multi_page_viewer.css
+++ b/multi_page_viewer.css
@@ -181,7 +181,7 @@ span {
width: 200px;
top: 62px;
bottom: 18px;
- left: -170px;
+ left: -140px;
transition: left 0.25s ease-in-out 1s;
-moz-transition: left 0.25s ease-in-out 1s;
-webkit-transition: left 0.25s ease-in-out 1s;
diff --git a/multi_page_viewer.html b/multi_page_viewer.html
index e90606a23..df71d6690 100644
--- a/multi_page_viewer.html
+++ b/multi_page_viewer.html
@@ -27,9 +27,9 @@
Zoom
diff --git a/multi_page_viewer.js b/multi_page_viewer.js
index b2c0dc3ed..180e715eb 100644
--- a/multi_page_viewer.js
+++ b/multi_page_viewer.js
@@ -29,11 +29,15 @@ var PDFViewer = {
scale: 1.0,
pageWidth: function(page) {
- return page.mediaBox[2] * PDFViewer.scale;
+ var pdfToCssUnitsCoef = 96.0 / 72.0;
+ var width = (page.mediaBox[2] - page.mediaBox[0]);
+ return width * PDFViewer.scale * pdfToCssUnitsCoef;
},
pageHeight: function(page) {
- return page.mediaBox[3] * PDFViewer.scale;
+ var pdfToCssUnitsCoef = 96.0 / 72.0;
+ var height = (page.mediaBox[3] - page.mediaBox[1]);
+ return height * PDFViewer.scale * pdfToCssUnitsCoef;
},
lastPagesDrawn: [],
@@ -106,10 +110,11 @@ var PDFViewer = {
canvas.id = 'thumbnail' + num;
canvas.mozOpaque = true;
- // Canvas dimensions must be specified in CSS pixels. CSS pixels
- // are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi.
- canvas.width = 104;
- canvas.height = 134;
+ var pageWidth = PDFViewer.pageWidth(page);
+ var pageHeight = PDFViewer.pageHeight(page);
+ var thumbScale = Math.min(104 / pageWidth, 134 / pageHeight);
+ canvas.width = pageWidth * thumbScale;
+ canvas.height = pageHeight * thumbScale;
div.appendChild(canvas);
var ctx = canvas.getContext('2d');
@@ -175,8 +180,6 @@ var PDFViewer = {
canvas.id = 'page' + num;
canvas.mozOpaque = true;
- // Canvas dimensions must be specified in CSS pixels. CSS pixels
- // are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi.
canvas.width = PDFViewer.pageWidth(page);
canvas.height = PDFViewer.pageHeight(page);
div.appendChild(canvas);
@@ -216,7 +219,6 @@ var PDFViewer = {
if (PDFViewer.pdf) {
for (i = 1; i <= PDFViewer.numberOfPages; i++) {
- PDFViewer.createThumbnail(i);
PDFViewer.createPage(i);
}
}
@@ -249,7 +251,10 @@ var PDFViewer = {
PDFViewer.pageNumber = num;
PDFViewer.pageNumberInput.value = PDFViewer.pageNumber;
PDFViewer.willJumpToPage = true;
-
+
+ if (document.location.hash.substr(1) == PDFViewer.pageNumber)
+ // Force a "scroll event" to redraw
+ setTimeout(window.onscroll, 0);
document.location.hash = PDFViewer.pageNumber;
PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ? 'disabled' : '';
@@ -272,6 +277,12 @@ var PDFViewer = {
openURL: function(url) {
PDFViewer.url = url;
document.title = url;
+
+ if (this.thumbsLoadingInterval) {
+ // cancel thumbs loading operations
+ clearInterval(this.thumbsLoadingInterval);
+ this.thumbsLoadingInterval = null;
+ }
var req = new XMLHttpRequest();
req.open('GET', url);
@@ -288,7 +299,9 @@ var PDFViewer = {
req.send(null);
},
-
+
+ thumbsLoadingInterval: null,
+
readPDF: function(data) {
while (PDFViewer.element.hasChildNodes()) {
PDFViewer.element.removeChild(PDFViewer.element.firstChild);
@@ -310,12 +323,22 @@ var PDFViewer = {
PDFViewer.drawPage(1);
document.location.hash = 1;
- setTimeout(function() {
- for (var i = 1; i <= PDFViewer.numberOfPages; i++) {
- PDFViewer.createThumbnail(i);
- PDFViewer.drawThumbnail(i);
+ // slowly loading the thumbs (few per second)
+ // first time we are loading more images than subsequent
+ var currentPageIndex = 1, imagesToLoad = 15;
+ this.thumbsLoadingInterval = setInterval((function() {
+ while (imagesToLoad-- > 0) {
+ if (currentPageIndex > PDFViewer.numberOfPages) {
+ clearInterval(this.thumbsLoadingInterval);
+ this.thumbsLoadingInterval = null;
+ return;
+ }
+ PDFViewer.createThumbnail(currentPageIndex);
+ PDFViewer.drawThumbnail(currentPageIndex);
+ ++currentPageIndex;
}
- }, 500);
+ imagesToLoad = 3; // next time loading less images
+ }).bind(this), 500);
}
PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ? 'disabled' : '';
diff --git a/pdf.js b/pdf.js
index be471cfc0..3e3654087 100644
--- a/pdf.js
+++ b/pdf.js
@@ -641,7 +641,7 @@ var PredictorStream = (function() {
var pixBytes = this.pixBytes = (colors * bits + 7) >> 3;
// add an extra pixByte to represent the pixel left of column 0
var rowBytes = this.rowBytes = (columns * colors * bits + 7) >> 3;
-
+
DecodeStream.call(this);
return this;
}
@@ -3440,6 +3440,7 @@ var CanvasGraphics = (function() {
if (charset) {
assertWellFormed(IsString(charset), "invalid charset");
charset = charset.split("/");
+ charset.shift();
}
} else if (IsName(encoding)) {
var encoding = Encodings[encoding.name];
@@ -3534,13 +3535,16 @@ var CanvasGraphics = (function() {
type: subType.name,
encoding: encodingMap,
charset: charset,
+ firstChar: fontDict.get("FirstChar"),
+ lastChar: fontDict.get("LastChar"),
bbox: descriptor.get("FontBBox"),
ascent: descriptor.get("Ascent"),
descent: descriptor.get("Descent"),
xHeight: descriptor.get("XHeight"),
capHeight: descriptor.get("CapHeight"),
flags: descriptor.get("Flags"),
- italicAngle: descriptor.get("ItalicAngle")
+ italicAngle: descriptor.get("ItalicAngle"),
+ fixedPitch: false
};
return {
@@ -3807,18 +3811,22 @@ var CanvasGraphics = (function() {
if (fontDescriptor && fontDescriptor.num) {
var fontDescriptor = this.xref.fetchIfRef(fontDescriptor);
fontName = fontDescriptor.get("FontName").name.replace("+", "_");
- Fonts.active = fontName;
}
if (!fontName) {
// TODO: fontDescriptor is not available, fallback to default font
- this.current.fontSize = size;
- this.ctx.font = this.current.fontSize + 'px sans-serif';
- return;
+ fontName = "sans-serif";
}
+ this.current.fontName = fontName;
this.current.fontSize = size;
- this.ctx.font = this.current.fontSize +'px "' + fontName + '", Symbol';
+
+ if (this.ctx.$setFont) {
+ this.ctx.$setFont(fontName, size);
+ } else {
+ this.ctx.font = size + 'px "' + fontName + '"';
+ Fonts.setActive(fontName, size);
+ }
},
setTextRenderingMode: function(mode) {
TODO("text rendering mode");
@@ -3862,7 +3870,7 @@ var CanvasGraphics = (function() {
text = Fonts.charsToUnicode(text);
this.ctx.translate(this.current.x, -1 * this.current.y);
this.ctx.fillText(text, 0, 0);
- this.current.x += this.ctx.measureText(text).width;
+ this.current.x += Fonts.measureText(text);
}
this.ctx.restore();
diff --git a/test/test_slave.html b/test/test_slave.html
index d70e362af..0a330e703 100644
--- a/test/test_slave.html
+++ b/test/test_slave.html
@@ -4,6 +4,7 @@
+
+
+
+
+