Merge branch 'master' into colorspace
This commit is contained in:
commit
03a1a0369b
12
README
12
README
@ -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
27
README.md
Normal 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
|
10
crypto.js
10
crypto.js
@ -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;
|
||||||
|
198
fonts.js
198
fonts.js
@ -26,12 +26,15 @@ 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 kScalePrecision = 40;
|
||||||
var Fonts = {
|
var Fonts = {
|
||||||
_active: null,
|
_active: null,
|
||||||
|
|
||||||
@ -39,8 +42,9 @@ var Fonts = {
|
|||||||
return this._active;
|
return this._active;
|
||||||
},
|
},
|
||||||
|
|
||||||
set active(name) {
|
setActive: function fonts_setActive(name, size) {
|
||||||
this._active = this[name];
|
this._active = this[name];
|
||||||
|
this.ctx.font = (size * kScalePrecision) + 'px "' + name + '"';
|
||||||
},
|
},
|
||||||
|
|
||||||
charsToUnicode: function fonts_chars2Unicode(chars) {
|
charsToUnicode: function fonts_chars2Unicode(chars) {
|
||||||
@ -64,8 +68,8 @@ var Fonts = {
|
|||||||
var unicode = encoding[charcode];
|
var unicode = encoding[charcode];
|
||||||
|
|
||||||
// Check if the glyph has already been converted
|
// Check if the glyph has already been converted
|
||||||
if (unicode instanceof Name)
|
if (!IsNum(unicode))
|
||||||
unicode = encoding[unicode] = GlyphsUnicode[unicode.name];
|
unicode = encoding[unicode] = GlyphsUnicode[unicode.name];
|
||||||
|
|
||||||
// Handle surrogate pairs
|
// Handle surrogate pairs
|
||||||
if (unicode > 0xFFFF) {
|
if (unicode > 0xFFFF) {
|
||||||
@ -77,6 +81,16 @@ var Fonts = {
|
|||||||
|
|
||||||
// Enter the translated string into the cache
|
// Enter the translated string into the cache
|
||||||
return active.cache[chars] = str;
|
return active.cache[chars] = str;
|
||||||
|
},
|
||||||
|
|
||||||
|
get ctx() {
|
||||||
|
var ctx = document.createElement("canvas").getContext("2d");
|
||||||
|
ctx.scale(1 / kScalePrecision, 1);
|
||||||
|
return shadow(this, "ctx", ctx);
|
||||||
|
},
|
||||||
|
|
||||||
|
measureText: function fonts_measureText(text) {
|
||||||
|
return this.ctx.measureText(text).width / kScalePrecision;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -165,6 +179,7 @@ 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[name] = {
|
||||||
data: data,
|
data: data,
|
||||||
@ -720,7 +735,7 @@ var Font = (function () {
|
|||||||
"\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,18 +745,18 @@ 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;
|
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];
|
var charstring = charstrings[i];
|
||||||
hmtx += string16(width) + string16(lsb);
|
hmtx += string16(charstring.width) + string16(0);
|
||||||
}
|
}
|
||||||
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);
|
||||||
|
|
||||||
@ -778,9 +793,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 +824,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[fontName].loading = false;
|
||||||
|
this.start = 0;
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
}
|
}
|
||||||
}, 30, this);
|
}, 30, this);
|
||||||
|
|
||||||
@ -839,7 +867,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 +1008,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 +1029,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 +1044,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 +1107,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 +1141,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 +1317,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 +1370,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 +1386,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]);
|
||||||
|
48
pdf.js
48
pdf.js
@ -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;
|
||||||
}
|
}
|
||||||
@ -816,7 +816,7 @@ var DecryptStream = (function() {
|
|||||||
DecodeStream.call(this);
|
DecodeStream.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
const chunkSize = 512;
|
var chunkSize = 512;
|
||||||
|
|
||||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
constructor.prototype = Object.create(DecodeStream.prototype);
|
||||||
constructor.prototype.readBlock = function() {
|
constructor.prototype.readBlock = function() {
|
||||||
@ -910,18 +910,18 @@ var Ascii85Stream = (function() {
|
|||||||
|
|
||||||
var CCITTFaxStream = (function() {
|
var CCITTFaxStream = (function() {
|
||||||
|
|
||||||
const ccittEOL = -2;
|
var ccittEOL = -2;
|
||||||
const twoDimPass = 0;
|
var twoDimPass = 0;
|
||||||
const twoDimHoriz = 1;
|
var twoDimHoriz = 1;
|
||||||
const twoDimVert0 = 2;
|
var twoDimVert0 = 2;
|
||||||
const twoDimVertR1 = 3;
|
var twoDimVertR1 = 3;
|
||||||
const twoDimVertL1 = 4;
|
var twoDimVertL1 = 4;
|
||||||
const twoDimVertR2 = 5;
|
var twoDimVertR2 = 5;
|
||||||
const twoDimVertL2 = 6;
|
var twoDimVertL2 = 6;
|
||||||
const twoDimVertR3 = 7;
|
var twoDimVertR3 = 7;
|
||||||
const twoDimVertL3 = 8;
|
var twoDimVertL3 = 8;
|
||||||
|
|
||||||
const twoDimTable = [
|
var twoDimTable = [
|
||||||
[-1, -1], [-1, -1], // 000000x
|
[-1, -1], [-1, -1], // 000000x
|
||||||
[7, twoDimVertL3], // 0000010
|
[7, twoDimVertL3], // 0000010
|
||||||
[7, twoDimVertR3], // 0000011
|
[7, twoDimVertR3], // 0000011
|
||||||
@ -989,7 +989,7 @@ var CCITTFaxStream = (function() {
|
|||||||
[1, twoDimVert0], [1, twoDimVert0]
|
[1, twoDimVert0], [1, twoDimVert0]
|
||||||
];
|
];
|
||||||
|
|
||||||
const whiteTable1 = [
|
var whiteTable1 = [
|
||||||
[-1, -1], // 00000
|
[-1, -1], // 00000
|
||||||
[12, ccittEOL], // 00001
|
[12, ccittEOL], // 00001
|
||||||
[-1, -1], [-1, -1], // 0001x
|
[-1, -1], [-1, -1], // 0001x
|
||||||
@ -1011,7 +1011,7 @@ var CCITTFaxStream = (function() {
|
|||||||
[12, 2560] // 11111
|
[12, 2560] // 11111
|
||||||
];
|
];
|
||||||
|
|
||||||
const whiteTable2 = [
|
var whiteTable2 = [
|
||||||
[-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000000xx
|
[-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000000xx
|
||||||
[8, 29], [8, 29], // 00000010x
|
[8, 29], [8, 29], // 00000010x
|
||||||
[8, 30], [8, 30], // 00000011x
|
[8, 30], [8, 30], // 00000011x
|
||||||
@ -1175,7 +1175,7 @@ var CCITTFaxStream = (function() {
|
|||||||
[4, 7], [4, 7], [4, 7], [4, 7]
|
[4, 7], [4, 7], [4, 7], [4, 7]
|
||||||
];
|
];
|
||||||
|
|
||||||
const blackTable1 = [
|
var blackTable1 = [
|
||||||
[-1, -1], [-1, -1], // 000000000000x
|
[-1, -1], [-1, -1], // 000000000000x
|
||||||
[12, ccittEOL], [12, ccittEOL], // 000000000001x
|
[12, ccittEOL], [12, ccittEOL], // 000000000001x
|
||||||
[-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000001xx
|
[-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000001xx
|
||||||
@ -1236,7 +1236,7 @@ var CCITTFaxStream = (function() {
|
|||||||
[10, 64], [10, 64], [10, 64], [10, 64]
|
[10, 64], [10, 64], [10, 64], [10, 64]
|
||||||
];
|
];
|
||||||
|
|
||||||
const blackTable2 = [
|
var blackTable2 = [
|
||||||
[8, 13], [8, 13], [8, 13], [8, 13], // 00000100xxxx
|
[8, 13], [8, 13], [8, 13], [8, 13], // 00000100xxxx
|
||||||
[8, 13], [8, 13], [8, 13], [8, 13],
|
[8, 13], [8, 13], [8, 13], [8, 13],
|
||||||
[8, 13], [8, 13], [8, 13], [8, 13],
|
[8, 13], [8, 13], [8, 13], [8, 13],
|
||||||
@ -1315,7 +1315,7 @@ var CCITTFaxStream = (function() {
|
|||||||
[7, 12], [7, 12], [7, 12], [7, 12]
|
[7, 12], [7, 12], [7, 12], [7, 12]
|
||||||
];
|
];
|
||||||
|
|
||||||
const blackTable3 = [
|
var blackTable3 = [
|
||||||
[-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000xx
|
[-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000xx
|
||||||
[6, 9], // 000100
|
[6, 9], // 000100
|
||||||
[6, 8], // 000101
|
[6, 8], // 000101
|
||||||
@ -3809,18 +3809,24 @@ 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;
|
Fonts.setActive(fontName, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
this.current.fontSize = size;
|
||||||
this.ctx.font = this.current.fontSize + 'px sans-serif';
|
this.ctx.font = this.current.fontSize + 'px sans-serif';
|
||||||
|
Fonts.setActive("sans-serif", this.current.fontSize);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.current.fontName = fontName;
|
||||||
this.current.fontSize = size;
|
this.current.fontSize = size;
|
||||||
this.ctx.font = this.current.fontSize +'px "' + fontName + '", Symbol';
|
|
||||||
|
this.ctx.font = this.current.fontSize + 'px "' + fontName + '"';
|
||||||
|
if (this.ctx.$setFont) {
|
||||||
|
this.ctx.$setFont(fontName);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
setTextRenderingMode: function(mode) {
|
setTextRenderingMode: function(mode) {
|
||||||
TODO("text rendering mode");
|
TODO("text rendering mode");
|
||||||
@ -3864,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();
|
||||||
|
10
test/resources/browser_manifests/browser_manifest.json.linux
Normal file
10
test/resources/browser_manifests/browser_manifest.json.linux
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name":"firefox7",
|
||||||
|
"path":"/home/sayrer/firefoxen/nightly/firefox"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"chrome14",
|
||||||
|
"path":"/opt/google/chrome/chrome"
|
||||||
|
}
|
||||||
|
]
|
@ -6,5 +6,9 @@
|
|||||||
{
|
{
|
||||||
"name":"firefox6",
|
"name":"firefox6",
|
||||||
"path":"/Users/sayrer/firefoxen/Aurora.app"
|
"path":"/Users/sayrer/firefoxen/Aurora.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"chrome14",
|
||||||
|
"path":"/Applications/Google Chrome.app"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
63
test/test.py
63
test/test.py
@ -2,7 +2,7 @@ import json, platform, os, shutil, sys, subprocess, tempfile, threading, time, u
|
|||||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||||
import SocketServer
|
import SocketServer
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse, parse_qs
|
||||||
|
|
||||||
USAGE_EXAMPLE = "%prog"
|
USAGE_EXAMPLE = "%prog"
|
||||||
|
|
||||||
@ -132,6 +132,11 @@ class PDFTestHandler(BaseHTTPRequestHandler):
|
|||||||
self.send_header('Content-Type', 'text/plain')
|
self.send_header('Content-Type', 'text/plain')
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|
||||||
|
url = urlparse(self.path)
|
||||||
|
if url.path == "/tellMeToQuit":
|
||||||
|
tellAppToQuit(url.path, url.query)
|
||||||
|
return
|
||||||
|
|
||||||
result = json.loads(self.rfile.read(numBytes))
|
result = json.loads(self.rfile.read(numBytes))
|
||||||
browser, id, failure, round, page, snapshot = result['browser'], result['id'], result['failure'], result['round'], result['page'], result['snapshot']
|
browser, id, failure, round, page, snapshot = result['browser'], result['id'], result['failure'], result['round'], result['page'], result['snapshot']
|
||||||
taskResults = State.taskResults[browser][id]
|
taskResults = State.taskResults[browser][id]
|
||||||
@ -156,8 +161,20 @@ class PDFTestHandler(BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
State.done = (0 == State.remaining)
|
State.done = (0 == State.remaining)
|
||||||
|
|
||||||
# this just does Firefox for now
|
# Applescript hack to quit Chrome on Mac
|
||||||
class BrowserCommand():
|
def tellAppToQuit(path, query):
|
||||||
|
if platform.system() != "Darwin":
|
||||||
|
return
|
||||||
|
d = parse_qs(query)
|
||||||
|
path = d['path'][0]
|
||||||
|
cmd = """osascript<<END
|
||||||
|
tell application "%s"
|
||||||
|
quit
|
||||||
|
end tell
|
||||||
|
END""" % path
|
||||||
|
os.system(cmd)
|
||||||
|
|
||||||
|
class BaseBrowserCommand(object):
|
||||||
def __init__(self, browserRecord):
|
def __init__(self, browserRecord):
|
||||||
self.name = browserRecord["name"]
|
self.name = browserRecord["name"]
|
||||||
self.path = browserRecord["path"]
|
self.path = browserRecord["path"]
|
||||||
@ -170,14 +187,9 @@ class BrowserCommand():
|
|||||||
if not os.path.exists(self.path):
|
if not os.path.exists(self.path):
|
||||||
throw("Path to browser '%s' does not exist." % self.path)
|
throw("Path to browser '%s' does not exist." % self.path)
|
||||||
|
|
||||||
def _fixupMacPath(self):
|
|
||||||
self.path = os.path.join(self.path, "Contents", "MacOS", "firefox-bin")
|
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
self.tempDir = tempfile.mkdtemp()
|
self.tempDir = tempfile.mkdtemp()
|
||||||
self.profileDir = os.path.join(self.tempDir, "profile")
|
self.profileDir = os.path.join(self.tempDir, "profile")
|
||||||
shutil.copytree(os.path.join(DOC_ROOT, "test", "resources", "firefox"),
|
|
||||||
self.profileDir)
|
|
||||||
|
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
# If the browser is still running, wait up to ten seconds for it to quit
|
# If the browser is still running, wait up to ten seconds for it to quit
|
||||||
@ -194,6 +206,18 @@ class BrowserCommand():
|
|||||||
if self.tempDir is not None and os.path.exists(self.tempDir):
|
if self.tempDir is not None and os.path.exists(self.tempDir):
|
||||||
shutil.rmtree(self.tempDir)
|
shutil.rmtree(self.tempDir)
|
||||||
|
|
||||||
|
def start(self, url):
|
||||||
|
raise Exception("Can't start BaseBrowserCommand")
|
||||||
|
|
||||||
|
class FirefoxBrowserCommand(BaseBrowserCommand):
|
||||||
|
def _fixupMacPath(self):
|
||||||
|
self.path = os.path.join(self.path, "Contents", "MacOS", "firefox-bin")
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
super(FirefoxBrowserCommand, self).setup()
|
||||||
|
shutil.copytree(os.path.join(DOC_ROOT, "test", "resources", "firefox"),
|
||||||
|
self.profileDir)
|
||||||
|
|
||||||
def start(self, url):
|
def start(self, url):
|
||||||
cmds = [self.path]
|
cmds = [self.path]
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
@ -201,9 +225,29 @@ class BrowserCommand():
|
|||||||
cmds.extend(["-no-remote", "-profile", self.profileDir, url])
|
cmds.extend(["-no-remote", "-profile", self.profileDir, url])
|
||||||
self.process = subprocess.Popen(cmds)
|
self.process = subprocess.Popen(cmds)
|
||||||
|
|
||||||
|
class ChromeBrowserCommand(BaseBrowserCommand):
|
||||||
|
def _fixupMacPath(self):
|
||||||
|
self.path = os.path.join(self.path, "Contents", "MacOS", "Google Chrome")
|
||||||
|
|
||||||
|
def start(self, url):
|
||||||
|
cmds = [self.path]
|
||||||
|
cmds.extend(["--user-data-dir=%s" % self.profileDir,
|
||||||
|
"--no-first-run", "--disable-sync", url])
|
||||||
|
self.process = subprocess.Popen(cmds)
|
||||||
|
|
||||||
|
def makeBrowserCommand(browser):
|
||||||
|
path = browser["path"].lower()
|
||||||
|
name = browser["name"].lower()
|
||||||
|
if name.find("firefox") > -1 or path.find("firefox") > -1:
|
||||||
|
return FirefoxBrowserCommand(browser)
|
||||||
|
elif name.find("chrom") > -1 or path.find("chrom") > -1:
|
||||||
|
return ChromeBrowserCommand(browser)
|
||||||
|
else:
|
||||||
|
raise Exception("Unrecognized browser: %s" % browser)
|
||||||
|
|
||||||
def makeBrowserCommands(browserManifestFile):
|
def makeBrowserCommands(browserManifestFile):
|
||||||
with open(browserManifestFile) as bmf:
|
with open(browserManifestFile) as bmf:
|
||||||
browsers = [BrowserCommand(browser) for browser in json.load(bmf)]
|
browsers = [makeBrowserCommand(browser) for browser in json.load(bmf)]
|
||||||
return browsers
|
return browsers
|
||||||
|
|
||||||
def downloadLinkedPDFs(manifestList):
|
def downloadLinkedPDFs(manifestList):
|
||||||
@ -267,6 +311,7 @@ def startBrowsers(browsers, options):
|
|||||||
b.setup()
|
b.setup()
|
||||||
print 'Launching', b.name
|
print 'Launching', b.name
|
||||||
qs = 'browser='+ urllib.quote(b.name) +'&manifestFile='+ urllib.quote(options.manifestFile)
|
qs = 'browser='+ urllib.quote(b.name) +'&manifestFile='+ urllib.quote(options.manifestFile)
|
||||||
|
qs += '&path=' + b.path
|
||||||
b.start('http://localhost:8080/test/test_slave.html?'+ qs)
|
b.start('http://localhost:8080/test/test_slave.html?'+ qs)
|
||||||
|
|
||||||
def teardownBrowsers(browsers):
|
def teardownBrowsers(browsers):
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<script type="text/javascript" src="/fonts.js"></script>
|
<script type="text/javascript" src="/fonts.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 browser, canvas, currentTask, currentTaskIdx, failure, manifest, numPages, pdfDoc, stdout;
|
var appPath, browser, canvas, currentTask, currentTaskIdx, failure, manifest, numPages, pdfDoc, stdout;
|
||||||
|
|
||||||
function queryParams() {
|
function queryParams() {
|
||||||
var qs = window.location.search.substring(1);
|
var qs = window.location.search.substring(1);
|
||||||
@ -23,12 +23,13 @@ function load() {
|
|||||||
var params = queryParams();
|
var params = queryParams();
|
||||||
browser = params.browser;
|
browser = params.browser;
|
||||||
manifestFile = params.manifestFile;
|
manifestFile = params.manifestFile;
|
||||||
|
appPath = params.path;
|
||||||
|
|
||||||
canvas = document.createElement("canvas");
|
canvas = document.createElement("canvas");
|
||||||
canvas.mozOpaque = true;
|
canvas.mozOpaque = true;
|
||||||
stdout = document.getElementById("stdout");
|
stdout = document.getElementById("stdout");
|
||||||
|
|
||||||
log("Harness thinks this browser is '"+ browser +"'\n");
|
log("Harness thinks this browser is '"+ browser + "' with path " + appPath + "\n");
|
||||||
log("Fetching manifest "+ manifestFile +"...");
|
log("Fetching manifest "+ manifestFile +"...");
|
||||||
|
|
||||||
var r = new XMLHttpRequest();
|
var r = new XMLHttpRequest();
|
||||||
@ -157,13 +158,21 @@ function snapshotCurrentPage(gfx) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendQuitRequest() {
|
||||||
|
var r = new XMLHttpRequest();
|
||||||
|
r.open("POST", "/tellMeToQuit?path=" + escape(appPath), false);
|
||||||
|
r.send("");
|
||||||
|
}
|
||||||
|
|
||||||
function quitApp() {
|
function quitApp() {
|
||||||
log("Done!");
|
log("Done!");
|
||||||
document.body.innerHTML = "Tests are finished. <h1>CLOSE ME!</h1>";
|
document.body.innerHTML = "Tests are finished. <h1>CLOSE ME!</h1>";
|
||||||
if (window.SpecialPowers)
|
if (window.SpecialPowers) {
|
||||||
SpecialPowers.quitApplication();
|
SpecialPowers.quitApplication();
|
||||||
else
|
} else {
|
||||||
|
sendQuitRequest();
|
||||||
window.close();
|
window.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function done() {
|
function done() {
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
@ -119,7 +119,8 @@ function CanvasProxy(width, height) {
|
|||||||
"$addCurrentX",
|
"$addCurrentX",
|
||||||
"$saveCurrentX",
|
"$saveCurrentX",
|
||||||
"$restoreCurrentX",
|
"$restoreCurrentX",
|
||||||
"$showText"
|
"$showText",
|
||||||
|
"$setFont"
|
||||||
];
|
];
|
||||||
|
|
||||||
function buildFuncCall(name) {
|
function buildFuncCall(name) {
|
@ -18,12 +18,124 @@ 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
|
||||||
|
Fonts[name].properties = {
|
||||||
|
encoding: data[name].encoding
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call `Font.prototype.bindDOM` to make the font get loaded on the page.
|
||||||
|
Font.prototype.bindDOM.call(
|
||||||
|
Fonts[name],
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store only the data on Fonts that is needed later on, such that we
|
||||||
|
// hold track on as lease memory as possible.
|
||||||
|
Fonts[font.name] = {
|
||||||
|
name: font.name,
|
||||||
|
mimetype: font.mimetype,
|
||||||
|
// This is set later on the worker replay. For some fonts, the encoding
|
||||||
|
// is calculated during the conversion process happening on the worker
|
||||||
|
// and therefore is not available right now.
|
||||||
|
// properties: {
|
||||||
|
// encoding: font.properties.encoding
|
||||||
|
// },
|
||||||
|
cache: 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 +168,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 +249,10 @@ function WorkerPDFDoc(canvas) {
|
|||||||
throw "Pattern not found";
|
throw "Pattern not found";
|
||||||
}
|
}
|
||||||
this.strokeStyle = pattern;
|
this.strokeStyle = pattern;
|
||||||
|
},
|
||||||
|
|
||||||
|
"$setFont": function(name) {
|
||||||
|
Fonts.active = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,6 +304,18 @@ function WorkerPDFDoc(canvas) {
|
|||||||
div.setAttribute("style", style);
|
div.setAttribute("style", style);
|
||||||
document.body.appendChild(div);
|
document.body.appendChild(div);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"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 +336,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 +346,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 +374,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 +397,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
27
worker/console.js
Normal 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
65
worker/font.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -27,10 +27,11 @@ 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("../glyphlist.js")
|
||||||
|
|
||||||
// Use the JpegStreamProxy proxy.
|
// Use the JpegStreamProxy proxy.
|
||||||
JpegStream = JpegStreamProxy;
|
JpegStream = JpegStreamProxy;
|
||||||
@ -65,21 +66,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();
|
Loading…
Reference in New Issue
Block a user