Merge remote branch 'upstream/master'

This commit is contained in:
Rob Sayre 2011-06-30 21:02:47 -07:00
commit f4e5b2bcfd
18 changed files with 852 additions and 321 deletions

12
README
View File

@ -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

27
README.md Normal file
View File

@ -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

View File

@ -1,5 +1,5 @@
/* -*- Mode: Java; tab-width: s; indent-tabs-mode: nil; c-basic-offset: 2 -*- / /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=s tabstop=2 autoindent cindent expandtab: */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
"use strict"; "use strict";
@ -45,12 +45,12 @@ var ARCFourCipher = (function() {
})(); })();
var md5 = (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, 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, 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, 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]); 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, -680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426,
-1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162, -1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162,
1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632, 1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632,
@ -149,7 +149,7 @@ var CipherTransform = (function() {
var CipherTransformFactory = (function() { var CipherTransformFactory = (function() {
function prepareKeyData(fileId, password, ownerPassword, userPassword, flags, revision, keyLength) { 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, 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]); 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; var hashData = new Uint8Array(88), i = 0, j, n;

701
fonts.js
View File

@ -3,6 +3,8 @@
"use strict"; "use strict";
var isWorker = (typeof window == "undefined");
/** /**
* Maximum file size of the font. * Maximum file size of the font.
*/ */
@ -26,69 +28,109 @@ var fontName = "";
*/ */
var kDisableFonts = false; var kDisableFonts = false;
/** /**
* Hold a map of decoded fonts and of the standard fourteen Type1 fonts and * Hold a map of decoded fonts and of the standard fourteen Type1 fonts and
* their acronyms. * their acronyms.
* TODO Add the standard fourteen Type1 fonts list by default * TODO Add the standard fourteen Type1 fonts list by default
* http://cgit.freedesktop.org/poppler/poppler/tree/poppler/GfxFont.cc#n65 * http://cgit.freedesktop.org/poppler/poppler/tree/poppler/GfxFont.cc#n65
*/ */
var Fonts = {
_active: null,
get active() { var Fonts = (function () {
return this._active; var kScalePrecision = 40;
}, var fonts = Object.create(null);
set active(name) { if (!isWorker) {
this._active = this[name]; var ctx = document.createElement("canvas").getContext("2d");
}, ctx.scale(1 / kScalePrecision, 1);
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;
} }
};
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 = { var FontLoader = {
bind: function(fonts) { bind: function(fonts) {
var worker = (typeof window == "undefined");
var ready = true; var ready = true;
for (var i = 0; i < fonts.length; i++) { for (var i = 0; i < fonts.length; i++) {
var font = fonts[i]; var font = fonts[i];
if (Fonts[font.name]) { if (Fonts.lookup(font.name)) {
ready = ready && !Fonts[font.name].loading; ready = ready && !Fonts.lookup(font.name).loading;
continue; continue;
} }
@ -97,18 +139,152 @@ var FontLoader = {
var obj = new Font(font.name, font.file, font.properties); var obj = new Font(font.name, font.file, font.properties);
var str = ""; var str = "";
var data = Fonts[font.name].data; var data = Fonts.lookup(font.name).data;
var length = data.length; var length = data.length;
for (var j = 0; j < length; j++) for (var j = 0; j < length; j++)
str += String.fromCharCode(data[j]); str += String.fromCharCode(data[j]);
worker ? obj.bindWorker(str) : obj.bindDOM(str); isWorker ? obj.bindWorker(str) : obj.bindDOM(str);
} }
return ready; 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 * '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; this.encoding = properties.encoding;
// If the font has already been decoded simply return it // If the font has already been decoded simply return it
if (Fonts[name]) { if (Fonts.lookup(name)) {
this.font = Fonts[name].data; this.font = Fonts.lookup(name).data;
return; return;
} }
fontCount++; fontCount++;
@ -134,12 +310,7 @@ var Font = (function () {
// If the font is to be ignored, register it like an already loaded font // 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. // to avoid the cost of waiting for it be be loaded by the platform.
if (properties.ignore || kDisableFonts) { if (properties.ignore || kDisableFonts) {
Fonts[name] = { Fonts.blacklistFont(name);
data: file,
loading: false,
properties: {},
cache: Object.create(null)
}
return; return;
} }
@ -165,13 +336,8 @@ var Font = (function () {
warn("Font " + properties.type + " is not supported"); warn("Font " + properties.type + " is not supported");
break; break;
} }
this.data = data;
Fonts[name] = { Fonts.registerFont(name, data, properties);
data: data,
properties: properties,
loading: true,
cache: Object.create(null)
};
}; };
function stringToArray(str) { function stringToArray(str) {
@ -221,6 +387,9 @@ var Font = (function () {
// offset // offset
var offset = offsets.virtualOffset; var offset = offsets.virtualOffset;
// length
var length = data.length;
// Per spec tables must be 4-bytes align so add padding as needed // Per spec tables must be 4-bytes align so add padding as needed
while (data.length & 3) while (data.length & 3)
data.push(0x00); data.push(0x00);
@ -228,16 +397,10 @@ var Font = (function () {
while (offsets.virtualOffset & 3) while (offsets.virtualOffset & 3)
offsets.virtualOffset++; offsets.virtualOffset++;
// length
var length = data.length;
// checksum // checksum
var checksum = tag.charCodeAt(0) + var checksum = 0;
tag.charCodeAt(1) + for (var i = 0; i < length; i+=4)
tag.charCodeAt(2) + checksum += FontsUtils.bytesToInteger([data[i], data[i+1], data[i+2], data[i+3]]);
tag.charCodeAt(3) +
offset +
length;
var tableEntry = tag + string32(checksum) + string32(offset) + string32(length); var tableEntry = tag + string32(checksum) + string32(offset) + string32(length);
tableEntry = stringToArray(tableEntry); tableEntry = stringToArray(tableEntry);
@ -271,6 +434,7 @@ var Font = (function () {
}; };
function createCMapTable(glyphs) { function createCMapTable(glyphs) {
glyphs.push({ unicode: 0x0000 });
var ranges = getRanges(glyphs); var ranges = getRanges(glyphs);
var headerSize = (12 * 2 + (ranges.length * 4 * 2)); var headerSize = (12 * 2 + (ranges.length * 4 * 2));
@ -304,13 +468,13 @@ var Font = (function () {
var range = ranges[i]; var range = ranges[i];
var start = range[0]; var start = range[0];
var end = range[1]; var end = range[1];
var delta = (((start - 1) - bias) ^ 0xffff) + 1; var delta = (((start - 1) - bias) ^ 0xffff);
bias += (end - start + 1); bias += (end - start + 1);
startCount += string16(start); startCount += string16(start);
endCount += string16(end); endCount += string16(end);
idDeltas += string16(delta); idDeltas += string16(delta);
idRangeOffsets += string16(0); idRangeOffsets += string16(0);
for (var j = 0; j < range.length; j++) for (var j = 0; j < range.length; j++)
glyphsIds += String.fromCharCode(range[j]); glyphsIds += String.fromCharCode(range[j]);
@ -326,11 +490,43 @@ var Font = (function () {
}; };
function createOS2Table(properties) { 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 return "\x00\x03" + // version
"\x02\x24" + // xAvgCharWidth "\x02\x24" + // xAvgCharWidth
"\x01\xF4" + // usWeightClass "\x01\xF4" + // usWeightClass
"\x00\x05" + // usWidthClass "\x00\x05" + // usWidthClass
"\x00\x00" + // fstype "\x00\x02" + // fstype
"\x02\x8A" + // ySubscriptXSize "\x02\x8A" + // ySubscriptXSize
"\x02\xBB" + // ySubscriptYSize "\x02\xBB" + // ySubscriptYSize
"\x00\x00" + // ySubscriptXOffset "\x00\x00" + // ySubscriptXOffset
@ -342,41 +538,41 @@ var Font = (function () {
"\x00\x31" + // yStrikeOutSize "\x00\x31" + // yStrikeOutSize
"\x01\x02" + // yStrikeOutPosition "\x01\x02" + // yStrikeOutPosition
"\x00\x00" + // sFamilyClass "\x00\x00" + // sFamilyClass
"\x02\x00\x06\x03\x00\x00\x00\x00\x00\x00" + // Panose "\x00\x00\x06" + String.fromCharCode(properties.fixedPitch ? 0x09 : 0x00) +
"\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 0-31) "\x00\x00\x00\x00\x00\x00" + // Panose
"\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 32-63) string32(ulUnicodeRange1) + // ulUnicodeRange1 (Bits 0-31)
"\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 64-95) string32(ulUnicodeRange2) + // ulUnicodeRange2 (Bits 32-63)
"\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 96-127) string32(ulUnicodeRange3) + // ulUnicodeRange3 (Bits 64-95)
string32(ulUnicodeRange4) + // ulUnicodeRange4 (Bits 96-127)
"\x2A\x32\x31\x2A" + // achVendID "\x2A\x32\x31\x2A" + // achVendID
"\x00\x20" + // fsSelection string16(properties.italicAngle ? 1 : 0) + // fsSelection
"\x00\x2D" + // usFirstCharIndex string16(firstCharIndex || properties.firstChar) + // usFirstCharIndex
"\x00\x7A" + // usLastCharIndex string16(lastCharIndex || properties.lastChar) + // usLastCharIndex
"\x00\x03" + // sTypoAscender string16(properties.ascent) + // sTypoAscender
"\x00\x20" + // sTypeDescender string16(properties.descent) + // sTypoDescender
"\x00\x38" + // sTypoLineGap "\x00\x64" + // sTypoLineGap (7%-10% of the unitsPerEM value)
string16(properties.ascent) + // usWinAscent string16(properties.ascent) + // usWinAscent
string16(properties.descent) + // usWinDescent string16(-properties.descent) + // usWinDescent
"\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31) "\x00\x00\x00\x00" + // ulCodePageRange1 (Bits 0-31)
"\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63) "\x00\x00\x00\x00" + // ulCodePageRange2 (Bits 32-63)
string16(properties.xHeight) + // sxHeight string16(properties.xHeight) + // sxHeight
string16(properties.capHeight) + // sCapHeight string16(properties.capHeight) + // sCapHeight
"\x00\x01" + // usDefaultChar string16(0) + // usDefaultChar
"\x00\xCD" + // usBreakChar string16(firstCharIndex || properties.firstChar) + // usBreakChar
"\x00\x02"; // usMaxContext "\x00\x03"; // usMaxContext
}; };
function createPostTable(properties) { function createPostTable(properties) {
TODO("Fill with real values from the font dict"); var angle = Math.floor(properties.italicAngle * (Math.pow(2, 16)));
return "\x00\x03\x00\x00" + // Version number
return "\x00\x03\x00\x00" + // Version number string32(angle) + // italicAngle
string32(properties.italicAngle) + // italicAngle "\x00\x00" + // underlinePosition
"\x00\x00" + // underlinePosition "\x00\x00" + // underlineThickness
"\x00\x00" + // underlineThickness string32(properties.fixedPitch) + // isFixedPitch
"\x00\x00\x00\x00" + // isFixedPitch "\x00\x00\x00\x00" + // minMemType42
"\x00\x00\x00\x00" + // minMemType42 "\x00\x00\x00\x00" + // maxMemType42
"\x00\x00\x00\x00" + // maxMemType42 "\x00\x00\x00\x00" + // minMemType1
"\x00\x00\x00\x00" + // minMemType1 "\x00\x00\x00\x00"; // maxMemType1
"\x00\x00\x00\x00"; // maxMemType1
}; };
constructor.prototype = { constructor.prototype = {
@ -604,44 +800,75 @@ var Font = (function () {
var otf = new Uint8Array(kMaxFontFileSize); var otf = new Uint8Array(kMaxFontFileSize);
function createNameTable(name) { function createNameTable(name) {
var names = [ // All the strings of the name table should be an odd number of bytes
"See original licence", // Copyright if (name.length % 2)
fontName, // Font family name = name.slice(0, name.length - 1);
"undefined", // Font subfamily (font weight)
"uniqueID", // Unique ID var strings = [
fontName, // Full font name "Original licence", // 0.Copyright
"0.1", // Version name, // 1.Font family
"undefined", // Postscript name "Unknown", // 2.Font subfamily (font weight)
"undefined", // Trademark "uniqueID", // 3.Unique ID
"undefined", // Manufacturer name, // 4.Full font name
"undefined" // Designer "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 = var nameTable =
"\x00\x00" + // format "\x00\x00" + // format
"\x00\x0A" + // Number of names Record string16(namesRecordCount) + // Number of names Record
"\x00\x7E"; // Storage string16(namesRecordCount * 12 + 6); // Storage
// Build the name records field // Build the name records field
var strOffset = 0; var strOffset = 0;
for (var i = 0; i < names.length; i++) { for (var i = 0; i < platforms.length; i++) {
var str = names[i]; var strs = names[i];
for (var j = 0; j < strs.length; j++) {
var nameRecord = var str = strs[j];
"\x00\x01" + // platform ID var nameRecord =
"\x00\x00" + // encoding ID platforms[i] + // platform ID
"\x00\x00" + // language ID encodings[i] + // encoding ID
"\x00\x00" + // name ID languages[i] + // language ID
string16(str.length) + string16(i) + // name ID
string16(strOffset); string16(str.length) +
nameTable += nameRecord; string16(strOffset);
nameTable += nameRecord;
strOffset += str.length; strOffset += str.length;
}
} }
nameTable += names.join(""); nameTable += strings.join("") + stringsUnicode.join("");
return nameTable; 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 // Required Tables
var CFF = var CFF =
@ -672,30 +899,31 @@ var Font = (function () {
createTableEntry(otf, offsets, "CFF ", CFF); createTableEntry(otf, offsets, "CFF ", CFF);
/** OS/2 */ /** OS/2 */
var charstrings = font.charstrings;
properties.fixedPitch = isFixedPitch(charstrings);
OS2 = stringToArray(createOS2Table(properties)); OS2 = stringToArray(createOS2Table(properties));
createTableEntry(otf, offsets, "OS/2", OS2); createTableEntry(otf, offsets, "OS/2", OS2);
/** CMAP */ /** CMAP */
var charstrings = font.charstrings; cmap = createCMapTable(charstrings.slice());
cmap = createCMapTable(charstrings);
createTableEntry(otf, offsets, "cmap", cmap); createTableEntry(otf, offsets, "cmap", cmap);
/** HEAD */ /** HEAD */
head = stringToArray( head = stringToArray(
"\x00\x01\x00\x00" + // Version number "\x00\x01\x00\x00" + // Version number
"\x00\x00\x50\x00" + // fontRevision "\x00\x00\x10\x00" + // fontRevision
"\x00\x00\x00\x00" + // checksumAdjustement "\x00\x00\x00\x00" + // checksumAdjustement
"\x5F\x0F\x3C\xF5" + // magicNumber "\x5F\x0F\x3C\xF5" + // magicNumber
"\x00\x00" + // Flags "\x00\x00" + // Flags
"\x03\xE8" + // unitsPerEM (defaulting to 1000) "\x03\xE8" + // unitsPerEM (defaulting to 1000)
"\x00\x00\x00\x00\x00\x00\x00\x00" + // creation date "\x00\x00\x00\x00\x9e\x0b\x7e\x27" + // creation date
"\x00\x00\x00\x00\x00\x00\x00\x00" + // modifification date "\x00\x00\x00\x00\x9e\x0b\x7e\x27" + // modifification date
"\x00\x00" + // xMin "\x00\x00" + // xMin
"\x00\x00" + // yMin string16(properties.descent) + // yMin
"\x00\x00" + // xMax "\x0F\xFF" + // xMax
"\x00\x00" + // yMax string16(properties.ascent) + // yMax
"\x00\x00" + // macStyle string16(properties.italicAngle ? 2 : 0) + // macStyle
"\x00\x00" + // lowestRecPPEM "\x00\x11" + // lowestRecPPEM
"\x00\x00" + // fontDirectionHint "\x00\x00" + // fontDirectionHint
"\x00\x00" + // indexToLocFormat "\x00\x00" + // indexToLocFormat
"\x00\x00" // glyphDataFormat "\x00\x00" // glyphDataFormat
@ -705,22 +933,22 @@ var Font = (function () {
/** HHEA */ /** HHEA */
hhea = stringToArray( hhea = stringToArray(
"\x00\x01\x00\x00" + // Version number "\x00\x01\x00\x00" + // Version number
"\x00\x00" + // Typographic Ascent string16(properties.ascent) + // Typographic Ascent
"\x00\x00" + // Typographic Descent string16(properties.descent) + // Typographic Descent
"\x00\x00" + // Line Gap "\x00\x00" + // Line Gap
"\xFF\xFF" + // advanceWidthMax "\xFF\xFF" + // advanceWidthMax
"\x00\x00" + // minLeftSidebearing "\x00\x00" + // minLeftSidebearing
"\x00\x00" + // minRightSidebearing "\x00\x00" + // minRightSidebearing
"\x00\x00" + // xMaxExtent "\x00\x00" + // xMaxExtent
"\x00\x00" + // caretSlopeRise string16(properties.capHeight) + // caretSlopeRise
"\x00\x00" + // caretSlopeRun string16(Math.tan(properties.italicAngle) * properties.xHeight) + // caretSlopeRun
"\x00\x00" + // caretOffset "\x00\x00" + // caretOffset
"\x00\x00" + // -reserved- "\x00\x00" + // -reserved-
"\x00\x00" + // -reserved- "\x00\x00" + // -reserved-
"\x00\x00" + // -reserved- "\x00\x00" + // -reserved-
"\x00\x00" + // -reserved- "\x00\x00" + // -reserved-
"\x00\x00" + // metricDataFormat "\x00\x00" + // metricDataFormat
string16(charstrings.length) string16(charstrings.length + 1) // Number of HMetrics
); );
createTableEntry(otf, offsets, "hhea", hhea); 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 * while Windows use this data. So be careful if you hack on Linux and
* have to touch the 'hmtx' table * have to touch the 'hmtx' table
*/ */
hmtx = "\x01\xF4\x00\x00"; // Fake .notdef hmtx = "\x00\x00\x00\x00"; // Fake .notdef
var width = 0, lsb = 0;
for (var i = 0; i < charstrings.length; i++) { for (var i = 0; i < charstrings.length; i++) {
width = charstrings[i].charstring[1]; hmtx += string16(charstrings[i].width) + string16(0);
hmtx += string16(width) + string16(lsb);
} }
hmtx = stringToArray(hmtx); hmtx = stringToArray(hmtx);
createTableEntry(otf, offsets, "hmtx", hmtx); createTableEntry(otf, offsets, "hmtx", hmtx);
/** MAXP */ /** MAXP */
maxp = "\x00\x00\x50\x00" + // Version number 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); maxp = stringToArray(maxp);
createTableEntry(otf, offsets, "maxp", maxp); createTableEntry(otf, offsets, "maxp", maxp);
/** NAME */ /** NAME */
name = stringToArray(createNameTable(name)); name = stringToArray(createNameTable(fontName));
createTableEntry(otf, offsets, "name", name); createTableEntry(otf, offsets, "name", name);
/** POST */ /** POST */
@ -778,9 +1004,18 @@ var Font = (function () {
}); });
}, },
bindDOM: function font_bindDom(data) { bindDOM: function font_bindDom(data, callback) {
var fontName = this.name; 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 */ /** Hack begin */
// Actually there is not event when a font has finished downloading so // 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 // 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 // For some reasons the font has not loaded, so mark it loaded for the
// page to proceed but cry // page to proceed but cry
if ((Date.now() - this.start) >= kMaxWaitForFontFace) { if (textWidth == ctx.measureText(testString).width) {
window.clearInterval(interval); if ((Date.now() - this.start) < kMaxWaitForFontFace) {
Fonts[fontName].loading = false; return;
warn("Is " + fontName + " loaded?"); } else {
this.start = 0; warn("Is " + fontName + " loaded?");
} else if (textWidth != ctx.measureText(testString).width) { }
window.clearInterval(interval); }
Fonts[fontName].loading = false;
this.start = 0; window.clearInterval(interval);
Fonts.lookup(fontName).loading = false;
this.start = 0;
if (callback) {
callback();
} }
}, 30, this); }, 30, this);
@ -839,7 +1078,7 @@ var FontsUtils = {
bytes.set([value]); bytes.set([value]);
return bytes[0]; return bytes[0];
} else if (bytesCount == 2) { } else if (bytesCount == 2) {
bytes.set([value >> 8, value]); bytes.set([value >> 8, value & 0xff]);
return [bytes[0], bytes[1]]; return [bytes[0], bytes[1]];
} else if (bytesCount == 4) { } else if (bytesCount == 4) {
bytes.set([value >> 24, value >> 16, value >> 8, value]); bytes.set([value >> 24, value >> 16, value >> 8, value]);
@ -980,16 +1219,8 @@ var Type1Parser = function() {
"12": "div", "12": "div",
// callothersubr is a mechanism to make calls on the postscript // callothersubr is a mechanism to make calls on the postscript
// interpreter. // interpreter, this is not supported by Type2 charstring but hopefully
// TODO When decodeCharstring encounter such a command it should // most of the default commands can be ignored safely.
// 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[]
"16": "callothersubr", "16": "callothersubr",
"17": "pop", "17": "pop",
@ -1009,8 +1240,13 @@ var Type1Parser = function() {
"31": "hvcurveto" "31": "hvcurveto"
}; };
var kEscapeCommand = 12;
function decodeCharString(array) { function decodeCharString(array) {
var charString = []; var charstring = [];
var lsb = 0;
var width = 0;
var used = false;
var value = ""; var value = "";
var count = array.length; var count = array.length;
@ -1019,10 +1255,48 @@ var Type1Parser = function() {
if (value < 32) { if (value < 32) {
var command = null; var command = null;
if (value == 12) { if (value == kEscapeCommand) {
var escape = array[++i]; 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]; command = charStringDictionary["12"][escape];
} else { } 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]; command = charStringDictionary[value];
} }
@ -1044,16 +1318,14 @@ var Type1Parser = function() {
} else if (value <= 254) { } else if (value <= 254) {
value = -((value - 251) * 256) - parseInt(array[++i]) - 108; value = -((value - 251) * 256) - parseInt(array[++i]) - 108;
} else { } else {
var byte = array[++i]; value = (array[++i] & 0xff) << 24 | (array[++i] & 0xff) << 16 |
var high = (byte >> 1); (array[++i] & 0xff) << 8 | (array[++i] & 0xff) << 0;
value = (byte - high) << 24 | array[++i] << 16 |
array[++i] << 8 | array[++i];
} }
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); length = parseInt(length);
var data = eexecString.slice(i + 3, i + 3 + length); var data = eexecString.slice(i + 3, i + 3 + length);
var encodedSubr = decrypt(data, kCharStringsEncryptionKey, 4); var encodedSubr = decrypt(data, kCharStringsEncryptionKey, 4);
var subr = decodeCharString(encodedSubr); var str = decodeCharString(encodedSubr);
subrs.push(subr); subrs.push(str.charstring);
i += 3 + length; i += 3 + length;
} else if (inGlyphs && c == 0x52) { } else if (inGlyphs && c == 0x52) {
length = parseInt(length); length = parseInt(length);
var data = eexecString.slice(i + 3, i + 3 + length); var data = eexecString.slice(i + 3, i + 3 + length);
var encodedCharstring = decrypt(data, kCharStringsEncryptionKey, 4); var encodedCharstring = decrypt(data, kCharStringsEncryptionKey, 4);
var subr = decodeCharString(encodedCharstring); var str = decodeCharString(encodedCharstring);
glyphs.push({ glyphs.push({
glyph: glyph, glyph: glyph,
data: subr data: str.charstring,
lsb: str.lsb,
width: str.width
}); });
i += 3 + length; i += 3 + length;
} else if (inGlyphs && c == 0x2F) { } else if (inGlyphs && c == 0x2F) {
@ -1254,16 +1528,18 @@ CFF.prototype = {
var charstrings = []; var charstrings = [];
for (var i = 0; i < glyphs.length; i++) { for (var i = 0; i < glyphs.length; i++) {
var glyph = glyphs[i].glyph; var glyph = glyphs[i];
var unicode = GlyphsUnicode[glyph]; var unicode = GlyphsUnicode[glyph.glyph];
if (!unicode) { if (!unicode) {
if (glyph != ".notdef") if (glyph.glyph != ".notdef")
warn(glyph + " does not have an entry in the glyphs unicode dictionary"); warn(glyph + " does not have an entry in the glyphs unicode dictionary");
} else { } else {
charstrings.push({ charstrings.push({
glyph: glyph, glyph: glyph,
unicode: unicode, unicode: unicode,
charstring: glyphs[i].data charstring: glyph.data,
width: glyph.width,
lsb: glyph.lsb
}); });
} }
}; };
@ -1305,46 +1581,11 @@ CFF.prototype = {
var i = 0; var i = 0;
while (true) { while (true) {
var obj = charstring[i]; var obj = charstring[i];
if (obj == null) if (obj == undefined) {
return []; error("unknow charstring command for " + i + " in " + charstring);
}
if (obj.charAt) { if (obj.charAt) {
switch (obj) { 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 "endchar":
case "return": case "return":
// CharString is ready to be re-encode to commands number at this point // CharString is ready to be re-encode to commands number at this point
@ -1356,7 +1597,7 @@ CFF.prototype = {
} else if (command.charAt) { } else if (command.charAt) {
var cmd = this.commandsMap[command]; var cmd = this.commandsMap[command];
if (!cmd) if (!cmd)
error(command); error("Unknow command: " + command);
if (IsArray(cmd)) { if (IsArray(cmd)) {
charstring.splice(j, 1, cmd[0], cmd[1]); charstring.splice(j, 1, cmd[0], cmd[1]);
@ -1428,7 +1669,7 @@ CFF.prototype = {
charset.push(bytes[1]); 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 //Top Dict Index
var topDictIndex = [ var topDictIndex = [

View File

@ -181,7 +181,7 @@ span {
width: 200px; width: 200px;
top: 62px; top: 62px;
bottom: 18px; bottom: 18px;
left: -170px; left: -140px;
transition: left 0.25s ease-in-out 1s; transition: left 0.25s ease-in-out 1s;
-moz-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; -webkit-transition: left 0.25s ease-in-out 1s;

View File

@ -27,9 +27,9 @@
<select id="scaleSelect"> <select id="scaleSelect">
<option value="50">50%</option> <option value="50">50%</option>
<option value="75">75%</option> <option value="75">75%</option>
<option value="100" selected="selected">100%</option> <option value="100">100%</option>
<option value="125">125%</option> <option value="125">125%</option>
<option value="150">150%</option> <option value="150" selected="selected">150%</option>
<option value="200">200%</option> <option value="200">200%</option>
</select> </select>
<span class="label">Zoom</span> <span class="label">Zoom</span>

View File

@ -29,11 +29,15 @@ var PDFViewer = {
scale: 1.0, scale: 1.0,
pageWidth: function(page) { 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) { 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: [], lastPagesDrawn: [],
@ -106,10 +110,11 @@ var PDFViewer = {
canvas.id = 'thumbnail' + num; canvas.id = 'thumbnail' + num;
canvas.mozOpaque = true; canvas.mozOpaque = true;
// Canvas dimensions must be specified in CSS pixels. CSS pixels var pageWidth = PDFViewer.pageWidth(page);
// are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi. var pageHeight = PDFViewer.pageHeight(page);
canvas.width = 104; var thumbScale = Math.min(104 / pageWidth, 134 / pageHeight);
canvas.height = 134; canvas.width = pageWidth * thumbScale;
canvas.height = pageHeight * thumbScale;
div.appendChild(canvas); div.appendChild(canvas);
var ctx = canvas.getContext('2d'); var ctx = canvas.getContext('2d');
@ -175,8 +180,6 @@ var PDFViewer = {
canvas.id = 'page' + num; canvas.id = 'page' + num;
canvas.mozOpaque = true; 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.width = PDFViewer.pageWidth(page);
canvas.height = PDFViewer.pageHeight(page); canvas.height = PDFViewer.pageHeight(page);
div.appendChild(canvas); div.appendChild(canvas);
@ -216,7 +219,6 @@ var PDFViewer = {
if (PDFViewer.pdf) { if (PDFViewer.pdf) {
for (i = 1; i <= PDFViewer.numberOfPages; i++) { for (i = 1; i <= PDFViewer.numberOfPages; i++) {
PDFViewer.createThumbnail(i);
PDFViewer.createPage(i); PDFViewer.createPage(i);
} }
} }
@ -249,7 +251,10 @@ var PDFViewer = {
PDFViewer.pageNumber = num; PDFViewer.pageNumber = num;
PDFViewer.pageNumberInput.value = PDFViewer.pageNumber; PDFViewer.pageNumberInput.value = PDFViewer.pageNumber;
PDFViewer.willJumpToPage = true; 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; document.location.hash = PDFViewer.pageNumber;
PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ? 'disabled' : ''; PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ? 'disabled' : '';
@ -272,6 +277,12 @@ var PDFViewer = {
openURL: function(url) { openURL: function(url) {
PDFViewer.url = url; PDFViewer.url = url;
document.title = url; document.title = url;
if (this.thumbsLoadingInterval) {
// cancel thumbs loading operations
clearInterval(this.thumbsLoadingInterval);
this.thumbsLoadingInterval = null;
}
var req = new XMLHttpRequest(); var req = new XMLHttpRequest();
req.open('GET', url); req.open('GET', url);
@ -288,7 +299,9 @@ var PDFViewer = {
req.send(null); req.send(null);
}, },
thumbsLoadingInterval: null,
readPDF: function(data) { readPDF: function(data) {
while (PDFViewer.element.hasChildNodes()) { while (PDFViewer.element.hasChildNodes()) {
PDFViewer.element.removeChild(PDFViewer.element.firstChild); PDFViewer.element.removeChild(PDFViewer.element.firstChild);
@ -310,12 +323,22 @@ var PDFViewer = {
PDFViewer.drawPage(1); PDFViewer.drawPage(1);
document.location.hash = 1; document.location.hash = 1;
setTimeout(function() { // slowly loading the thumbs (few per second)
for (var i = 1; i <= PDFViewer.numberOfPages; i++) { // first time we are loading more images than subsequent
PDFViewer.createThumbnail(i); var currentPageIndex = 1, imagesToLoad = 15;
PDFViewer.drawThumbnail(i); 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' : ''; PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ? 'disabled' : '';

24
pdf.js
View File

@ -641,7 +641,7 @@ var PredictorStream = (function() {
var pixBytes = this.pixBytes = (colors * bits + 7) >> 3; var pixBytes = this.pixBytes = (colors * bits + 7) >> 3;
// add an extra pixByte to represent the pixel left of column 0 // add an extra pixByte to represent the pixel left of column 0
var rowBytes = this.rowBytes = (columns * colors * bits + 7) >> 3; var rowBytes = this.rowBytes = (columns * colors * bits + 7) >> 3;
DecodeStream.call(this); DecodeStream.call(this);
return this; return this;
} }
@ -3440,6 +3440,7 @@ var CanvasGraphics = (function() {
if (charset) { if (charset) {
assertWellFormed(IsString(charset), "invalid charset"); assertWellFormed(IsString(charset), "invalid charset");
charset = charset.split("/"); charset = charset.split("/");
charset.shift();
} }
} else if (IsName(encoding)) { } else if (IsName(encoding)) {
var encoding = Encodings[encoding.name]; var encoding = Encodings[encoding.name];
@ -3534,13 +3535,16 @@ var CanvasGraphics = (function() {
type: subType.name, type: subType.name,
encoding: encodingMap, encoding: encodingMap,
charset: charset, charset: charset,
firstChar: fontDict.get("FirstChar"),
lastChar: fontDict.get("LastChar"),
bbox: descriptor.get("FontBBox"), bbox: descriptor.get("FontBBox"),
ascent: descriptor.get("Ascent"), ascent: descriptor.get("Ascent"),
descent: descriptor.get("Descent"), descent: descriptor.get("Descent"),
xHeight: descriptor.get("XHeight"), xHeight: descriptor.get("XHeight"),
capHeight: descriptor.get("CapHeight"), capHeight: descriptor.get("CapHeight"),
flags: descriptor.get("Flags"), flags: descriptor.get("Flags"),
italicAngle: descriptor.get("ItalicAngle") italicAngle: descriptor.get("ItalicAngle"),
fixedPitch: false
}; };
return { return {
@ -3807,18 +3811,22 @@ var CanvasGraphics = (function() {
if (fontDescriptor && fontDescriptor.num) { if (fontDescriptor && fontDescriptor.num) {
var fontDescriptor = this.xref.fetchIfRef(fontDescriptor); var fontDescriptor = this.xref.fetchIfRef(fontDescriptor);
fontName = fontDescriptor.get("FontName").name.replace("+", "_"); fontName = fontDescriptor.get("FontName").name.replace("+", "_");
Fonts.active = fontName;
} }
if (!fontName) { if (!fontName) {
// TODO: fontDescriptor is not available, fallback to default font // TODO: fontDescriptor is not available, fallback to default font
this.current.fontSize = size; fontName = "sans-serif";
this.ctx.font = this.current.fontSize + 'px sans-serif';
return;
} }
this.current.fontName = fontName;
this.current.fontSize = size; 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) { setTextRenderingMode: function(mode) {
TODO("text rendering mode"); TODO("text rendering mode");
@ -3862,7 +3870,7 @@ var CanvasGraphics = (function() {
text = Fonts.charsToUnicode(text); text = Fonts.charsToUnicode(text);
this.ctx.translate(this.current.x, -1 * this.current.y); this.ctx.translate(this.current.x, -1 * this.current.y);
this.ctx.fillText(text, 0, 0); this.ctx.fillText(text, 0, 0);
this.current.x += this.ctx.measureText(text).width; this.current.x += Fonts.measureText(text);
} }
this.ctx.restore(); this.ctx.restore();

View File

@ -4,6 +4,7 @@
<style type="text/css"></style> <style type="text/css"></style>
<script type="text/javascript" src="/pdf.js"></script> <script type="text/javascript" src="/pdf.js"></script>
<script type="text/javascript" src="/fonts.js"></script> <script type="text/javascript" src="/fonts.js"></script>
<script type="text/javascript" src="/crypto.js"></script>
<script type="text/javascript" src="/glyphlist.js"></script> <script type="text/javascript" src="/glyphlist.js"></script>
<script type="application/javascript"> <script type="application/javascript">
var appPath, browser, canvas, currentTask, currentTaskIdx, failure, manifest, numPages, pdfDoc, stdout; var appPath, browser, canvas, currentTask, currentTaskIdx, failure, manifest, numPages, pdfDoc, stdout;
@ -103,11 +104,12 @@ function nextPage() {
} }
try { try {
var pdfToCssUnitsCoef = 96.0 / 72.0;
// using mediaBox for the canvas size // using mediaBox for the canvas size
var wTwips = (currentPage.mediaBox[2] - currentPage.mediaBox[0]); var pageWidth = (currentPage.mediaBox[2] - currentPage.mediaBox[0]);
var hTwips = (currentPage.mediaBox[3] - currentPage.mediaBox[1]); var pageHeight = (currentPage.mediaBox[3] - currentPage.mediaBox[1]);
canvas.width = wTwips * 96.0 / 72.0; canvas.width = pageWidth * pdfToCssUnitsCoef;
canvas.height = hTwips * 96.0 / 72.0; canvas.height = pageHeight * pdfToCssUnitsCoef;
clear(ctx); clear(ctx);
} catch(e) { } catch(e) {
failure = 'page setup: '+ e.toString(); failure = 'page setup: '+ e.toString();

View File

@ -24,10 +24,11 @@ span#info {
} }
#viewer { #viewer {
}
#canvas {
margin: auto; margin: auto;
border: 1px solid black; display: block;
width: 12.75in;
height: 16.5in;
} }
#pageNumber { #pageNumber {

View File

@ -25,9 +25,7 @@
</div> </div>
<div id="viewer"> <div id="viewer">
<!-- Canvas dimensions must be specified in CSS pixels. CSS pixels <canvas id="canvas"></canvas>
are always 96 dpi. 816x1056 is 8.5x11in at 96dpi. -->
<canvas id="canvas" width="816" height="1056" defaultwidth="816" defaultheight="1056"></canvas>
</div> </div>
</body> </body>
</html> </html>

View File

@ -60,12 +60,12 @@ function displayPage(num) {
var t0 = Date.now(); var t0 = Date.now();
var page = pdfDocument.getPage(pageNum = num); var page = pdfDocument.getPage(pageNum = num);
canvas.width = parseInt(canvas.getAttribute("defaultwidth")) * pageScale;
canvas.height = parseInt(canvas.getAttribute("defaultheight")) * pageScale;
// scale canvas by 2 var pdfToCssUnitsCoef = 96.0 / 72.0;
canvas.width = 2 * page.mediaBox[2]; var pageWidth = (page.mediaBox[2] - page.mediaBox[0]);
canvas.hieght = 2 * page.mediaBox[3]; var pageHeight = (page.mediaBox[3] - page.mediaBox[1]);
canvas.width = pageScale * pageWidth * pdfToCssUnitsCoef;
canvas.height = pageScale * pageHeight * pdfToCssUnitsCoef;
var t1 = Date.now(); var t1 = Date.now();
var ctx = canvas.getContext("2d"); var ctx = canvas.getContext("2d");

View File

@ -1,7 +1,10 @@
<html> <html>
<head> <head>
<title>Simple pdf.js page worker viewer</title> <title>Simple pdf.js page worker viewer</title>
<script type="text/javascript" src="worker_client.js"></script> <script type="text/javascript" src="fonts.js"></script>
<script type="text/javascript" src="glyphlist.js"></script>
<script type="text/javascript" src="pdf.js"></script>
<script type="text/javascript" src="worker/client.js"></script>
<script> <script>
@ -36,10 +39,7 @@ window.onload = function() {
</div> </div>
<div id="viewer"> <div id="viewer">
<!-- Canvas dimensions must be specified in CSS pixels. CSS pixels <canvas id="canvas"></canvas>
are always 96 dpi. 816x1056 is 8.5x11in at 96dpi. -->
<!-- We're rendering here at 1.5x scale. -->
<canvas id="canvas" width="1224" height="1584"></canvas>
</div> </div>
</body> </body>
</html> </html>

View File

@ -119,7 +119,8 @@ function CanvasProxy(width, height) {
"$addCurrentX", "$addCurrentX",
"$saveCurrentX", "$saveCurrentX",
"$restoreCurrentX", "$restoreCurrentX",
"$showText" "$showText",
"$setFont"
]; ];
function buildFuncCall(name) { function buildFuncCall(name) {

View File

@ -18,12 +18,115 @@ if (typeof console.time == "undefined") {
}; };
} }
function FontWorker() {
this.worker = new Worker("worker/font.js");
this.fontsWaiting = 0;
this.fontsWaitingCallbacks = [];
// Listen to the WebWorker for data and call actionHandler on it.
this.worker.onmessage = function(event) {
var data = event.data;
var actionHandler = this.actionHandler
if (data.action in actionHandler) {
actionHandler[data.action].call(this, data.data);
} else {
throw "Unkown action from worker: " + data.action;
}
}.bind(this);
this.$handleFontLoadedCallback = this.handleFontLoadedCallback.bind(this);
}
FontWorker.prototype = {
handleFontLoadedCallback: function() {
// Decrease the number of fonts wainting to be loaded.
this.fontsWaiting--;
// If all fonts are available now, then call all the callbacks.
if (this.fontsWaiting == 0) {
var callbacks = this.fontsWaitingCallbacks;
for (var i = 0; i < callbacks.length; i++) {
callbacks[i]();
}
this.fontsWaitingCallbacks.length = 0;
}
},
actionHandler: {
"log": function(data) {
console.log.apply(console, data);
},
"fonts": function(data) {
// console.log("got processed fonts from worker", Object.keys(data));
for (name in data) {
// Update the encoding property.
var font = Fonts.lookup(name);
font.properties = {
encoding: data[name].encoding
}
// Call `Font.prototype.bindDOM` to make the font get loaded on the page.
Font.prototype.bindDOM.call(
font,
data[name].str,
// IsLoadedCallback.
this.$handleFontLoadedCallback
);
}
}
},
ensureFonts: function(data, callback) {
var font;
var notLoaded = [];
for (var i = 0; i < data.length; i++) {
font = data[i];
if (Fonts[font.name]) {
continue;
}
// Register the font but don't pass in any real data. The idea is to
// store as less data as possible to reduce memory usage.
Fonts.registerFont(font.name, Object.create(null), Object.create(null));
// Mark this font to be handled later.
notLoaded.push(font);
// Increate the number of fonts to wait for.
this.fontsWaiting++;
}
console.time("ensureFonts");
// If there are fonts, that need to get loaded, tell the FontWorker to get
// started and push the callback on the waiting-callback-stack.
if (notLoaded.length != 0) {
console.log("fonts -> FontWorker");
// Send the worker the fonts to work on.
this.worker.postMessage({
action: "fonts",
data: notLoaded
});
if (callback) {
this.fontsWaitingCallbacks.push(callback);
}
}
// All fonts are present? Well, then just call the callback if there is one.
else {
if (callback) {
callback();
}
}
},
}
function WorkerPDFDoc(canvas) { function WorkerPDFDoc(canvas) {
var timer = null var timer = null
this.ctx = canvas.getContext("2d"); this.ctx = canvas.getContext("2d");
this.canvas = canvas; this.canvas = canvas;
this.worker = new Worker('pdf_worker.js'); this.worker = new Worker('worker/pdf.js');
this.fontWorker = new FontWorker();
this.waitingForFonts = false;
this.waitingForFontsCallback = [];
this.numPage = 1; this.numPage = 1;
this.numPages = null; this.numPages = null;
@ -56,6 +159,7 @@ function WorkerPDFDoc(canvas) {
}, },
"$showText": function(y, text) { "$showText": function(y, text) {
text = Fonts.charsToUnicode(text);
this.translate(currentX, -1 * y); this.translate(currentX, -1 * y);
this.fillText(text, 0, 0); this.fillText(text, 0, 0);
currentX += this.measureText(text).width; currentX += this.measureText(text).width;
@ -136,6 +240,11 @@ function WorkerPDFDoc(canvas) {
throw "Pattern not found"; throw "Pattern not found";
} }
this.strokeStyle = pattern; this.strokeStyle = pattern;
},
"$setFont": function(name, size) {
this.font = size + 'px "' + name + '"';
Fonts.setActive(name, size);
} }
} }
@ -187,6 +296,25 @@ function WorkerPDFDoc(canvas) {
div.setAttribute("style", style); div.setAttribute("style", style);
document.body.appendChild(div); document.body.appendChild(div);
}, },
"setup_page": function(data) {
var size = data.split(",");
var canvas = this.canvas, ctx = this.ctx;
canvas.width = parseInt(size[0]);
canvas.height = parseInt(size[1]);
},
"fonts": function(data) {
this.waitingForFonts = true;
this.fontWorker.ensureFonts(data, function() {
this.waitingForFonts = false;
var callbacks = this.waitingForFontsCallback;
for (var i = 0; i < callbacks.length; i++) {
callbacks[i]();
}
this.waitingForFontsCallback.length = 0;
}.bind(this));
},
"jpeg_stream": function(data) { "jpeg_stream": function(data) {
var img = new Image(); var img = new Image();
@ -207,11 +335,9 @@ function WorkerPDFDoc(canvas) {
canvasList[id] = newCanvas; canvasList[id] = newCanvas;
} }
// There might be fonts that need to get loaded. Shedule the var renderData = function() {
// rendering at the end of the event queue ensures this.
setTimeout(function() {
if (id == 0) { if (id == 0) {
console.time("canvas rendering"); console.time("main canvas rendering");
var ctx = this.ctx; var ctx = this.ctx;
ctx.save(); ctx.save();
ctx.fillStyle = "rgb(255, 255, 255)"; ctx.fillStyle = "rgb(255, 255, 255)";
@ -219,12 +345,27 @@ function WorkerPDFDoc(canvas) {
ctx.restore(); ctx.restore();
} }
renderProxyCanvas(canvasList[id], cmdQueue); renderProxyCanvas(canvasList[id], cmdQueue);
if (id == 0) console.timeEnd("canvas rendering") if (id == 0) {
}, 0, this); console.timeEnd("main canvas rendering");
console.timeEnd(">>> total page display time:");
}
}.bind(this);
if (this.waitingForFonts) {
if (id == 0) {
console.log("want to render, but not all fonts are there", id);
this.waitingForFontsCallback.push(renderData);
} else {
// console.log("assume canvas doesn't have fonts", id);
renderData();
}
} else {
renderData();
}
} }
} }
// List to the WebWorker for data and call actionHandler on it. // Listen to the WebWorker for data and call actionHandler on it.
this.worker.onmessage = function(event) { this.worker.onmessage = function(event) {
var data = event.data; var data = event.data;
if (data.action in actionHandler) { if (data.action in actionHandler) {
@ -232,7 +373,7 @@ function WorkerPDFDoc(canvas) {
} else { } else {
throw "Unkown action from worker: " + data.action; throw "Unkown action from worker: " + data.action;
} }
} }.bind(this)
} }
WorkerPDFDoc.prototype.open = function(url, callback) { WorkerPDFDoc.prototype.open = function(url, callback) {
@ -255,6 +396,8 @@ WorkerPDFDoc.prototype.open = function(url, callback) {
WorkerPDFDoc.prototype.showPage = function(numPage) { WorkerPDFDoc.prototype.showPage = function(numPage) {
this.numPage = parseInt(numPage); this.numPage = parseInt(numPage);
console.log("=== start rendering page " + numPage + " ===");
console.time(">>> total page display time:");
this.worker.postMessage(numPage); this.worker.postMessage(numPage);
if (this.onChangePage) { if (this.onChangePage) {
this.onChangePage(numPage); this.onChangePage(numPage);

27
worker/console.js Normal file
View File

@ -0,0 +1,27 @@
/* -*- 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 consoleTimer = {};
var console = {
log: function log() {
var args = Array.prototype.slice.call(arguments);
postMessage({
action: "log",
data: args
});
},
time: function(name) {
consoleTimer[name] = Date.now();
},
timeEnd: function(name) {
var time = consoleTimer[name];
if (time == null) {
throw "Unkown timer name " + name;
}
this.log("Timer:", name, Date.now() - time);
}
}

65
worker/font.js Normal file
View File

@ -0,0 +1,65 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
"use strict";
importScripts("console.js");
importScripts("../pdf.js");
importScripts("../fonts.js");
importScripts("../glyphlist.js")
function fontDataToString(font) {
// Doing postMessage on objects make them lose their "shape". This adds the
// "shape" for all required objects agains, such that the encoding works as
// expected.
var fontFileDict = new Dict();
fontFileDict.map = font.file.dict.map;
var fontFile = new Stream(font.file.bytes, font.file.start, font.file.end - font.file.start, fontFileDict);
font.file = new FlateStream(fontFile);
// This will encode the font.
var fontObj = new Font(font.name, font.file, font.properties);
// Create string that is used for css later.
var str = "";
var data = fontObj.data;
var length = data.length;
for (var j = 0; j < length; j++)
str += String.fromCharCode(data[j]);
return {
str: str,
encoding: font.properties.encoding
}
}
/**
* Functions to handle data sent by the MainThread.
*/
var actionHandler = {
"fonts": function(data) {
var fontData;
var result = {};
for (var i = 0; i < data.length; i++) {
fontData = data[i];
result[fontData.name] = fontDataToString(fontData);
}
postMessage({
action: "fonts",
data: result
})
},
}
// Listen to the MainThread for data and call actionHandler on it.
this.onmessage = function(event) {
var data = event.data;
if (data.action in actionHandler) {
actionHandler[data.action].call(this, data.data);
} else {
throw "Unkown action from worker: " + data.action;
}
}

View File

@ -27,10 +27,12 @@ var console = {
} }
// //
importScripts("canvas_proxy.js"); importScripts("console.js")
importScripts("pdf.js"); importScripts("canvas.js");
importScripts("fonts.js"); importScripts("../pdf.js");
importScripts("glyphlist.js") importScripts("../fonts.js");
importScripts("../crypto.js");
importScripts("../glyphlist.js")
// Use the JpegStreamProxy proxy. // Use the JpegStreamProxy proxy.
JpegStream = JpegStreamProxy; JpegStream = JpegStreamProxy;
@ -58,6 +60,18 @@ onmessage = function(event) {
// Let's try to render the first page... // Let's try to render the first page...
var page = pdfDocument.getPage(parseInt(data)); var page = pdfDocument.getPage(parseInt(data));
var pdfToCssUnitsCoef = 96.0 / 72.0;
var pageWidth = (page.mediaBox[2] - page.mediaBox[0]) * pdfToCssUnitsCoef;
var pageHeight = (page.mediaBox[3] - page.mediaBox[1]) * pdfToCssUnitsCoef;
postMessage({
action: "setup_page",
data: pageWidth + "," + pageHeight
});
// Set canvas size.
canvas.width = pageWidth;
canvas.height = pageHeight;
// page.compile will collect all fonts for us, once we have loaded them // page.compile will collect all fonts for us, once we have loaded them
// we can trigger the actual page rendering with page.display // we can trigger the actual page rendering with page.display
var fonts = []; var fonts = [];
@ -65,21 +79,14 @@ onmessage = function(event) {
page.compile(gfx, fonts); page.compile(gfx, fonts);
console.timeEnd("compile"); console.timeEnd("compile");
// Send fonts to the main thread.
console.time("fonts"); console.time("fonts");
// Inspect fonts and translate the missing one. postMessage({
var count = fonts.length; action: "fonts",
for (var i = 0; i < count; i++) { data: fonts
var font = fonts[i]; });
if (Fonts[font.name]) {
fontsReady = fontsReady && !Fonts[font.name].loading;
continue;
}
// This "builds" the font and sents it over to the main thread.
new Font(font.name, font.file, font.properties);
}
console.timeEnd("fonts"); console.timeEnd("fonts");
console.time("display"); console.time("display");
page.display(gfx); page.display(gfx);
canvas.flush(); canvas.flush();