Merge branch 'master' into faxstream
This commit is contained in:
commit
548f481062
260
crypto.js
Normal file
260
crypto.js
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
/* -*- Mode: Java; tab-width: s; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||||
|
/* vim: set shiftwidth=s tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var ARCFourCipher = (function() {
|
||||||
|
function constructor(key) {
|
||||||
|
this.a = 0;
|
||||||
|
this.b = 0;
|
||||||
|
var s = new Uint8Array(256);
|
||||||
|
var i, j = 0, tmp, keyLength = key.length;
|
||||||
|
for (i = 0; i < 256; ++i)
|
||||||
|
s[i] = i;
|
||||||
|
for (i = 0; i < 256; ++i) {
|
||||||
|
tmp = s[i];
|
||||||
|
j = (j + tmp + key[i % keyLength]) & 0xFF;
|
||||||
|
s[i] = s[j];
|
||||||
|
s[j] = tmp;
|
||||||
|
}
|
||||||
|
this.s = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
encryptBlock: function(data) {
|
||||||
|
var i, n = data.length, tmp, tmp2;
|
||||||
|
var a = this.a, b = this.b, s = this.s;
|
||||||
|
var output = new Uint8Array(n);
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
var tmp;
|
||||||
|
a = (a + 1) & 0xFF;
|
||||||
|
tmp = s[a];
|
||||||
|
b = (b + tmp) & 0xFF;
|
||||||
|
tmp2 = s[b]
|
||||||
|
s[a] = tmp2;
|
||||||
|
s[b] = tmp;
|
||||||
|
output[i] = data[i] ^ s[(tmp + tmp2) & 0xFF];
|
||||||
|
}
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var md5 = (function() {
|
||||||
|
const r = new Uint8Array([
|
||||||
|
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||||
|
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||||
|
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
||||||
|
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]);
|
||||||
|
const k = new Int32Array([
|
||||||
|
-680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426,
|
||||||
|
-1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162,
|
||||||
|
1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632,
|
||||||
|
643717713, -373897302, -701558691, 38016083, -660478335, -405537848, 568446438,
|
||||||
|
-1019803690, -187363961, 1163531501, -1444681467, -51403784, 1735328473,
|
||||||
|
-1926607734, -378558, -2022574463, 1839030562, -35309556, -1530992060,
|
||||||
|
1272893353, -155497632, -1094730640, 681279174, -358537222, -722521979,
|
||||||
|
76029189, -640364487, -421815835, 530742520, -995338651, -198630844, 1126891415,
|
||||||
|
-1416354905, -57434055, 1700485571, -1894986606, -1051523, -2054922799,
|
||||||
|
1873313359, -30611744, -1560198380, 1309151649, -145523070, -1120210379,
|
||||||
|
718787259, -343485551]);
|
||||||
|
|
||||||
|
function hash(data, offset, length) {
|
||||||
|
var h0 = 1732584193, h1 = -271733879, h2 = -1732584194, h3 = 271733878;
|
||||||
|
// pre-processing
|
||||||
|
var paddedLength = (length + 72) & ~63; // data + 9 extra bytes
|
||||||
|
var padded = new Uint8Array(paddedLength);
|
||||||
|
var i, j, n;
|
||||||
|
for (i = 0; i < length; ++i)
|
||||||
|
padded[i] = data[offset++];
|
||||||
|
padded[i++] = 0x80;
|
||||||
|
n = paddedLength - 8;
|
||||||
|
for (; i < n; ++i)
|
||||||
|
padded[i] = 0;
|
||||||
|
padded[i++] = (length << 3) & 0xFF;
|
||||||
|
padded[i++] = (length >> 5) & 0xFF;
|
||||||
|
padded[i++] = (length >> 13) & 0xFF;
|
||||||
|
padded[i++] = (length >> 21) & 0xFF;
|
||||||
|
padded[i++] = (length >>> 29) & 0xFF;
|
||||||
|
padded[i++] = 0;
|
||||||
|
padded[i++] = 0;
|
||||||
|
padded[i++] = 0;
|
||||||
|
// chunking
|
||||||
|
// TODO ArrayBuffer ?
|
||||||
|
var w = new Int32Array(16);
|
||||||
|
for (i = 0; i < paddedLength;) {
|
||||||
|
for (j = 0; j < 16; ++j, i += 4)
|
||||||
|
w[j] = padded[i] | (padded[i + 1] << 8) | (padded[i + 2] << 16) | (padded[i + 3] << 24);
|
||||||
|
var a = h0, b = h1, c = h2, d = h3, f, g;
|
||||||
|
for (j = 0; j < 64; ++j) {
|
||||||
|
if (j < 16) {
|
||||||
|
f = (b & c) | ((~b) & d);
|
||||||
|
g = j;
|
||||||
|
} else if (j < 32) {
|
||||||
|
f = (d & b) | ((~d) & c);
|
||||||
|
g = (5 * j + 1) & 15;
|
||||||
|
} else if (j < 48) {
|
||||||
|
f = b ^ c ^ d;
|
||||||
|
g = (3 * j + 5) & 15;
|
||||||
|
} else {
|
||||||
|
f = c ^ (b | (~d));
|
||||||
|
g = (7 * j) & 15;
|
||||||
|
}
|
||||||
|
var tmp = d, rotateArg = (a + f + k[j] + w[g]) | 0, rotate = r[j];
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
b = (b + ((rotateArg << rotate) | (rotateArg >>> (32 - rotate)))) | 0;
|
||||||
|
a = tmp;
|
||||||
|
}
|
||||||
|
h0 = (h0 + a) | 0;
|
||||||
|
h1 = (h1 + b) | 0;
|
||||||
|
h2 = (h2 + c) | 0;
|
||||||
|
h3 = (h3 + d) | 0;
|
||||||
|
}
|
||||||
|
return new Uint8Array([
|
||||||
|
h0 & 0xFF, (h0 >> 8) & 0xFF, (h0 >> 16) & 0xFF, (h0 >>> 24) & 0xFF,
|
||||||
|
h1 & 0xFF, (h1 >> 8) & 0xFF, (h1 >> 16) & 0xFF, (h1 >>> 24) & 0xFF,
|
||||||
|
h2 & 0xFF, (h2 >> 8) & 0xFF, (h2 >> 16) & 0xFF, (h2 >>> 24) & 0xFF,
|
||||||
|
h3 & 0xFF, (h3 >> 8) & 0xFF, (h3 >> 16) & 0xFF, (h3 >>> 24) & 0xFF
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var CipherTransform = (function() {
|
||||||
|
function constructor(stringCipherConstructor, streamCipherConstructor) {
|
||||||
|
this.stringCipherConstructor = stringCipherConstructor;
|
||||||
|
this.streamCipherConstructor = streamCipherConstructor;
|
||||||
|
}
|
||||||
|
constructor.prototype = {
|
||||||
|
createStream: function (stream) {
|
||||||
|
var cipher = new this.streamCipherConstructor();
|
||||||
|
return new DecryptStream(stream, function(data) {
|
||||||
|
return cipher.encryptBlock(data);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
decryptString: function(s) {
|
||||||
|
var cipher = new this.stringCipherConstructor();
|
||||||
|
var data = string2bytes(s);
|
||||||
|
data = cipher.encryptBlock(data);
|
||||||
|
return bytes2string(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var CipherTransformFactory = (function() {
|
||||||
|
function prepareKeyData(fileId, password, ownerPassword, userPassword, flags, revision, keyLength) {
|
||||||
|
const defaultPasswordBytes = new Uint8Array([
|
||||||
|
0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
|
||||||
|
0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]);
|
||||||
|
var hashData = new Uint8Array(88), i = 0, j, n;
|
||||||
|
if (password) {
|
||||||
|
n = Math.min(32, password.length);
|
||||||
|
for (; i < n; ++i)
|
||||||
|
hashData[i] = password[i];
|
||||||
|
}
|
||||||
|
j = 0;
|
||||||
|
while (i < 32) {
|
||||||
|
hashData[i++] = defaultPasswordBytes[j++];
|
||||||
|
}
|
||||||
|
// as now the padded password in the hashData[0..i]
|
||||||
|
for (j = 0, n = ownerPassword.length; j < n; ++j)
|
||||||
|
hashData[i++] = ownerPassword[j];
|
||||||
|
hashData[i++] = flags & 0xFF;
|
||||||
|
hashData[i++] = (flags >> 8) & 0xFF;
|
||||||
|
hashData[i++] = (flags >> 16) & 0xFF;
|
||||||
|
hashData[i++] = (flags >>> 24) & 0xFF;
|
||||||
|
for (j = 0, n = fileId.length; j < n; ++j)
|
||||||
|
hashData[i++] = fileId[j];
|
||||||
|
// TODO rev 4, if metadata is not encrypted pass 0xFFFFFF also
|
||||||
|
var hash = md5(hashData, 0, i);
|
||||||
|
var keyLengthInBytes = keyLength >> 3;
|
||||||
|
if (revision >= 3) {
|
||||||
|
for (j = 0; j < 50; ++j) {
|
||||||
|
hash = md5(hash, 0, keyLengthInBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var encryptionKey = hash.subarray(0, keyLengthInBytes);
|
||||||
|
var cipher, checkData;
|
||||||
|
|
||||||
|
if (revision >= 3) {
|
||||||
|
// padded password in hashData, we can use this array for user password check
|
||||||
|
i = 32;
|
||||||
|
for(j = 0, n = fileId.length; j < n; ++j)
|
||||||
|
hashData[i++] = fileId[j];
|
||||||
|
cipher = new ARCFourCipher(encryptionKey);
|
||||||
|
var checkData = cipher.encryptBlock(md5(hashData, 0, i));
|
||||||
|
n = encryptionKey.length;
|
||||||
|
var derrivedKey = new Uint8Array(n), k;
|
||||||
|
for (j = 1; j <= 19; ++j) {
|
||||||
|
for (k = 0; k < n; ++k)
|
||||||
|
derrivedKey[k] = encryptionKey[k] ^ j;
|
||||||
|
cipher = new ARCFourCipher(derrivedKey);
|
||||||
|
checkData = cipher.encryptBlock(checkData);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cipher = new ARCFourCipher(encryptionKey);
|
||||||
|
checkData = cipher.encryptBlock(hashData.subarray(0, 32));
|
||||||
|
}
|
||||||
|
for (j = 0, n = checkData.length; j < n; ++j) {
|
||||||
|
if (userPassword[j] != checkData[j])
|
||||||
|
error("incorrect password");
|
||||||
|
}
|
||||||
|
return encryptionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
function constructor(dict, fileId, password) {
|
||||||
|
var filter = dict.get("Filter");
|
||||||
|
if (!IsName(filter) || filter.name != "Standard")
|
||||||
|
error("unknown encryption method");
|
||||||
|
this.dict = dict;
|
||||||
|
var algorithm = dict.get("V");
|
||||||
|
if (!IsInt(algorithm) ||
|
||||||
|
(algorithm != 1 && algorithm != 2))
|
||||||
|
error("unsupported encryption algorithm");
|
||||||
|
// TODO support algorithm 4
|
||||||
|
var keyLength = dict.get("Length") || 40;
|
||||||
|
if (!IsInt(keyLength) ||
|
||||||
|
keyLength < 40 || (keyLength % 8) != 0)
|
||||||
|
error("invalid key length");
|
||||||
|
// prepare keys
|
||||||
|
var ownerPassword = stringToBytes(dict.get("O"));
|
||||||
|
var userPassword = stringToBytes(dict.get("U"));
|
||||||
|
var flags = dict.get("P");
|
||||||
|
var revision = dict.get("R");
|
||||||
|
var fileIdBytes = stringToBytes(fileId);
|
||||||
|
var passwordBytes;
|
||||||
|
if (password)
|
||||||
|
passwordBytes = stringToBytes(password);
|
||||||
|
|
||||||
|
this.encryptionKey = prepareKeyData(fileIdBytes, passwordBytes,
|
||||||
|
ownerPassword, userPassword, flags, revision, keyLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
createCipherTransform: function(num, gen) {
|
||||||
|
var encryptionKey = this.encryptionKey;
|
||||||
|
var key = new Uint8Array(encryptionKey.length + 5), i, n;
|
||||||
|
for (i = 0, n = encryptionKey.length; i < n; ++i)
|
||||||
|
key[i] = encryptionKey[i];
|
||||||
|
key[i++] = num & 0xFF;
|
||||||
|
key[i++] = (num >> 8) & 0xFF;
|
||||||
|
key[i++] = (num >> 16) & 0xFF;
|
||||||
|
key[i++] = gen & 0xFF;
|
||||||
|
key[i++] = (gen >> 8) & 0xFF;
|
||||||
|
var hash = md5(key, 0, i);
|
||||||
|
key = hash.subarray(0, Math.min(key.length, 16));
|
||||||
|
var cipherConstructor = function() {
|
||||||
|
return new ARCFourCipher(key);
|
||||||
|
};
|
||||||
|
return new CipherTransform(cipherConstructor, cipherConstructor);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
339
fonts.js
339
fonts.js
@ -80,6 +80,36 @@ var Fonts = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var FontLoader = {
|
||||||
|
bind: function(fonts) {
|
||||||
|
var worker = (typeof window == "undefined");
|
||||||
|
var ready = true;
|
||||||
|
|
||||||
|
for (var i = 0; i < fonts.length; i++) {
|
||||||
|
var font = fonts[i];
|
||||||
|
if (Fonts[font.name]) {
|
||||||
|
ready = ready && !Fonts[font.name].loading;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ready = false;
|
||||||
|
|
||||||
|
var obj = new Font(font.name, font.file, font.properties);
|
||||||
|
|
||||||
|
var str = "";
|
||||||
|
var data = Fonts[font.name].data;
|
||||||
|
var length = data.length;
|
||||||
|
for (var j = 0; j < length; j++)
|
||||||
|
str += String.fromCharCode(data[j]);
|
||||||
|
|
||||||
|
worker ? obj.bindWorker(str) : obj.bindDOM(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* '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
|
||||||
* decoding logics whatever type it is (assuming the font type is supported).
|
* decoding logics whatever type it is (assuming the font type is supported).
|
||||||
@ -113,13 +143,14 @@ var Font = (function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var data;
|
||||||
switch (properties.type) {
|
switch (properties.type) {
|
||||||
case "Type1":
|
case "Type1":
|
||||||
var cff = new CFF(name, file, properties);
|
var cff = new CFF(name, file, properties);
|
||||||
this.mimetype = "font/opentype";
|
this.mimetype = "font/opentype";
|
||||||
|
|
||||||
// Wrap the CFF data inside an OTF font file
|
// Wrap the CFF data inside an OTF font file
|
||||||
this.font = this.convert(name, cff, properties);
|
data = this.convert(name, cff, properties);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "TrueType":
|
case "TrueType":
|
||||||
@ -127,7 +158,7 @@ var Font = (function () {
|
|||||||
|
|
||||||
// Repair the TrueType file if it is can be damaged in the point of
|
// Repair the TrueType file if it is can be damaged in the point of
|
||||||
// view of the sanitizer
|
// view of the sanitizer
|
||||||
this.font = this.checkAndRepair(name, file, properties);
|
data = this.checkAndRepair(name, file, properties);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -135,28 +166,12 @@ var Font = (function () {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = this.font;
|
|
||||||
Fonts[name] = {
|
Fonts[name] = {
|
||||||
data: data,
|
data: data,
|
||||||
properties: properties,
|
properties: properties,
|
||||||
loading: true,
|
loading: true,
|
||||||
cache: Object.create(null)
|
cache: Object.create(null)
|
||||||
}
|
};
|
||||||
|
|
||||||
// Convert data to a string.
|
|
||||||
var dataStr = "";
|
|
||||||
var length = data.length;
|
|
||||||
for (var i = 0; i < length; ++i)
|
|
||||||
dataStr += String.fromCharCode(data[i]);
|
|
||||||
|
|
||||||
// Attach the font to the document. If this script is runnig in a worker,
|
|
||||||
// call `bindWorker`, which sends stuff over to the main thread.
|
|
||||||
if (typeof window != "undefined") {
|
|
||||||
this.bindDOM(dataStr);
|
|
||||||
} else {
|
|
||||||
this.bindWorker(dataStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function stringToArray(str) {
|
function stringToArray(str) {
|
||||||
@ -310,57 +325,60 @@ var Font = (function () {
|
|||||||
idDeltas + idRangeOffsets + glyphsIds);
|
idDeltas + idRangeOffsets + glyphsIds);
|
||||||
};
|
};
|
||||||
|
|
||||||
function createOS2Table() {
|
function createOS2Table(properties) {
|
||||||
var OS2 = stringToArray(
|
return "\x00\x03" + // version
|
||||||
"\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\x00" + // fstype
|
"\x02\x8A" + // ySubscriptXSize
|
||||||
"\x02\x8A" + // ySubscriptXSize
|
"\x02\xBB" + // ySubscriptYSize
|
||||||
"\x02\xBB" + // ySubscriptYSize
|
"\x00\x00" + // ySubscriptXOffset
|
||||||
"\x00\x00" + // ySubscriptXOffset
|
"\x00\x8C" + // ySubscriptYOffset
|
||||||
"\x00\x8C" + // ySubscriptYOffset
|
"\x02\x8A" + // ySuperScriptXSize
|
||||||
"\x02\x8A" + // ySuperScriptXSize
|
"\x02\xBB" + // ySuperScriptYSize
|
||||||
"\x02\xBB" + // ySuperScriptYSize
|
"\x00\x00" + // ySuperScriptXOffset
|
||||||
"\x00\x00" + // ySuperScriptXOffset
|
"\x01\xDF" + // ySuperScriptYOffset
|
||||||
"\x01\xDF" + // ySuperScriptYOffset
|
"\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
|
||||||
"\x02\x00\x06\x03\x00\x00\x00\x00\x00\x00" + // Panose
|
"\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 0-31)
|
||||||
"\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 0-31)
|
"\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 32-63)
|
||||||
"\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 32-63)
|
"\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 64-95)
|
||||||
"\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 64-95)
|
"\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 96-127)
|
||||||
"\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 96-127)
|
"\x2A\x32\x31\x2A" + // achVendID
|
||||||
"\x2A\x32\x31\x2A" + // achVendID
|
"\x00\x20" + // fsSelection
|
||||||
"\x00\x20" + // fsSelection
|
"\x00\x2D" + // usFirstCharIndex
|
||||||
"\x00\x2D" + // usFirstCharIndex
|
"\x00\x7A" + // usLastCharIndex
|
||||||
"\x00\x7A" + // usLastCharIndex
|
"\x00\x03" + // sTypoAscender
|
||||||
"\x00\x03" + // sTypoAscender
|
"\x00\x20" + // sTypeDescender
|
||||||
"\x00\x20" + // sTypeDescender
|
"\x00\x38" + // sTypoLineGap
|
||||||
"\x00\x38" + // sTypoLineGap
|
string16(properties.ascent) + // usWinAscent
|
||||||
"\x00\x5A" + // usWinAscent
|
string16(properties.descent) + // usWinDescent
|
||||||
"\x02\xB4" + // usWinDescent
|
"\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31)
|
||||||
"\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31)
|
"\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63)
|
||||||
"\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63)
|
string16(properties.xHeight) + // sxHeight
|
||||||
"\x00\x00" + // sxHeight
|
string16(properties.capHeight) + // sCapHeight
|
||||||
"\x00\x00" + // sCapHeight
|
"\x00\x01" + // usDefaultChar
|
||||||
"\x00\x01" + // usDefaultChar
|
"\x00\xCD" + // usBreakChar
|
||||||
"\x00\xCD" + // usBreakChar
|
"\x00\x02"; // usMaxContext
|
||||||
"\x00\x02" // usMaxContext
|
};
|
||||||
);
|
|
||||||
return OS2;
|
function createPostTable(properties) {
|
||||||
|
TODO("Fill with real values from the font dict");
|
||||||
|
|
||||||
|
return "\x00\x03\x00\x00" + // Version number
|
||||||
|
string32(properties.italicAngle) + // italicAngle
|
||||||
|
"\x00\x00" + // underlinePosition
|
||||||
|
"\x00\x00" + // underlineThickness
|
||||||
|
"\x00\x00\x00\x00" + // isFixedPitch
|
||||||
|
"\x00\x00\x00\x00" + // minMemType42
|
||||||
|
"\x00\x00\x00\x00" + // maxMemType42
|
||||||
|
"\x00\x00\x00\x00" + // minMemType1
|
||||||
|
"\x00\x00\x00\x00"; // maxMemType1
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* A bunch of the OpenType code is duplicate between this class and the
|
|
||||||
* TrueType code, this is intentional and will merge in a future version
|
|
||||||
* where all the code relative to OpenType will probably have its own
|
|
||||||
* class and will take decision without the Fonts consent.
|
|
||||||
* But at the moment it allows to develop around the TrueType rewriting
|
|
||||||
* on the fly without messing up with the 'regular' Type1 to OTF conversion.
|
|
||||||
*/
|
|
||||||
constructor.prototype = {
|
constructor.prototype = {
|
||||||
name: null,
|
name: null,
|
||||||
font: null,
|
font: null,
|
||||||
@ -422,6 +440,7 @@ var Font = (function () {
|
|||||||
(format == 6 && numTables == 1 && !properties.encoding.empty)) {
|
(format == 6 && numTables == 1 && !properties.encoding.empty)) {
|
||||||
// Format 0 alone is not allowed by the sanitizer so let's rewrite
|
// Format 0 alone is not allowed by the sanitizer so let's rewrite
|
||||||
// that to a 3-1-4 Unicode BMP table
|
// that to a 3-1-4 Unicode BMP table
|
||||||
|
TODO("Use an other source of informations than charset here, it is not reliable");
|
||||||
var charset = properties.charset;
|
var charset = properties.charset;
|
||||||
var glyphs = [];
|
var glyphs = [];
|
||||||
for (var j = 0; j < charset.length; j++) {
|
for (var j = 0; j < charset.length; j++) {
|
||||||
@ -525,10 +544,9 @@ var Font = (function () {
|
|||||||
createOpenTypeHeader("\x00\x01\x00\x00", ttf, offsets, numTables);
|
createOpenTypeHeader("\x00\x01\x00\x00", ttf, offsets, numTables);
|
||||||
|
|
||||||
// Insert the missing table
|
// Insert the missing table
|
||||||
var OS2 = createOS2Table();
|
|
||||||
tables.push({
|
tables.push({
|
||||||
tag: "OS/2",
|
tag: "OS/2",
|
||||||
data: OS2
|
data: stringToArray(createOS2Table(properties))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Replace the old CMAP table with a shiny new one
|
// Replace the old CMAP table with a shiny new one
|
||||||
@ -536,20 +554,9 @@ var Font = (function () {
|
|||||||
|
|
||||||
// Rewrite the 'post' table if needed
|
// Rewrite the 'post' table if needed
|
||||||
if (!post) {
|
if (!post) {
|
||||||
post =
|
tables.push({
|
||||||
"\x00\x03\x00\x00" + // Version number
|
|
||||||
"\x00\x00\x01\x00" + // italicAngle
|
|
||||||
"\x00\x00" + // underlinePosition
|
|
||||||
"\x00\x00" + // underlineThickness
|
|
||||||
"\x00\x00\x00\x00" + // isFixedPitch
|
|
||||||
"\x00\x00\x00\x00" + // minMemType42
|
|
||||||
"\x00\x00\x00\x00" + // maxMemType42
|
|
||||||
"\x00\x00\x00\x00" + // minMemType1
|
|
||||||
"\x00\x00\x00\x00"; // maxMemType1
|
|
||||||
|
|
||||||
tables.unshift({
|
|
||||||
tag: "post",
|
tag: "post",
|
||||||
data: stringToArray(post)
|
data: stringToArray(createPostTable(properties))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,16 +600,16 @@ var Font = (function () {
|
|||||||
return font.getBytes();
|
return font.getBytes();
|
||||||
},
|
},
|
||||||
|
|
||||||
convert: function font_convert(name, font, properties) {
|
convert: function font_convert(fontName, font, properties) {
|
||||||
var otf = new Uint8Array(kMaxFontFileSize);
|
var otf = new Uint8Array(kMaxFontFileSize);
|
||||||
|
|
||||||
function createNameTable(name) {
|
function createNameTable(name) {
|
||||||
var names = [
|
var names = [
|
||||||
"See original licence", // Copyright
|
"See original licence", // Copyright
|
||||||
name, // Font family
|
fontName, // Font family
|
||||||
"undefined", // Font subfamily (font weight)
|
"undefined", // Font subfamily (font weight)
|
||||||
"uniqueID", // Unique ID
|
"uniqueID", // Unique ID
|
||||||
name, // Full font name
|
fontName, // Full font name
|
||||||
"0.1", // Version
|
"0.1", // Version
|
||||||
"undefined", // Postscript name
|
"undefined", // Postscript name
|
||||||
"undefined", // Trademark
|
"undefined", // Trademark
|
||||||
@ -610,7 +617,7 @@ var Font = (function () {
|
|||||||
"undefined" // Designer
|
"undefined" // Designer
|
||||||
];
|
];
|
||||||
|
|
||||||
var name =
|
var nameTable =
|
||||||
"\x00\x00" + // format
|
"\x00\x00" + // format
|
||||||
"\x00\x0A" + // Number of names Record
|
"\x00\x0A" + // Number of names Record
|
||||||
"\x00\x7E"; // Storage
|
"\x00\x7E"; // Storage
|
||||||
@ -627,21 +634,21 @@ var Font = (function () {
|
|||||||
"\x00\x00" + // name ID
|
"\x00\x00" + // name ID
|
||||||
string16(str.length) +
|
string16(str.length) +
|
||||||
string16(strOffset);
|
string16(strOffset);
|
||||||
name += nameRecord;
|
nameTable += nameRecord;
|
||||||
|
|
||||||
strOffset += str.length;
|
strOffset += str.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
name += names.join("");
|
nameTable += names.join("");
|
||||||
return name;
|
return nameTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Required Tables
|
// Required Tables
|
||||||
var CFF =
|
var CFF =
|
||||||
font.data, // PostScript Font Program
|
font.data, // PostScript Font Program
|
||||||
OS2, // OS/2 and Windows Specific metrics
|
OS2, // OS/2 and Windows Specific metrics
|
||||||
cmap, // Character to glyphs mapping
|
cmap, // Character to glyphs mapping
|
||||||
head, // Font eader
|
head, // Font header
|
||||||
hhea, // Horizontal header
|
hhea, // Horizontal header
|
||||||
hmtx, // Horizontal metrics
|
hmtx, // Horizontal metrics
|
||||||
maxp, // Maximum profile
|
maxp, // Maximum profile
|
||||||
@ -665,13 +672,11 @@ var Font = (function () {
|
|||||||
createTableEntry(otf, offsets, "CFF ", CFF);
|
createTableEntry(otf, offsets, "CFF ", CFF);
|
||||||
|
|
||||||
/** OS/2 */
|
/** OS/2 */
|
||||||
OS2 = createOS2Table();
|
OS2 = stringToArray(createOS2Table(properties));
|
||||||
createTableEntry(otf, offsets, "OS/2", OS2);
|
createTableEntry(otf, offsets, "OS/2", OS2);
|
||||||
|
|
||||||
//XXX Getting charstrings here seems wrong since this is another CFF glue
|
|
||||||
var charstrings = font.getOrderedCharStrings(properties.glyphs);
|
|
||||||
|
|
||||||
/** CMAP */
|
/** CMAP */
|
||||||
|
var charstrings = font.charstrings;
|
||||||
cmap = createCMapTable(charstrings);
|
cmap = createCMapTable(charstrings);
|
||||||
createTableEntry(otf, offsets, "cmap", cmap);
|
createTableEntry(otf, offsets, "cmap", cmap);
|
||||||
|
|
||||||
@ -720,11 +725,15 @@ var Font = (function () {
|
|||||||
createTableEntry(otf, offsets, "hhea", hhea);
|
createTableEntry(otf, offsets, "hhea", hhea);
|
||||||
|
|
||||||
/** HMTX */
|
/** HMTX */
|
||||||
hmtx = "\x01\xF4\x00\x00";
|
/* For some reasons, probably related to how the backend handle fonts,
|
||||||
|
* Linux seems to ignore this file and prefer the data from the CFF itself
|
||||||
|
* while Windows use this data. So be careful if you hack on Linux and
|
||||||
|
* have to touch the 'hmtx' table
|
||||||
|
*/
|
||||||
|
hmtx = "\x01\xF4\x00\x00"; // Fake .notdef
|
||||||
|
var width = 0, lsb = 0;
|
||||||
for (var i = 0; i < charstrings.length; i++) {
|
for (var i = 0; i < charstrings.length; i++) {
|
||||||
var charstring = charstrings[i].charstring;
|
width = charstrings[i].charstring[1];
|
||||||
var width = charstring[1];
|
|
||||||
var lsb = charstring[0];
|
|
||||||
hmtx += string16(width) + string16(lsb);
|
hmtx += string16(width) + string16(lsb);
|
||||||
}
|
}
|
||||||
hmtx = stringToArray(hmtx);
|
hmtx = stringToArray(hmtx);
|
||||||
@ -741,17 +750,7 @@ var Font = (function () {
|
|||||||
createTableEntry(otf, offsets, "name", name);
|
createTableEntry(otf, offsets, "name", name);
|
||||||
|
|
||||||
/** POST */
|
/** POST */
|
||||||
// TODO: get those informations from the FontInfo structure
|
post = stringToArray(createPostTable(properties));
|
||||||
post = "\x00\x03\x00\x00" + // Version number
|
|
||||||
"\x00\x00\x01\x00" + // italicAngle
|
|
||||||
"\x00\x00" + // underlinePosition
|
|
||||||
"\x00\x00" + // underlineThickness
|
|
||||||
"\x00\x00\x00\x00" + // isFixedPitch
|
|
||||||
"\x00\x00\x00\x00" + // minMemType42
|
|
||||||
"\x00\x00\x00\x00" + // maxMemType42
|
|
||||||
"\x00\x00\x00\x00" + // minMemType1
|
|
||||||
"\x00\x00\x00\x00"; // maxMemType1
|
|
||||||
post = stringToArray(post);
|
|
||||||
createTableEntry(otf, offsets, "post", post);
|
createTableEntry(otf, offsets, "post", post);
|
||||||
|
|
||||||
// Once all the table entries header are written, dump the data!
|
// Once all the table entries header are written, dump the data!
|
||||||
@ -768,96 +767,55 @@ var Font = (function () {
|
|||||||
return fontData;
|
return fontData;
|
||||||
},
|
},
|
||||||
|
|
||||||
bindWorker: function font_bind_worker(dataStr) {
|
bindWorker: function font_bindWorker(data) {
|
||||||
postMessage({
|
postMessage({
|
||||||
action: "font",
|
action: "font",
|
||||||
data: {
|
data: {
|
||||||
raw: dataStr,
|
raw: data,
|
||||||
fontName: this.name,
|
fontName: this.name,
|
||||||
mimetype: this.mimetype
|
mimetype: this.mimetype
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
bindDOM: function font_bind_dom(dataStr) {
|
bindDOM: function font_bindDom(data) {
|
||||||
var fontName = this.name;
|
var fontName = this.name;
|
||||||
|
|
||||||
/** 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
|
||||||
|
// This code could go away when bug 471915 has landed
|
||||||
var canvas = document.createElement("canvas");
|
var canvas = document.createElement("canvas");
|
||||||
var style = "border: 1px solid black; position:absolute; top: " +
|
|
||||||
(debug ? (100 * fontCount) : "-200") + "px; left: 2px; width: 340px; height: 100px";
|
|
||||||
canvas.setAttribute("style", style);
|
|
||||||
canvas.setAttribute("width", 340);
|
|
||||||
canvas.setAttribute("heigth", 100);
|
|
||||||
document.body.appendChild(canvas);
|
|
||||||
|
|
||||||
// Get the font size canvas think it will be for 'spaces'
|
|
||||||
var ctx = canvas.getContext("2d");
|
var ctx = canvas.getContext("2d");
|
||||||
ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
|
ctx.font = "bold italic 20px " + fontName + ", Symbol";
|
||||||
var testString = " ";
|
var testString = " ";
|
||||||
|
|
||||||
// When debugging use the characters provided by the charsets to visually
|
|
||||||
// see what's happening instead of 'spaces'
|
|
||||||
var debug = false;
|
|
||||||
if (debug) {
|
|
||||||
var name = document.createElement("font");
|
|
||||||
name.setAttribute("style", "position: absolute; left: 20px; top: " +
|
|
||||||
(100 * fontCount + 60) + "px");
|
|
||||||
name.innerHTML = fontName;
|
|
||||||
document.body.appendChild(name);
|
|
||||||
|
|
||||||
// Retrieve font charset
|
|
||||||
var charset = Fonts[fontName].properties.charset || [];
|
|
||||||
|
|
||||||
// if the charset is too small make it repeat a few times
|
|
||||||
var count = 30;
|
|
||||||
while (count-- && charset.length <= 30)
|
|
||||||
charset = charset.concat(charset.slice());
|
|
||||||
|
|
||||||
for (var i = 0; i < charset.length; i++) {
|
|
||||||
var unicode = GlyphsUnicode[charset[i]];
|
|
||||||
if (!unicode)
|
|
||||||
continue;
|
|
||||||
testString += String.fromCharCode(unicode);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.fillText(testString, 20, 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Periodicaly check for the width of the testString, it will be
|
// Periodicaly check for the width of the testString, it will be
|
||||||
// different once the real font has loaded
|
// different once the real font has loaded
|
||||||
var textWidth = ctx.measureText(testString).width;
|
var textWidth = ctx.measureText(testString).width;
|
||||||
|
|
||||||
var interval = window.setInterval(function canvasInterval(self) {
|
var interval = window.setInterval(function canvasInterval(self) {
|
||||||
this.start = this.start || Date.now();
|
this.start = this.start || Date.now();
|
||||||
ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
|
ctx.font = "bold italic 20px " + fontName + ", Symbol";
|
||||||
|
|
||||||
// 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 ((Date.now() - this.start) >= kMaxWaitForFontFace) {
|
||||||
window.clearInterval(interval);
|
window.clearInterval(interval);
|
||||||
Fonts[fontName].loading = false;
|
Fonts[fontName].loading = false;
|
||||||
warn("Is " + fontName + " for charset: " + charset + " loaded?");
|
warn("Is " + fontName + " loaded?");
|
||||||
this.start = 0;
|
this.start = 0;
|
||||||
} else if (textWidth != ctx.measureText(testString).width) {
|
} else if (textWidth != ctx.measureText(testString).width) {
|
||||||
window.clearInterval(interval);
|
window.clearInterval(interval);
|
||||||
Fonts[fontName].loading = false;
|
Fonts[fontName].loading = false;
|
||||||
this.start = 0;
|
this.start = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug)
|
|
||||||
ctx.fillText(testString, 20, 50);
|
|
||||||
}, 30, this);
|
}, 30, this);
|
||||||
|
|
||||||
/** Hack end */
|
/** Hack end */
|
||||||
|
|
||||||
// Convert the data string and add it to the page.
|
|
||||||
var base64 = window.btoa(dataStr);
|
|
||||||
|
|
||||||
// Add the @font-face rule to the document
|
// Add the @font-face rule to the document
|
||||||
var url = "url(data:" + this.mimetype + ";base64," + base64 + ");";
|
var url = "url(data:" + this.mimetype + ";base64," + window.btoa(data) + ");";
|
||||||
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}";
|
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}";
|
||||||
var styleSheet = document.styleSheets[0];
|
var styleSheet = document.styleSheets[0];
|
||||||
styleSheet.insertRule(rule, styleSheet.length);
|
styleSheet.insertRule(rule, styleSheet.length);
|
||||||
@ -887,6 +845,9 @@ var FontsUtils = {
|
|||||||
bytes.set([value >> 24, value >> 16, value >> 8, value]);
|
bytes.set([value >> 24, value >> 16, value >> 8, value]);
|
||||||
return [bytes[0], bytes[1], bytes[2], bytes[3]];
|
return [bytes[0], bytes[1], bytes[2], bytes[3]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error("This number of bytes " + bytesCount + " is not supported");
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
bytesToInteger: function fu_bytesToInteger(bytesArray) {
|
bytesToInteger: function fu_bytesToInteger(bytesArray) {
|
||||||
@ -1223,6 +1184,8 @@ var CFFStrings = [
|
|||||||
"001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold"
|
"001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
var type1Parser = new Type1Parser();
|
||||||
|
|
||||||
var CFF = function(name, file, properties) {
|
var CFF = function(name, file, properties) {
|
||||||
// Get the data block containing glyphs and subrs informations
|
// Get the data block containing glyphs and subrs informations
|
||||||
var length1 = file.dict.get("Length1");
|
var length1 = file.dict.get("Length1");
|
||||||
@ -1230,17 +1193,15 @@ var CFF = function(name, file, properties) {
|
|||||||
file.skip(length1);
|
file.skip(length1);
|
||||||
var eexecBlock = file.getBytes(length2);
|
var eexecBlock = file.getBytes(length2);
|
||||||
|
|
||||||
// Decrypt the data blocks and retrieve the informations from it
|
// Decrypt the data blocks and retrieve it's content
|
||||||
var parser = new Type1Parser();
|
var data = type1Parser.extractFontProgram(eexecBlock);
|
||||||
var fontInfo = parser.extractFontProgram(eexecBlock);
|
|
||||||
|
|
||||||
properties.subrs = fontInfo.subrs;
|
this.charstrings = this.getOrderedCharStrings(data.charstrings);
|
||||||
properties.glyphs = fontInfo.charstrings;
|
this.data = this.wrap(name, this.charstrings, data.subrs, properties);
|
||||||
this.data = this.wrap(name, properties);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CFF.prototype = {
|
CFF.prototype = {
|
||||||
createCFFIndexHeader: function(objects, isByte) {
|
createCFFIndexHeader: function cff_createCFFIndexHeader(objects, isByte) {
|
||||||
// First 2 bytes contains the number of objects contained into this index
|
// First 2 bytes contains the number of objects contained into this index
|
||||||
var count = objects.length;
|
var count = objects.length;
|
||||||
|
|
||||||
@ -1277,18 +1238,18 @@ CFF.prototype = {
|
|||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
|
|
||||||
encodeNumber: function(value) {
|
encodeNumber: function cff_encodeNumber(value) {
|
||||||
var x = 0;
|
var x = 0;
|
||||||
if (value >= -32768 && value <= 32767) {
|
if (value >= -32768 && value <= 32767) {
|
||||||
return [ 28, value >> 8, value & 0xFF ];
|
return [ 28, value >> 8, value & 0xFF ];
|
||||||
} else if (value >= (-2147483647-1) && value <= 2147483647) {
|
} else if (value >= (-2147483647-1) && value <= 2147483647) {
|
||||||
return [ 0xFF, value >> 24, Value >> 16, value >> 8, value & 0xFF ];
|
return [ 0xFF, value >> 24, Value >> 16, value >> 8, value & 0xFF ];
|
||||||
} else {
|
|
||||||
error("Value: " + value + " is not allowed");
|
|
||||||
}
|
}
|
||||||
|
error("Value: " + value + " is not allowed");
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
getOrderedCharStrings: function(glyphs) {
|
getOrderedCharStrings: function cff_getOrderedCharStrings(glyphs) {
|
||||||
var charstrings = [];
|
var charstrings = [];
|
||||||
|
|
||||||
for (var i = 0; i < glyphs.length; i++) {
|
for (var i = 0; i < glyphs.length; i++) {
|
||||||
@ -1301,7 +1262,7 @@ CFF.prototype = {
|
|||||||
charstrings.push({
|
charstrings.push({
|
||||||
glyph: glyph,
|
glyph: glyph,
|
||||||
unicode: unicode,
|
unicode: unicode,
|
||||||
charstring: glyphs[i].data.slice()
|
charstring: glyphs[i].data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1334,7 +1295,7 @@ CFF.prototype = {
|
|||||||
"hvcurveto": 31,
|
"hvcurveto": 31,
|
||||||
},
|
},
|
||||||
|
|
||||||
flattenCharstring: function flattenCharstring(glyph, charstring, subrs) {
|
flattenCharstring: function flattenCharstring(charstring, subrs) {
|
||||||
var i = 0;
|
var i = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
var obj = charstring[i];
|
var obj = charstring[i];
|
||||||
@ -1344,9 +1305,9 @@ CFF.prototype = {
|
|||||||
if (obj.charAt) {
|
if (obj.charAt) {
|
||||||
switch (obj) {
|
switch (obj) {
|
||||||
case "callsubr":
|
case "callsubr":
|
||||||
var subr = subrs[charstring[i - 1]].slice();
|
var subr = subrs[charstring[i - 1]];
|
||||||
if (subr.length > 1) {
|
if (subr.length > 1) {
|
||||||
subr = this.flattenCharstring(glyph, subr, subrs);
|
subr = this.flattenCharstring(subr, subrs);
|
||||||
subr.pop();
|
subr.pop();
|
||||||
charstring.splice(i - 1, 2, subr);
|
charstring.splice(i - 1, 2, subr);
|
||||||
} else {
|
} else {
|
||||||
@ -1436,23 +1397,16 @@ CFF.prototype = {
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
error("failing with i = " + i + " in charstring:" + charstring + "(" + charstring.length + ")");
|
error("failing with i = " + i + " in charstring:" + charstring + "(" + charstring.length + ")");
|
||||||
|
return [];
|
||||||
},
|
},
|
||||||
|
|
||||||
wrap: function wrap(name, properties) {
|
wrap: function wrap(name, charstrings, subrs, properties) {
|
||||||
var charstrings = this.getOrderedCharStrings(properties.glyphs);
|
|
||||||
|
|
||||||
// Starts the conversion of the Type1 charstrings to Type2
|
// Starts the conversion of the Type1 charstrings to Type2
|
||||||
var charstringsCount = 0;
|
|
||||||
var charstringsDataLength = 0;
|
|
||||||
var glyphs = [];
|
var glyphs = [];
|
||||||
for (var i = 0; i < charstrings.length; i++) {
|
var glyphsCount = charstrings.length;
|
||||||
var charstring = charstrings[i].charstring.slice();
|
for (var i = 0; i < glyphsCount; i++) {
|
||||||
var glyph = charstrings[i].glyph;
|
var charstring = charstrings[i].charstring;
|
||||||
|
glyphs.push(this.flattenCharstring(charstring.slice(), subrs));
|
||||||
var flattened = this.flattenCharstring(glyph, charstring, properties.subrs);
|
|
||||||
glyphs.push(flattened);
|
|
||||||
charstringsCount++;
|
|
||||||
charstringsDataLength += flattened.length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a CFF font data
|
// Create a CFF font data
|
||||||
@ -1487,17 +1441,16 @@ CFF.prototype = {
|
|||||||
|
|
||||||
// Fill the charset header (first byte is the encoding)
|
// Fill the charset header (first byte is the encoding)
|
||||||
var charset = [0x00];
|
var charset = [0x00];
|
||||||
for (var i = 0; i < glyphs.length; i++) {
|
for (var i = 0; i < glyphsCount; i++) {
|
||||||
var index = CFFStrings.indexOf(charstrings[i].glyph);
|
var index = CFFStrings.indexOf(charstrings[i].glyph);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
index = CFFStrings.length + strings.indexOf(glyph);
|
index = CFFStrings.length + strings.indexOf(charstrings[i].glyph);
|
||||||
var bytes = FontsUtils.integerToBytes(index, 2);
|
var bytes = FontsUtils.integerToBytes(index, 2);
|
||||||
charset.push(bytes[0]);
|
charset.push(bytes[0]);
|
||||||
charset.push(bytes[1]);
|
charset.push(bytes[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var charstringsIndex = this.createCFFIndexHeader([[0x40, 0x0E]].concat(glyphs), true);
|
var charstringsIndex = this.createCFFIndexHeader([[0x40, 0x0E]].concat(glyphs), true);
|
||||||
charstringsIndex = charstringsIndex.join(" ").split(" "); // XXX why?
|
|
||||||
|
|
||||||
//Top Dict Index
|
//Top Dict Index
|
||||||
var topDictIndex = [
|
var topDictIndex = [
|
||||||
@ -1523,7 +1476,7 @@ CFF.prototype = {
|
|||||||
|
|
||||||
topDictIndex = topDictIndex.concat([28, 0, 0, 16]) // Encoding
|
topDictIndex = topDictIndex.concat([28, 0, 0, 16]) // Encoding
|
||||||
|
|
||||||
var charstringsOffset = charsetOffset + (charstringsCount * 2) + 1;
|
var charstringsOffset = charsetOffset + (glyphsCount * 2) + 1;
|
||||||
topDictIndex = topDictIndex.concat(this.encodeNumber(charstringsOffset));
|
topDictIndex = topDictIndex.concat(this.encodeNumber(charstringsOffset));
|
||||||
topDictIndex.push(17); // charstrings
|
topDictIndex.push(17); // charstrings
|
||||||
|
|
||||||
@ -1531,7 +1484,6 @@ CFF.prototype = {
|
|||||||
var privateOffset = charstringsOffset + charstringsIndex.length;
|
var privateOffset = charstringsOffset + charstringsIndex.length;
|
||||||
topDictIndex = topDictIndex.concat(this.encodeNumber(privateOffset));
|
topDictIndex = topDictIndex.concat(this.encodeNumber(privateOffset));
|
||||||
topDictIndex.push(18); // Private
|
topDictIndex.push(18); // Private
|
||||||
topDictIndex = topDictIndex.join(" ").split(" ");
|
|
||||||
|
|
||||||
var indexes = [
|
var indexes = [
|
||||||
topDictIndex, stringsIndex,
|
topDictIndex, stringsIndex,
|
||||||
@ -1561,7 +1513,6 @@ CFF.prototype = {
|
|||||||
139, 12, 14,
|
139, 12, 14,
|
||||||
28, 0, 55, 19
|
28, 0, 55, 19
|
||||||
]);
|
]);
|
||||||
privateData = privateData.join(" ").split(" ");
|
|
||||||
cff.set(privateData, currentOffset);
|
cff.set(privateData, currentOffset);
|
||||||
currentOffset += privateData.length;
|
currentOffset += privateData.length;
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
<link rel="stylesheet" href="multi_page_viewer.css" type="text/css" media="screen"/>
|
<link rel="stylesheet" href="multi_page_viewer.css" type="text/css" media="screen"/>
|
||||||
<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="text/javascript" src="multi_page_viewer.js"></script>
|
<script type="text/javascript" src="multi_page_viewer.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var pageTimeout;
|
||||||
|
|
||||||
var PDFViewer = {
|
var PDFViewer = {
|
||||||
queryParams: {},
|
queryParams: {},
|
||||||
|
|
||||||
@ -75,16 +77,13 @@ var PDFViewer = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
drawPage: function(num) {
|
drawPage: function(num) {
|
||||||
if (!PDFViewer.pdf) {
|
if (!PDFViewer.pdf)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
var div = document.getElementById('pageContainer' + num);
|
var div = document.getElementById('pageContainer' + num);
|
||||||
var canvas = document.createElement('canvas');
|
var canvas = document.createElement('canvas');
|
||||||
|
|
||||||
if (div && !div.hasChildNodes()) {
|
if (div && !div.hasChildNodes()) {
|
||||||
div.appendChild(canvas);
|
|
||||||
|
|
||||||
var page = PDFViewer.pdf.getPage(num);
|
var page = PDFViewer.pdf.getPage(num);
|
||||||
|
|
||||||
canvas.id = 'page' + num;
|
canvas.id = 'page' + num;
|
||||||
@ -94,6 +93,7 @@ var PDFViewer = {
|
|||||||
// are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi.
|
// are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi.
|
||||||
canvas.width = PDFViewer.pageWidth();
|
canvas.width = PDFViewer.pageWidth();
|
||||||
canvas.height = PDFViewer.pageHeight();
|
canvas.height = PDFViewer.pageHeight();
|
||||||
|
div.appendChild(canvas);
|
||||||
|
|
||||||
var ctx = canvas.getContext('2d');
|
var ctx = canvas.getContext('2d');
|
||||||
ctx.save();
|
ctx.save();
|
||||||
@ -102,54 +102,20 @@ var PDFViewer = {
|
|||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
||||||
var gfx = new CanvasGraphics(ctx);
|
var gfx = new CanvasGraphics(ctx);
|
||||||
var fonts = [];
|
|
||||||
|
|
||||||
// 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 = [];
|
||||||
page.compile(gfx, fonts);
|
page.compile(gfx, fonts);
|
||||||
|
|
||||||
var areFontsReady = true;
|
var loadFont = function() {
|
||||||
|
if (!FontLoader.bind(fonts)) {
|
||||||
// Inspect fonts and translate the missing one
|
pageTimeout = window.setTimeout(loadFont, 10);
|
||||||
var fontCount = fonts.length;
|
return;
|
||||||
|
|
||||||
for (var i = 0; i < fontCount; i++) {
|
|
||||||
var font = fonts[i];
|
|
||||||
|
|
||||||
if (Fonts[font.name]) {
|
|
||||||
areFontsReady = areFontsReady && !Fonts[font.name].loading;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
page.display(gfx);
|
||||||
new Font(font.name, font.file, font.properties);
|
|
||||||
|
|
||||||
areFontsReady = false;
|
|
||||||
}
|
}
|
||||||
|
loadFont();
|
||||||
var pageInterval;
|
|
||||||
|
|
||||||
var delayLoadFont = function() {
|
|
||||||
for (var i = 0; i < fontCount; i++) {
|
|
||||||
if (Fonts[font.name].loading) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearInterval(pageInterval);
|
|
||||||
|
|
||||||
while (div.hasChildNodes()) {
|
|
||||||
div.removeChild(div.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
PDFViewer.drawPage(num);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!areFontsReady) {
|
|
||||||
pageInterval = setInterval(delayLoadFont, 10);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
page.display(gfx);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -258,7 +224,6 @@ var PDFViewer = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
|
|
||||||
// Parse the URL query parameters into a cached object.
|
// Parse the URL query parameters into a cached object.
|
||||||
PDFViewer.queryParams = function() {
|
PDFViewer.queryParams = function() {
|
||||||
var qs = window.location.search.substring(1);
|
var qs = window.location.search.substring(1);
|
||||||
|
184
pdf.js
184
pdf.js
@ -56,6 +56,14 @@ function bytesToString(bytes) {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stringToBytes(str) {
|
||||||
|
var length = str.length;
|
||||||
|
var bytes = new Uint8Array(length);
|
||||||
|
for (var n = 0; n < length; ++n)
|
||||||
|
bytes[n] = str.charCodeAt(n) & 0xFF;
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
var Stream = (function() {
|
var Stream = (function() {
|
||||||
function constructor(arrayBuffer, start, length, dict) {
|
function constructor(arrayBuffer, start, length, dict) {
|
||||||
this.bytes = new Uint8Array(arrayBuffer);
|
this.bytes = new Uint8Array(arrayBuffer);
|
||||||
@ -71,14 +79,14 @@ var Stream = (function() {
|
|||||||
get length() {
|
get length() {
|
||||||
return this.end - this.start;
|
return this.end - this.start;
|
||||||
},
|
},
|
||||||
getByte: function() {
|
getByte: function stream_getByte() {
|
||||||
if (this.pos >= this.end)
|
if (this.pos >= this.end)
|
||||||
return;
|
return null;
|
||||||
return this.bytes[this.pos++];
|
return this.bytes[this.pos++];
|
||||||
},
|
},
|
||||||
// returns subarray of original buffer
|
// returns subarray of original buffer
|
||||||
// should only be read
|
// should only be read
|
||||||
getBytes: function(length) {
|
getBytes: function stream_getBytes(length) {
|
||||||
var bytes = this.bytes;
|
var bytes = this.bytes;
|
||||||
var pos = this.pos;
|
var pos = this.pos;
|
||||||
var strEnd = this.end;
|
var strEnd = this.end;
|
||||||
@ -93,28 +101,28 @@ var Stream = (function() {
|
|||||||
this.pos = end;
|
this.pos = end;
|
||||||
return bytes.subarray(pos, end);
|
return bytes.subarray(pos, end);
|
||||||
},
|
},
|
||||||
lookChar: function() {
|
lookChar: function stream_lookChar() {
|
||||||
if (this.pos >= this.end)
|
if (this.pos >= this.end)
|
||||||
return;
|
return null;
|
||||||
return String.fromCharCode(this.bytes[this.pos]);
|
return String.fromCharCode(this.bytes[this.pos]);
|
||||||
},
|
},
|
||||||
getChar: function() {
|
getChar: function stream_getChar() {
|
||||||
if (this.pos >= this.end)
|
if (this.pos >= this.end)
|
||||||
return;
|
return null;
|
||||||
return String.fromCharCode(this.bytes[this.pos++]);
|
return String.fromCharCode(this.bytes[this.pos++]);
|
||||||
},
|
},
|
||||||
skip: function(n) {
|
skip: function stream_skip(n) {
|
||||||
if (!n)
|
if (!n)
|
||||||
n = 1;
|
n = 1;
|
||||||
this.pos += n;
|
this.pos += n;
|
||||||
},
|
},
|
||||||
reset: function() {
|
reset: function stream_reset() {
|
||||||
this.pos = this.start;
|
this.pos = this.start;
|
||||||
},
|
},
|
||||||
moveStart: function() {
|
moveStart: function stream_moveStart() {
|
||||||
this.start = this.pos;
|
this.start = this.pos;
|
||||||
},
|
},
|
||||||
makeSubStream: function(start, length, dict) {
|
makeSubStream: function stream_makeSubstream(start, length, dict) {
|
||||||
return new Stream(this.bytes.buffer, start, length, dict);
|
return new Stream(this.bytes.buffer, start, length, dict);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -146,7 +154,7 @@ var DecodeStream = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
constructor.prototype = {
|
||||||
ensureBuffer: function(requested) {
|
ensureBuffer: function decodestream_ensureBuffer(requested) {
|
||||||
var buffer = this.buffer;
|
var buffer = this.buffer;
|
||||||
var current = buffer ? buffer.byteLength : 0;
|
var current = buffer ? buffer.byteLength : 0;
|
||||||
if (requested < current)
|
if (requested < current)
|
||||||
@ -159,16 +167,16 @@ var DecodeStream = (function() {
|
|||||||
buffer2[i] = buffer[i];
|
buffer2[i] = buffer[i];
|
||||||
return this.buffer = buffer2;
|
return this.buffer = buffer2;
|
||||||
},
|
},
|
||||||
getByte: function() {
|
getByte: function decodestream_getByte() {
|
||||||
var pos = this.pos;
|
var pos = this.pos;
|
||||||
while (this.bufferLength <= pos) {
|
while (this.bufferLength <= pos) {
|
||||||
if (this.eof)
|
if (this.eof)
|
||||||
return;
|
return null;
|
||||||
this.readBlock();
|
this.readBlock();
|
||||||
}
|
}
|
||||||
return this.buffer[this.pos++];
|
return this.buffer[this.pos++];
|
||||||
},
|
},
|
||||||
getBytes: function(length) {
|
getBytes: function decodestream_getBytes(length) {
|
||||||
var pos = this.pos;
|
var pos = this.pos;
|
||||||
|
|
||||||
if (length) {
|
if (length) {
|
||||||
@ -191,25 +199,25 @@ var DecodeStream = (function() {
|
|||||||
this.pos = end;
|
this.pos = end;
|
||||||
return this.buffer.subarray(pos, end)
|
return this.buffer.subarray(pos, end)
|
||||||
},
|
},
|
||||||
lookChar: function() {
|
lookChar: function decodestream_lookChar() {
|
||||||
var pos = this.pos;
|
var pos = this.pos;
|
||||||
while (this.bufferLength <= pos) {
|
while (this.bufferLength <= pos) {
|
||||||
if (this.eof)
|
if (this.eof)
|
||||||
return;
|
return null;
|
||||||
this.readBlock();
|
this.readBlock();
|
||||||
}
|
}
|
||||||
return String.fromCharCode(this.buffer[this.pos]);
|
return String.fromCharCode(this.buffer[this.pos]);
|
||||||
},
|
},
|
||||||
getChar: function() {
|
getChar: function decodestream_getChar() {
|
||||||
var pos = this.pos;
|
var pos = this.pos;
|
||||||
while (this.bufferLength <= pos) {
|
while (this.bufferLength <= pos) {
|
||||||
if (this.eof)
|
if (this.eof)
|
||||||
return;
|
return null;
|
||||||
this.readBlock();
|
this.readBlock();
|
||||||
}
|
}
|
||||||
return String.fromCharCode(this.buffer[this.pos++]);
|
return String.fromCharCode(this.buffer[this.pos++]);
|
||||||
},
|
},
|
||||||
skip: function(n) {
|
skip: function decodestream_skip(n) {
|
||||||
if (!n)
|
if (!n)
|
||||||
n = 1;
|
n = 1;
|
||||||
this.pos += n;
|
this.pos += n;
|
||||||
@ -635,6 +643,7 @@ var PredictorStream = (function() {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
constructor.prototype = Object.create(DecodeStream.prototype);
|
||||||
@ -707,7 +716,7 @@ var PredictorStream = (function() {
|
|||||||
var rawBytes = this.stream.getBytes(rowBytes);
|
var rawBytes = this.stream.getBytes(rowBytes);
|
||||||
|
|
||||||
var bufferLength = this.bufferLength;
|
var bufferLength = this.bufferLength;
|
||||||
var buffer = this.ensureBuffer(bufferLength + pixBytes);
|
var buffer = this.ensureBuffer(bufferLength + rowBytes);
|
||||||
|
|
||||||
var currentRow = buffer.subarray(bufferLength, bufferLength + rowBytes);
|
var currentRow = buffer.subarray(bufferLength, bufferLength + rowBytes);
|
||||||
var prevRow = buffer.subarray(bufferLength - rowBytes, bufferLength);
|
var prevRow = buffer.subarray(bufferLength - rowBytes, bufferLength);
|
||||||
@ -799,11 +808,34 @@ var JpegStream = (function() {
|
|||||||
return constructor;
|
return constructor;
|
||||||
})();
|
})();
|
||||||
var DecryptStream = (function() {
|
var DecryptStream = (function() {
|
||||||
function constructor(str, fileKey, encAlgorithm, keyLength) {
|
function constructor(str, decrypt) {
|
||||||
TODO("decrypt stream is not implemented");
|
this.str = str;
|
||||||
|
this.dict = str.dict;
|
||||||
|
this.decrypt = decrypt;
|
||||||
|
|
||||||
|
DecodeStream.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = Stream.prototype;
|
const chunkSize = 512;
|
||||||
|
|
||||||
|
constructor.prototype = Object.create(DecodeStream.prototype);
|
||||||
|
constructor.prototype.readBlock = function() {
|
||||||
|
var chunk = this.str.getBytes(chunkSize);
|
||||||
|
if (!chunk || chunk.length == 0) {
|
||||||
|
this.eof = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var decrypt = this.decrypt;
|
||||||
|
chunk = decrypt(chunk);
|
||||||
|
|
||||||
|
var bufferLength = this.bufferLength;
|
||||||
|
var i, n = chunk.length;
|
||||||
|
var buffer = this.ensureBuffer(bufferLength + n);
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
buffer[bufferLength++] = chunk[i];
|
||||||
|
this.bufferLength = bufferLength;
|
||||||
|
this.eof = n < chunkSize;
|
||||||
|
};
|
||||||
|
|
||||||
return constructor;
|
return constructor;
|
||||||
})();
|
})();
|
||||||
@ -1883,7 +1915,9 @@ var Dict = (function() {
|
|||||||
|
|
||||||
constructor.prototype = {
|
constructor.prototype = {
|
||||||
get: function(key) {
|
get: function(key) {
|
||||||
return this.map[key];
|
if (key in this.map)
|
||||||
|
return this.map[key];
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
get2: function(key1, key2) {
|
get2: function(key1, key2) {
|
||||||
return this.get(key1) || this.get(key2);
|
return this.get(key1) || this.get(key2);
|
||||||
@ -1954,7 +1988,7 @@ function IsArray(v) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function IsStream(v) {
|
function IsStream(v) {
|
||||||
return typeof v == "object" && "getChar" in v;
|
return typeof v == "object" && v != null && ("getChar" in v);
|
||||||
}
|
}
|
||||||
|
|
||||||
function IsRef(v) {
|
function IsRef(v) {
|
||||||
@ -2023,10 +2057,10 @@ var Lexer = (function() {
|
|||||||
|
|
||||||
function ToHexDigit(ch) {
|
function ToHexDigit(ch) {
|
||||||
if (ch >= "0" && ch <= "9")
|
if (ch >= "0" && ch <= "9")
|
||||||
return ch - "0";
|
return ch.charCodeAt(0) - 48;
|
||||||
ch = ch.toLowerCase();
|
ch = ch.toUpperCase();
|
||||||
if (ch >= "a" && ch <= "f")
|
if (ch >= "A" && ch <= "F")
|
||||||
return ch - "a";
|
return ch.charCodeAt(0) - 55;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2320,7 +2354,7 @@ var Parser = (function() {
|
|||||||
// don't buffer inline image data
|
// don't buffer inline image data
|
||||||
this.buf2 = (this.inlineImg > 0) ? null : this.lexer.getObj();
|
this.buf2 = (this.inlineImg > 0) ? null : this.lexer.getObj();
|
||||||
},
|
},
|
||||||
getObj: function() {
|
getObj: function(cipherTransform) {
|
||||||
// refill buffer after inline image data
|
// refill buffer after inline image data
|
||||||
if (this.inlineImg == 2)
|
if (this.inlineImg == 2)
|
||||||
this.refill();
|
this.refill();
|
||||||
@ -2346,7 +2380,7 @@ var Parser = (function() {
|
|||||||
this.shift();
|
this.shift();
|
||||||
if (IsEOF(this.buf1))
|
if (IsEOF(this.buf1))
|
||||||
break;
|
break;
|
||||||
dict.set(key, this.getObj());
|
dict.set(key, this.getObj(cipherTransform));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (IsEOF(this.buf1))
|
if (IsEOF(this.buf1))
|
||||||
@ -2355,7 +2389,7 @@ var Parser = (function() {
|
|||||||
// stream objects are not allowed inside content streams or
|
// stream objects are not allowed inside content streams or
|
||||||
// object streams
|
// object streams
|
||||||
if (this.allowStreams && IsCmd(this.buf2, "stream")) {
|
if (this.allowStreams && IsCmd(this.buf2, "stream")) {
|
||||||
return this.makeStream(dict);
|
return this.makeStream(dict, cipherTransform);
|
||||||
} else {
|
} else {
|
||||||
this.shift();
|
this.shift();
|
||||||
}
|
}
|
||||||
@ -2374,17 +2408,8 @@ var Parser = (function() {
|
|||||||
} else if (IsString(this.buf1)) { // string
|
} else if (IsString(this.buf1)) { // string
|
||||||
var str = this.buf1;
|
var str = this.buf1;
|
||||||
this.shift();
|
this.shift();
|
||||||
if (this.fileKey) {
|
if (cipherTransform)
|
||||||
var decrypt = new DecryptStream(new StringStream(str),
|
str = cipherTransform.decryptString(str);
|
||||||
this.fileKey,
|
|
||||||
this.encAlgorithm,
|
|
||||||
this.keyLength);
|
|
||||||
var str = "";
|
|
||||||
var pos = decrypt.pos;
|
|
||||||
var length = decrypt.length;
|
|
||||||
while (pos++ > length)
|
|
||||||
str += decrypt.getChar();
|
|
||||||
}
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2393,7 +2418,7 @@ var Parser = (function() {
|
|||||||
this.shift();
|
this.shift();
|
||||||
return obj;
|
return obj;
|
||||||
},
|
},
|
||||||
makeStream: function(dict) {
|
makeStream: function(dict, cipherTransform) {
|
||||||
var lexer = this.lexer;
|
var lexer = this.lexer;
|
||||||
var stream = lexer.stream;
|
var stream = lexer.stream;
|
||||||
|
|
||||||
@ -2420,12 +2445,8 @@ var Parser = (function() {
|
|||||||
this.shift();
|
this.shift();
|
||||||
|
|
||||||
stream = stream.makeSubStream(pos, length, dict);
|
stream = stream.makeSubStream(pos, length, dict);
|
||||||
if (this.fileKey) {
|
if (cipherTransform)
|
||||||
stream = new DecryptStream(stream,
|
stream = cipherTransform.createStream(stream);
|
||||||
this.fileKey,
|
|
||||||
this.encAlgorithm,
|
|
||||||
this.keyLength);
|
|
||||||
}
|
|
||||||
stream = this.filter(stream, dict, length);
|
stream = this.filter(stream, dict, length);
|
||||||
stream.parameters = dict;
|
stream.parameters = dict;
|
||||||
return stream;
|
return stream;
|
||||||
@ -2559,16 +2580,22 @@ var XRef = (function() {
|
|||||||
this.xrefstms = {};
|
this.xrefstms = {};
|
||||||
var trailerDict = this.readXRef(startXRef);
|
var trailerDict = this.readXRef(startXRef);
|
||||||
|
|
||||||
|
// prepare the XRef cache
|
||||||
|
this.cache = [];
|
||||||
|
|
||||||
|
var encrypt = trailerDict.get("Encrypt");
|
||||||
|
if (encrypt) {
|
||||||
|
var fileId = trailerDict.get("ID");
|
||||||
|
this.encrypt = new CipherTransformFactory(this.fetch(encrypt), fileId[0] /*, password */);
|
||||||
|
}
|
||||||
|
|
||||||
// get the root dictionary (catalog) object
|
// get the root dictionary (catalog) object
|
||||||
if (!IsRef(this.root = trailerDict.get("Root")))
|
if (!IsRef(this.root = trailerDict.get("Root")))
|
||||||
error("Invalid root reference");
|
error("Invalid root reference");
|
||||||
|
|
||||||
// prepare the XRef cache
|
|
||||||
this.cache = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
constructor.prototype = {
|
||||||
readXRefTable: function(parser) {
|
readXRefTable: function readXRefTable(parser) {
|
||||||
var obj;
|
var obj;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (IsCmd(obj = parser.getObj(), "trailer"))
|
if (IsCmd(obj = parser.getObj(), "trailer"))
|
||||||
@ -2639,7 +2666,7 @@ var XRef = (function() {
|
|||||||
|
|
||||||
return dict;
|
return dict;
|
||||||
},
|
},
|
||||||
readXRefStream: function(stream) {
|
readXRefStream: function readXRefStream(stream) {
|
||||||
var streamParameters = stream.parameters;
|
var streamParameters = stream.parameters;
|
||||||
var length = streamParameters.get("Length");
|
var length = streamParameters.get("Length");
|
||||||
var byteWidths = streamParameters.get("W");
|
var byteWidths = streamParameters.get("W");
|
||||||
@ -2691,7 +2718,7 @@ var XRef = (function() {
|
|||||||
this.readXRef(prev);
|
this.readXRef(prev);
|
||||||
return streamParameters;
|
return streamParameters;
|
||||||
},
|
},
|
||||||
readXRef: function(startXRef) {
|
readXRef: function readXref(startXRef) {
|
||||||
var stream = this.stream;
|
var stream = this.stream;
|
||||||
stream.pos = startXRef;
|
stream.pos = startXRef;
|
||||||
var parser = new Parser(new Lexer(stream), true);
|
var parser = new Parser(new Lexer(stream), true);
|
||||||
@ -2709,6 +2736,7 @@ var XRef = (function() {
|
|||||||
return this.readXRefStream(obj);
|
return this.readXRefStream(obj);
|
||||||
}
|
}
|
||||||
error("Invalid XRef");
|
error("Invalid XRef");
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
getEntry: function(i) {
|
getEntry: function(i) {
|
||||||
var e = this.entries[i];
|
var e = this.entries[i];
|
||||||
@ -2752,7 +2780,11 @@ var XRef = (function() {
|
|||||||
}
|
}
|
||||||
error("bad XRef entry");
|
error("bad XRef entry");
|
||||||
}
|
}
|
||||||
e = parser.getObj();
|
if (this.encrypt) {
|
||||||
|
e = parser.getObj(this.encrypt.createCipherTransform(num, gen));
|
||||||
|
} else {
|
||||||
|
e = parser.getObj();
|
||||||
|
}
|
||||||
// Don't cache streams since they are mutable.
|
// Don't cache streams since they are mutable.
|
||||||
if (!IsStream(e))
|
if (!IsStream(e))
|
||||||
this.cache[num] = e;
|
this.cache[num] = e;
|
||||||
@ -3374,7 +3406,7 @@ var CanvasGraphics = (function() {
|
|||||||
if (!fd)
|
if (!fd)
|
||||||
// XXX deprecated "special treatment" for standard
|
// XXX deprecated "special treatment" for standard
|
||||||
// fonts? What do we need to do here?
|
// fonts? What do we need to do here?
|
||||||
return;
|
return null;
|
||||||
var descriptor = xref.fetch(fd);
|
var descriptor = xref.fetch(fd);
|
||||||
|
|
||||||
var fontName = descriptor.get("FontName");
|
var fontName = descriptor.get("FontName");
|
||||||
@ -3386,16 +3418,9 @@ var CanvasGraphics = (function() {
|
|||||||
error("FontFile not found for font: " + fontName);
|
error("FontFile not found for font: " + fontName);
|
||||||
fontFile = xref.fetchIfRef(fontFile);
|
fontFile = xref.fetchIfRef(fontFile);
|
||||||
|
|
||||||
// Fonts with an embedded cmap but without any assignment in
|
|
||||||
// it are not yet supported, so ask the fonts loader to ignore
|
|
||||||
// them to not pay a stupid one sec latence.
|
|
||||||
var ignoreFont = false;
|
|
||||||
|
|
||||||
var encodingMap = {};
|
var encodingMap = {};
|
||||||
var charset = [];
|
var charset = [];
|
||||||
if (fontDict.has("Encoding")) {
|
if (fontDict.has("Encoding")) {
|
||||||
ignoreFont = false;
|
|
||||||
|
|
||||||
var encoding = xref.fetchIfRef(fontDict.get("Encoding"));
|
var encoding = xref.fetchIfRef(fontDict.get("Encoding"));
|
||||||
if (IsDict(encoding)) {
|
if (IsDict(encoding)) {
|
||||||
// Build a map between codes and glyphs
|
// Build a map between codes and glyphs
|
||||||
@ -3418,9 +3443,8 @@ var CanvasGraphics = (function() {
|
|||||||
error("Unknown font encoding");
|
error("Unknown font encoding");
|
||||||
|
|
||||||
var index = 0;
|
var index = 0;
|
||||||
for (var j = 0; j < encoding.length; j++) {
|
for (var j = 0; j < encoding.length; j++)
|
||||||
encodingMap[index++] = GlyphsUnicode[encoding[j]];
|
encodingMap[index++] = GlyphsUnicode[encoding[j]];
|
||||||
}
|
|
||||||
|
|
||||||
var firstChar = xref.fetchIfRef(fontDict.get("FirstChar"));
|
var firstChar = xref.fetchIfRef(fontDict.get("FirstChar"));
|
||||||
var widths = xref.fetchIfRef(fontDict.get("Widths"));
|
var widths = xref.fetchIfRef(fontDict.get("Widths"));
|
||||||
@ -3444,13 +3468,7 @@ var CanvasGraphics = (function() {
|
|||||||
var tokens = [];
|
var tokens = [];
|
||||||
var token = "";
|
var token = "";
|
||||||
|
|
||||||
var length = cmapObj.length;
|
var cmap = cmapObj.getBytes(cmapObj.length);
|
||||||
if (cmapObj instanceof FlateStream) {
|
|
||||||
cmapObj.readBlock();
|
|
||||||
length = cmapObj.bufferLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmap = cmapObj.getBytes(length);
|
|
||||||
for (var i =0; i < cmap.length; i++) {
|
for (var i =0; i < cmap.length; i++) {
|
||||||
var byte = cmap[i];
|
var byte = cmap[i];
|
||||||
if (byte == 0x20 || byte == 0x0A || byte == 0x3C || byte == 0x3E) {
|
if (byte == 0x20 || byte == 0x0A || byte == 0x3C || byte == 0x3E) {
|
||||||
@ -3460,7 +3478,6 @@ var CanvasGraphics = (function() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "beginbfrange":
|
case "beginbfrange":
|
||||||
ignoreFont = false;
|
|
||||||
case "begincodespacerange":
|
case "begincodespacerange":
|
||||||
token = "";
|
token = "";
|
||||||
tokens = [];
|
tokens = [];
|
||||||
@ -3507,16 +3524,19 @@ var CanvasGraphics = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var subType = fontDict.get("Subtype");
|
var subType = fontDict.get("Subtype");
|
||||||
var bbox = descriptor.get("FontBBox");
|
assertWellFormed(IsName(subType), "invalid font Subtype");
|
||||||
assertWellFormed(IsName(subType) && IsArray(bbox),
|
|
||||||
"invalid font Subtype or FontBBox");
|
|
||||||
|
|
||||||
var properties = {
|
var properties = {
|
||||||
type: subType.name,
|
type: subType.name,
|
||||||
encoding: encodingMap,
|
encoding: encodingMap,
|
||||||
charset: charset,
|
charset: charset,
|
||||||
bbox: bbox,
|
bbox: descriptor.get("FontBBox"),
|
||||||
ignore: ignoreFont
|
ascent: descriptor.get("Ascent"),
|
||||||
|
descent: descriptor.get("Descent"),
|
||||||
|
xHeight: descriptor.get("XHeight"),
|
||||||
|
capHeight: descriptor.get("CapHeight"),
|
||||||
|
flags: descriptor.get("Flags"),
|
||||||
|
italicAngle: descriptor.get("ItalicAngle")
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -4027,7 +4047,7 @@ var CanvasGraphics = (function() {
|
|||||||
this.restore();
|
this.restore();
|
||||||
|
|
||||||
TODO("Inverse pattern is painted");
|
TODO("Inverse pattern is painted");
|
||||||
var pattern = this.ctx.createPattern(tmpCanvas, "repeat");
|
pattern = this.ctx.createPattern(tmpCanvas, "repeat");
|
||||||
this.ctx.fillStyle = pattern;
|
this.ctx.fillStyle = pattern;
|
||||||
},
|
},
|
||||||
setStrokeGray: function(gray) {
|
setStrokeGray: function(gray) {
|
||||||
|
1
test/pdfs/intelisa.pdf.link
Normal file
1
test/pdfs/intelisa.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
http://www.intel.com/Assets/PDF/manual/253665.pdf
|
BIN
test/resources/favicon.ico
Normal file
BIN
test/resources/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -32,3 +32,5 @@ user_pref("app.update.enabled", false);
|
|||||||
user_pref("browser.panorama.experienced_first_run", true); // Assume experienced
|
user_pref("browser.panorama.experienced_first_run", true); // Assume experienced
|
||||||
user_pref("dom.w3c_touch_events.enabled", true);
|
user_pref("dom.w3c_touch_events.enabled", true);
|
||||||
user_pref("extensions.checkCompatibility", false);
|
user_pref("extensions.checkCompatibility", false);
|
||||||
|
user_pref("extensions.installDistroAddons", false); // prevent testpilot etc
|
||||||
|
user_pref("browser.safebrowsing.enable", false); // prevent traffic to google servers
|
||||||
|
93
test/test.py
93
test/test.py
@ -1,4 +1,4 @@
|
|||||||
import json, platform, os, shutil, sys, subprocess, tempfile, threading, urllib, urllib2
|
import json, platform, os, shutil, sys, subprocess, tempfile, threading, time, urllib, urllib2
|
||||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||||
import SocketServer
|
import SocketServer
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
@ -51,6 +51,7 @@ MIMEs = {
|
|||||||
'.json': 'application/json',
|
'.json': 'application/json',
|
||||||
'.pdf': 'application/pdf',
|
'.pdf': 'application/pdf',
|
||||||
'.xhtml': 'application/xhtml+xml',
|
'.xhtml': 'application/xhtml+xml',
|
||||||
|
'.ico': 'image/x-icon'
|
||||||
}
|
}
|
||||||
|
|
||||||
class State:
|
class State:
|
||||||
@ -69,9 +70,10 @@ class State:
|
|||||||
eqLog = None
|
eqLog = None
|
||||||
|
|
||||||
class Result:
|
class Result:
|
||||||
def __init__(self, snapshot, failure):
|
def __init__(self, snapshot, failure, page):
|
||||||
self.snapshot = snapshot
|
self.snapshot = snapshot
|
||||||
self.failure = failure
|
self.failure = failure
|
||||||
|
self.page = page
|
||||||
|
|
||||||
class TestServer(SocketServer.TCPServer):
|
class TestServer(SocketServer.TCPServer):
|
||||||
allow_reuse_address = True
|
allow_reuse_address = True
|
||||||
@ -83,6 +85,14 @@ class PDFTestHandler(BaseHTTPRequestHandler):
|
|||||||
if VERBOSE:
|
if VERBOSE:
|
||||||
BaseHTTPRequestHandler.log_request(code, size)
|
BaseHTTPRequestHandler.log_request(code, size)
|
||||||
|
|
||||||
|
def sendFile(self, path, ext):
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-Type", MIMEs[ext])
|
||||||
|
self.send_header("Content-Length", os.path.getsize(path))
|
||||||
|
self.end_headers()
|
||||||
|
with open(path) as f:
|
||||||
|
self.wfile.write(f.read())
|
||||||
|
|
||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
url = urlparse(self.path)
|
url = urlparse(self.path)
|
||||||
# Ignore query string
|
# Ignore query string
|
||||||
@ -91,9 +101,14 @@ class PDFTestHandler(BaseHTTPRequestHandler):
|
|||||||
prefix = os.path.commonprefix(( path, DOC_ROOT ))
|
prefix = os.path.commonprefix(( path, DOC_ROOT ))
|
||||||
_, ext = os.path.splitext(path)
|
_, ext = os.path.splitext(path)
|
||||||
|
|
||||||
|
if url.path == "/favicon.ico":
|
||||||
|
self.sendFile(os.path.join(DOC_ROOT, "test", "resources", "favicon.ico"), ext)
|
||||||
|
return
|
||||||
|
|
||||||
if not (prefix == DOC_ROOT
|
if not (prefix == DOC_ROOT
|
||||||
and os.path.isfile(path)
|
and os.path.isfile(path)
|
||||||
and ext in MIMEs):
|
and ext in MIMEs):
|
||||||
|
print path
|
||||||
self.send_error(404)
|
self.send_error(404)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -102,14 +117,8 @@ class PDFTestHandler(BaseHTTPRequestHandler):
|
|||||||
self.send_error(501)
|
self.send_error(501)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.send_response(200)
|
self.sendFile(path, ext)
|
||||||
self.send_header("Content-Type", MIMEs[ext])
|
|
||||||
self.end_headers()
|
|
||||||
|
|
||||||
# Sigh, os.sendfile() plz
|
|
||||||
f = open(path)
|
|
||||||
self.wfile.write(f.read())
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
|
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
@ -122,10 +131,20 @@ class PDFTestHandler(BaseHTTPRequestHandler):
|
|||||||
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]
|
||||||
taskResults[round].append(Result(snapshot, failure))
|
taskResults[round].append(Result(snapshot, failure, page))
|
||||||
assert len(taskResults[round]) == page
|
|
||||||
|
|
||||||
if result['taskDone']:
|
def isTaskDone():
|
||||||
|
numPages = result["numPages"]
|
||||||
|
rounds = State.manifest[id]["rounds"]
|
||||||
|
for round in range(0,rounds):
|
||||||
|
if len(taskResults[round]) < numPages:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
if isTaskDone():
|
||||||
|
# sort the results since they sometimes come in out of order
|
||||||
|
for results in taskResults:
|
||||||
|
results.sort(key=lambda result: result.page)
|
||||||
check(State.manifest[id], taskResults, browser)
|
check(State.manifest[id], taskResults, browser)
|
||||||
# Please oh please GC this ...
|
# Please oh please GC this ...
|
||||||
del State.taskResults[browser][id]
|
del State.taskResults[browser][id]
|
||||||
@ -138,6 +157,8 @@ class BrowserCommand():
|
|||||||
def __init__(self, browserRecord):
|
def __init__(self, browserRecord):
|
||||||
self.name = browserRecord["name"]
|
self.name = browserRecord["name"]
|
||||||
self.path = browserRecord["path"]
|
self.path = browserRecord["path"]
|
||||||
|
self.tempDir = None
|
||||||
|
self.process = None
|
||||||
|
|
||||||
if platform.system() == "Darwin" and (self.path.endswith(".app") or self.path.endswith(".app/")):
|
if platform.system() == "Darwin" and (self.path.endswith(".app") or self.path.endswith(".app/")):
|
||||||
self._fixupMacPath()
|
self._fixupMacPath()
|
||||||
@ -151,19 +172,30 @@ class BrowserCommand():
|
|||||||
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")
|
||||||
print self.profileDir
|
|
||||||
shutil.copytree(os.path.join(DOC_ROOT, "test", "resources", "firefox"),
|
shutil.copytree(os.path.join(DOC_ROOT, "test", "resources", "firefox"),
|
||||||
self.profileDir)
|
self.profileDir)
|
||||||
|
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
shutil.rmtree(self.tempDir)
|
# If the browser is still running, wait up to ten seconds for it to quit
|
||||||
|
if self.process and self.process.poll() is None:
|
||||||
|
checks = 0
|
||||||
|
while self.process.poll() is None and checks < 20:
|
||||||
|
checks += 1
|
||||||
|
time.sleep(.5)
|
||||||
|
# If it's still not dead, try to kill it
|
||||||
|
if self.process.poll() is None:
|
||||||
|
print "Process %s is still running. Killing." % self.name
|
||||||
|
self.process.kill()
|
||||||
|
|
||||||
|
if self.tempDir is not None and os.path.exists(self.tempDir):
|
||||||
|
shutil.rmtree(self.tempDir)
|
||||||
|
|
||||||
def start(self, url):
|
def start(self, url):
|
||||||
cmds = [self.path]
|
cmds = [self.path]
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
cmds.append("-foreground")
|
cmds.append("-foreground")
|
||||||
cmds.extend(["-no-remote", "-profile", self.profileDir, url])
|
cmds.extend(["-no-remote", "-profile", self.profileDir, url])
|
||||||
subprocess.call(cmds)
|
self.process = subprocess.Popen(cmds)
|
||||||
|
|
||||||
def makeBrowserCommands(browserManifestFile):
|
def makeBrowserCommands(browserManifestFile):
|
||||||
with open(browserManifestFile) as bmf:
|
with open(browserManifestFile) as bmf:
|
||||||
@ -223,14 +255,23 @@ def setUp(options):
|
|||||||
|
|
||||||
State.remaining = len(testBrowsers) * len(manifestList)
|
State.remaining = len(testBrowsers) * len(manifestList)
|
||||||
|
|
||||||
for b in testBrowsers:
|
return testBrowsers
|
||||||
|
|
||||||
|
def startBrowsers(browsers, options):
|
||||||
|
for b in browsers:
|
||||||
|
b.setup()
|
||||||
|
print 'Launching', b.name
|
||||||
|
qs = 'browser='+ urllib.quote(b.name) +'&manifestFile='+ urllib.quote(options.manifestFile)
|
||||||
|
b.start('http://localhost:8080/test/test_slave.html?'+ qs)
|
||||||
|
|
||||||
|
def teardownBrowsers(browsers):
|
||||||
|
for b in browsers:
|
||||||
try:
|
try:
|
||||||
b.setup()
|
|
||||||
print 'Launching', b.name
|
|
||||||
qs = 'browser='+ urllib.quote(b.name) +'&manifestFile='+ urllib.quote(options.manifestFile)
|
|
||||||
b.start('http://localhost:8080/test/test_slave.html?'+ qs)
|
|
||||||
finally:
|
|
||||||
b.teardown()
|
b.teardown()
|
||||||
|
except:
|
||||||
|
print "Error cleaning up after browser at ", b.path
|
||||||
|
print "Temp dir was ", b.tempDir
|
||||||
|
print "Error:", sys.exc_info()[0]
|
||||||
|
|
||||||
def check(task, results, browser):
|
def check(task, results, browser):
|
||||||
failed = False
|
failed = False
|
||||||
@ -385,8 +426,14 @@ def main():
|
|||||||
httpd_thread.setDaemon(True)
|
httpd_thread.setDaemon(True)
|
||||||
httpd_thread.start()
|
httpd_thread.start()
|
||||||
|
|
||||||
setUp(options)
|
browsers = setUp(options)
|
||||||
processResults()
|
try:
|
||||||
|
startBrowsers(browsers, options)
|
||||||
|
while not State.done:
|
||||||
|
time.sleep(1)
|
||||||
|
processResults()
|
||||||
|
finally:
|
||||||
|
teardownBrowsers(browsers)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
@ -14,6 +14,12 @@
|
|||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "load"
|
"type": "load"
|
||||||
},
|
},
|
||||||
|
{ "id": "intelisa-load",
|
||||||
|
"file": "pdfs/intelisa.pdf",
|
||||||
|
"link": true,
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "load"
|
||||||
|
},
|
||||||
{ "id": "pdfspec-load",
|
{ "id": "pdfspec-load",
|
||||||
"file": "pdfs/pdf.pdf",
|
"file": "pdfs/pdf.pdf",
|
||||||
"link": true,
|
"link": true,
|
||||||
|
@ -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, pdfDoc, stdout;
|
var 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);
|
||||||
@ -63,7 +63,15 @@ function nextTask() {
|
|||||||
if (r.readyState == 4) {
|
if (r.readyState == 4) {
|
||||||
var data = r.mozResponseArrayBuffer || r.mozResponse ||
|
var data = r.mozResponseArrayBuffer || r.mozResponse ||
|
||||||
r.responseArrayBuffer || r.response;
|
r.responseArrayBuffer || r.response;
|
||||||
pdfDoc = new PDFDoc(new Stream(data));
|
|
||||||
|
try {
|
||||||
|
pdfDoc = new PDFDoc(new Stream(data));
|
||||||
|
numPages = pdfDoc.numPages;
|
||||||
|
} catch(e) {
|
||||||
|
numPages = 1;
|
||||||
|
failure = 'load PDF doc: '+ e.toString();
|
||||||
|
}
|
||||||
|
|
||||||
currentTask.pageNum = 1, nextPage();
|
currentTask.pageNum = 1, nextPage();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -71,7 +79,7 @@ function nextTask() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function nextPage() {
|
function nextPage() {
|
||||||
if (currentTask.pageNum > pdfDoc.numPages) {
|
if (currentTask.pageNum > numPages) {
|
||||||
if (++currentTask.round < currentTask.rounds) {
|
if (++currentTask.round < currentTask.rounds) {
|
||||||
log(" Round "+ (1 + currentTask.round) +"\n");
|
log(" Round "+ (1 + currentTask.round) +"\n");
|
||||||
currentTask.pageNum = 1;
|
currentTask.pageNum = 1;
|
||||||
@ -88,43 +96,34 @@ function nextPage() {
|
|||||||
clear(ctx);
|
clear(ctx);
|
||||||
|
|
||||||
var fonts = [];
|
var fonts = [];
|
||||||
var fontsReady = true;
|
var gfx = null;
|
||||||
var gfx = new CanvasGraphics(ctx);
|
|
||||||
try {
|
try {
|
||||||
|
gfx = new CanvasGraphics(ctx);
|
||||||
currentPage = pdfDoc.getPage(currentTask.pageNum);
|
currentPage = pdfDoc.getPage(currentTask.pageNum);
|
||||||
currentPage.compile(gfx, fonts);
|
currentPage.compile(gfx, fonts);
|
||||||
|
|
||||||
// Inspect fonts and translate the missing ones
|
|
||||||
var count = fonts.length;
|
|
||||||
for (var i = 0; i < count; ++i) {
|
|
||||||
var font = fonts[i];
|
|
||||||
if (Fonts[font.name]) {
|
|
||||||
fontsReady = fontsReady && !Fonts[font.name].loading;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
new Font(font.name, font.file, font.properties);
|
|
||||||
fontsReady = false;
|
|
||||||
}
|
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
failure = 'compile: '+ e.toString();
|
failure = 'compile: '+ e.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkFontsLoadedIntervalTimer = null;
|
var fontLoaderTimer = null;
|
||||||
function checkFontsLoaded() {
|
function checkFontsLoaded() {
|
||||||
for (var i = 0; i < count; i++) {
|
try {
|
||||||
if (Fonts[font.name].loading) {
|
if (!FontLoader.bind(fonts)) {
|
||||||
|
fontLoaderTimer = window.setTimeout(checkFontsLoaded, 10);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} catch(e) {
|
||||||
|
failure = 'fonts: '+ e.toString();
|
||||||
}
|
}
|
||||||
window.clearInterval(checkFontsLoadedIntervalTimer);
|
|
||||||
|
|
||||||
snapshotCurrentPage(gfx);
|
snapshotCurrentPage(gfx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (failure || fontsReady) {
|
if (failure) {
|
||||||
|
// Skip font loading if there was a failure, since the fonts might
|
||||||
|
// be in an inconsistent state.
|
||||||
snapshotCurrentPage(gfx);
|
snapshotCurrentPage(gfx);
|
||||||
} else {
|
} else {
|
||||||
checkFontsLoadedIntervalTimer = setInterval(checkFontsLoaded, 10);
|
checkFontsLoaded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,31 +138,41 @@ function snapshotCurrentPage(gfx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTask.taskDone = (currentTask.pageNum == pdfDoc.numPages
|
|
||||||
&& (1 + currentTask.round) == currentTask.rounds);
|
|
||||||
sendTaskResult(canvas.toDataURL("image/png"));
|
sendTaskResult(canvas.toDataURL("image/png"));
|
||||||
log("done"+ (failure ? " (failed!)" : "") +"\n");
|
log("done"+ (failure ? " (failed!)" : "") +"\n");
|
||||||
|
|
||||||
++currentTask.pageNum, nextPage();
|
// Set up the next request
|
||||||
}
|
backoff = (inFlightRequests > 0) ? inFlightRequests * 10 : 0;
|
||||||
|
|
||||||
function done() {
|
|
||||||
log("Done!\n");
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
document.body.innerHTML = "Tests are finished. <h1>CLOSE ME!</h1>";
|
++currentTask.pageNum, nextPage();
|
||||||
if (window.SpecialPowers)
|
|
||||||
SpecialPowers.quitApplication();
|
|
||||||
else
|
|
||||||
window.close();
|
|
||||||
},
|
},
|
||||||
100
|
backoff
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function quitApp() {
|
||||||
|
log("Done!");
|
||||||
|
document.body.innerHTML = "Tests are finished. <h1>CLOSE ME!</h1>";
|
||||||
|
if (window.SpecialPowers)
|
||||||
|
SpecialPowers.quitApplication();
|
||||||
|
else
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function done() {
|
||||||
|
if (inFlightRequests > 0) {
|
||||||
|
document.getElementById("inFlightCount").innerHTML = inFlightRequests;
|
||||||
|
setTimeout(done, 100);
|
||||||
|
} else {
|
||||||
|
setTimeout(quitApp, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var inFlightRequests = 0;
|
||||||
function sendTaskResult(snapshot) {
|
function sendTaskResult(snapshot) {
|
||||||
var result = { browser: browser,
|
var result = { browser: browser,
|
||||||
id: currentTask.id,
|
id: currentTask.id,
|
||||||
taskDone: currentTask.taskDone,
|
numPages: numPages,
|
||||||
failure: failure,
|
failure: failure,
|
||||||
file: currentTask.file,
|
file: currentTask.file,
|
||||||
round: currentTask.round,
|
round: currentTask.round,
|
||||||
@ -172,9 +181,14 @@ function sendTaskResult(snapshot) {
|
|||||||
|
|
||||||
var r = new XMLHttpRequest();
|
var r = new XMLHttpRequest();
|
||||||
// (The POST URI is ignored atm.)
|
// (The POST URI is ignored atm.)
|
||||||
r.open("POST", "/submit_task_results", false);
|
r.open("POST", "/submit_task_results", true);
|
||||||
r.setRequestHeader("Content-Type", "application/json");
|
r.setRequestHeader("Content-Type", "application/json");
|
||||||
// XXX async
|
r.onreadystatechange = function(e) {
|
||||||
|
if (r.readyState == 4) {
|
||||||
|
inFlightRequests--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.getElementById("inFlightCount").innerHTML = inFlightRequests++;
|
||||||
r.send(JSON.stringify(result));
|
r.send(JSON.stringify(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +208,8 @@ function log(str) {
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body onload="load();">
|
<body onload="load();">
|
||||||
<pre id="stdout"></pre>
|
<pre style="width:800; height:800; overflow: scroll;"id="stdout"></pre>
|
||||||
|
<p>Inflight requests: <span id="inFlightCount"></span></p>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
<script type="text/javascript" src="viewer.js"></script>
|
<script type="text/javascript" src="viewer.js"></script>
|
||||||
<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>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
35
viewer.js
35
viewer.js
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var pdfDocument, canvas, pageDisplay, pageNum, numPages, pageInterval;
|
var pdfDocument, canvas, pageDisplay, pageNum, numPages, pageTimeout;
|
||||||
function load(userInput) {
|
function load(userInput) {
|
||||||
canvas = document.getElementById("canvas");
|
canvas = document.getElementById("canvas");
|
||||||
canvas.mozOpaque = true;
|
canvas.mozOpaque = true;
|
||||||
@ -52,7 +52,7 @@ function gotoPage(num) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function displayPage(num) {
|
function displayPage(num) {
|
||||||
window.clearInterval(pageInterval);
|
window.clearTimeout(pageTimeout);
|
||||||
|
|
||||||
document.getElementById("pageNumber").value = num;
|
document.getElementById("pageNumber").value = num;
|
||||||
|
|
||||||
@ -75,28 +75,12 @@ function displayPage(num) {
|
|||||||
page.compile(gfx, fonts);
|
page.compile(gfx, fonts);
|
||||||
var t2 = Date.now();
|
var t2 = Date.now();
|
||||||
|
|
||||||
var fontsReady = true;
|
function loadFont() {
|
||||||
|
if (!FontLoader.bind(fonts)) {
|
||||||
// Inspect fonts and translate the missing one
|
pageTimeout = window.setTimeout(loadFont, 10);
|
||||||
var count = fonts.length;
|
return;
|
||||||
for (var i = 0; i < count; i++) {
|
|
||||||
var font = fonts[i];
|
|
||||||
if (Fonts[font.name]) {
|
|
||||||
fontsReady = fontsReady && !Fonts[font.name].loading;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new Font(font.name, font.file, font.properties);
|
|
||||||
fontsReady = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function delayLoadFont() {
|
|
||||||
for (var i = 0; i < count; i++) {
|
|
||||||
if (Fonts[font.name].loading)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
window.clearInterval(pageInterval);
|
|
||||||
|
|
||||||
var t3 = Date.now();
|
var t3 = Date.now();
|
||||||
|
|
||||||
page.display(gfx);
|
page.display(gfx);
|
||||||
@ -106,12 +90,7 @@ function displayPage(num) {
|
|||||||
var infoDisplay = document.getElementById("info");
|
var infoDisplay = document.getElementById("info");
|
||||||
infoDisplay.innerHTML = "Time to load/compile/fonts/render: "+ (t1 - t0) + "/" + (t2 - t1) + "/" + (t3 - t2) + "/" + (t4 - t3) + " ms";
|
infoDisplay.innerHTML = "Time to load/compile/fonts/render: "+ (t1 - t0) + "/" + (t2 - t1) + "/" + (t3 - t2) + "/" + (t4 - t3) + " ms";
|
||||||
};
|
};
|
||||||
|
loadFont();
|
||||||
if (fontsReady) {
|
|
||||||
delayLoadFont();
|
|
||||||
} else {
|
|
||||||
pageInterval = setInterval(delayLoadFont, 10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextPage() {
|
function nextPage() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user