Merge remote branch 'upstream/master'

Conflicts:
	web/viewer.js
This commit is contained in:
Vivien Nicolas 2011-08-23 16:40:49 +02:00
commit 62e7d2f608
18 changed files with 1923 additions and 608 deletions

View File

@ -7,6 +7,7 @@
Vivien Nicolas <21@vingtetun.org> Vivien Nicolas <21@vingtetun.org>
Justin D'Arcangelo <justindarc@gmail.com> Justin D'Arcangelo <justindarc@gmail.com>
Yury Delendik Yury Delendik
Kalervo Kujala
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
@ -25,3 +26,4 @@
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.

View File

@ -86,7 +86,7 @@ font-test:
# To install gjslint, see: # To install gjslint, see:
# #
# <http://code.google.com/closure/utilities/docs/linter_howto.html> # <http://code.google.com/closure/utilities/docs/linter_howto.html>
SRC_DIRS := . utils worker web SRC_DIRS := . utils worker web test
GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js)) GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js))
lint: lint:
gjslint $(GJSLINT_FILES) gjslint $(GJSLINT_FILES)

392
fonts.js
View File

@ -4,11 +4,6 @@
'use strict'; 'use strict';
var isWorker = (typeof window == 'undefined'); var isWorker = (typeof window == 'undefined');
/**
* Maximum file size of the font.
*/
var kMaxFontFileSize = 200000;
/** /**
* Maximum time to wait for a font to be loaded by font-face rules. * Maximum time to wait for a font to be loaded by font-face rules.
*/ */
@ -19,46 +14,46 @@ var kMaxWaitForFontFace = 1000;
* fonts and their acronyms. * fonts and their acronyms.
*/ */
var stdFontMap = { var stdFontMap = {
"Arial": "Helvetica", 'Arial': 'Helvetica',
"Arial_Bold": "Helvetica-Bold", 'Arial_Bold': 'Helvetica-Bold',
"Arial_BoldItalic": "Helvetica-BoldOblique", 'Arial_BoldItalic': 'Helvetica-BoldOblique',
"Arial_Italic": "Helvetica-Oblique", 'Arial_Italic': 'Helvetica-Oblique',
"Arial_BoldItalicMT": "Helvetica-BoldOblique", 'Arial_BoldItalicMT': 'Helvetica-BoldOblique',
"Arial_BoldMT": "Helvetica-Bold", 'Arial_BoldMT': 'Helvetica-Bold',
"Arial_ItalicMT": "Helvetica-Oblique", 'Arial_ItalicMT': 'Helvetica-Oblique',
"ArialMT": "Helvetica", 'ArialMT': 'Helvetica',
"Courier_Bold": "Courier-Bold", 'Courier_Bold': 'Courier-Bold',
"Courier_BoldItalic": "Courier-BoldOblique", 'Courier_BoldItalic': 'Courier-BoldOblique',
"Courier_Italic": "Courier-Oblique", 'Courier_Italic': 'Courier-Oblique',
"CourierNew": "Courier", 'CourierNew': 'Courier',
"CourierNew_Bold": "Courier-Bold", 'CourierNew_Bold': 'Courier-Bold',
"CourierNew_BoldItalic": "Courier-BoldOblique", 'CourierNew_BoldItalic': 'Courier-BoldOblique',
"CourierNew_Italic": "Courier-Oblique", 'CourierNew_Italic': 'Courier-Oblique',
"CourierNewPS_BoldItalicMT": "Courier-BoldOblique", 'CourierNewPS_BoldItalicMT': 'Courier-BoldOblique',
"CourierNewPS_BoldMT": "Courier-Bold", 'CourierNewPS_BoldMT': 'Courier-Bold',
"CourierNewPS_ItalicMT": "Courier-Oblique", 'CourierNewPS_ItalicMT': 'Courier-Oblique',
"CourierNewPSMT": "Courier", 'CourierNewPSMT': 'Courier',
"Helvetica_Bold": "Helvetica-Bold", 'Helvetica_Bold': 'Helvetica-Bold',
"Helvetica_BoldItalic": "Helvetica-BoldOblique", 'Helvetica_BoldItalic': 'Helvetica-BoldOblique',
"Helvetica_Italic": "Helvetica-Oblique", 'Helvetica_Italic': 'Helvetica-Oblique',
"Symbol_Bold": "Symbol", 'Symbol_Bold': 'Symbol',
"Symbol_BoldItalic": "Symbol", 'Symbol_BoldItalic': 'Symbol',
"Symbol_Italic": "Symbol", 'Symbol_Italic': 'Symbol',
"TimesNewRoman": "Times-Roman", 'TimesNewRoman': 'Times-Roman',
"TimesNewRoman_Bold": "Times-Bold", 'TimesNewRoman_Bold': 'Times-Bold',
"TimesNewRoman_BoldItalic": "Times-BoldItalic", 'TimesNewRoman_BoldItalic': 'Times-BoldItalic',
"TimesNewRoman_Italic": "Times-Italic", 'TimesNewRoman_Italic': 'Times-Italic',
"TimesNewRomanPS": "Times-Roman", 'TimesNewRomanPS': 'Times-Roman',
"TimesNewRomanPS_Bold": "Times-Bold", 'TimesNewRomanPS_Bold': 'Times-Bold',
"TimesNewRomanPS_BoldItalic": "Times-BoldItalic", 'TimesNewRomanPS_BoldItalic': 'Times-BoldItalic',
"TimesNewRomanPS_BoldItalicMT": "Times-BoldItalic", 'TimesNewRomanPS_BoldItalicMT': 'Times-BoldItalic',
"TimesNewRomanPS_BoldMT": "Times-Bold", 'TimesNewRomanPS_BoldMT': 'Times-Bold',
"TimesNewRomanPS_Italic": "Times-Italic", 'TimesNewRomanPS_Italic': 'Times-Italic',
"TimesNewRomanPS_ItalicMT": "Times-Italic", 'TimesNewRomanPS_ItalicMT': 'Times-Italic',
"TimesNewRomanPSMT": "Times-Roman", 'TimesNewRomanPSMT': 'Times-Roman',
"TimesNewRomanPSMT_Bold": "Times-Bold", 'TimesNewRomanPSMT_Bold': 'Times-Bold',
"TimesNewRomanPSMT_BoldItalic": "Times-BoldItalic", 'TimesNewRomanPSMT_BoldItalic': 'Times-BoldItalic',
"TimesNewRomanPSMT_Italic": "Times-Italic" 'TimesNewRomanPSMT_Italic': 'Times-Italic'
}; };
var FontMeasure = (function FontMeasure() { var FontMeasure = (function FontMeasure() {
@ -76,14 +71,14 @@ var FontMeasure = (function FontMeasure() {
if (!(measureCache = sizes[size])) if (!(measureCache = sizes[size]))
measureCache = sizes[size] = Object.create(null); measureCache = sizes[size] = Object.create(null);
} else { } else {
measureCache = null measureCache = null;
} }
var name = font.loadedName; var name = font.loadedName;
var bold = font.bold ? "bold" : "normal"; var bold = font.bold ? 'bold' : 'normal';
var italic = font.italic ? "italic" : "normal"; var italic = font.italic ? 'italic' : 'normal';
size *= kScalePrecision; size *= kScalePrecision;
var rule = bold + " " + italic + " " + size + 'px "' + name + '"'; var rule = italic + ' ' + bold + ' ' + size + 'px "' + name + '"';
ctx.font = rule; ctx.font = rule;
}, },
measureText: function fonts_measureText(text) { measureText: function fonts_measureText(text) {
@ -395,17 +390,23 @@ var Font = (function Font() {
// If the font is to be ignored, register it like an already loaded font // If the font is to be ignored, register it like an already loaded font
// to avoid the cost of waiting for it be be loaded by the platform. // to avoid the cost of waiting for it be be loaded by the platform.
if (properties.ignore) { if (properties.ignore) {
this.loadedName = 'Arial'; this.loadedName = 'sans-serif';
this.loading = false; this.loading = false;
return; return;
} }
if (!file) { if (!file) {
var fontName = stdFontMap[name]; // The file data is not specified. Trying to fix the font name
this.bold = (fontName.indexOf("Bold") != -1); // to be used with the canvas.font.
this.italic = (fontName.indexOf("Oblique") != -1); var fontName = stdFontMap[name] || name.replace('_', '-');
this.loadedName = fontName.split("-")[0]; this.bold = (fontName.indexOf('Bold') != -1);
this.italic = (fontName.indexOf('Oblique') != -1) ||
(fontName.indexOf('Italic') != -1);
this.loadedName = fontName.split('-')[0];
this.loading = false; this.loading = false;
this.charsToUnicode = function(s) {
return s;
};
return; return;
} }
@ -461,6 +462,14 @@ var Font = (function Font() {
return array; return array;
}; };
function arrayToString(arr) {
var str = '';
for (var i = 0; i < arr.length; ++i)
str += String.fromCharCode(arr[i]);
return str;
};
function int16(bytes) { function int16(bytes) {
return (bytes[0] << 8) + (bytes[1] & 0xff); return (bytes[0] << 8) + (bytes[1] & 0xff);
}; };
@ -496,7 +505,7 @@ var Font = (function Font() {
String.fromCharCode(value & 0xff); String.fromCharCode(value & 0xff);
}; };
function createOpenTypeHeader(sfnt, file, offsets, numTables) { function createOpenTypeHeader(sfnt, file, numTables) {
// sfnt version (4 bytes) // sfnt version (4 bytes)
var header = sfnt; var header = sfnt;
@ -514,14 +523,13 @@ var Font = (function Font() {
// rangeShift (2 bytes) // rangeShift (2 bytes)
header += string16(numTables * 16 - searchRange); header += string16(numTables * 16 - searchRange);
file.set(stringToArray(header), offsets.currentOffset); file.file += header;
offsets.currentOffset += header.length; file.virtualOffset += header.length;
offsets.virtualOffset += header.length;
}; };
function createTableEntry(file, offsets, tag, data) { function createTableEntry(file, tag, data) {
// offset // offset
var offset = offsets.virtualOffset; var offset = file.virtualOffset;
// length // length
var length = data.length; var length = data.length;
@ -530,21 +538,19 @@ var Font = (function Font() {
while (data.length & 3) while (data.length & 3)
data.push(0x00); data.push(0x00);
while (offsets.virtualOffset & 3) while (file.virtualOffset & 3)
offsets.virtualOffset++; file.virtualOffset++;
// checksum // checksum
var checksum = 0, n = data.length; var checksum = 0, n = data.length;
for (var i = 0; i < n; i+=4) for (var i = 0; i < n; i += 4)
checksum = (checksum + int32([data[i], data[i+1], data[i+2], data[i+3]])) | 0; checksum = (checksum + int32([data[i], data[i + 1], data[i + 2],
data[i + 3]])) | 0;
var tableEntry = (tag + string32(checksum) + var tableEntry = (tag + string32(checksum) +
string32(offset) + string32(length)); string32(offset) + string32(length));
tableEntry = stringToArray(tableEntry); file.file += tableEntry;
file.set(tableEntry, offsets.currentOffset); file.virtualOffset += data.length;
offsets.currentOffset += tableEntry.length;
offsets.virtualOffset += data.length;
}; };
function getRanges(glyphs) { function getRanges(glyphs) {
@ -598,7 +604,7 @@ var Font = (function Font() {
var range = ranges[i]; var range = ranges[i];
var start = range[0]; var start = range[0];
var end = range[1]; var end = range[1];
var offset = (segCount - i) * 2 + bias * 2; var offset = (segCount - i) * 2 + bias * 2;
bias += (end - start + 1); bias += (end - start + 1);
startCount += string16(start); startCount += string16(start);
@ -783,8 +789,10 @@ var Font = (function Font() {
encoding: null, encoding: null,
checkAndRepair: function font_checkAndRepair(name, font, properties) { checkAndRepair: function font_checkAndRepair(name, font, properties) {
// offset glyphs to the Unicode Private Use Area
var kCmapGlyphOffset = 0xE000;
function readTableEntry(file) { function readTableEntry(file) {
// tag
var tag = file.getBytes(4); var tag = file.getBytes(4);
tag = String.fromCharCode(tag[0]) + tag = String.fromCharCode(tag[0]) +
String.fromCharCode(tag[1]) + String.fromCharCode(tag[1]) +
@ -803,7 +811,8 @@ var Font = (function Font() {
file.pos = previousPosition; file.pos = previousPosition;
if (tag == 'head') if (tag == 'head')
data[8] = data[9] = data[10] = data[11] = 0; // clearing checksum adjustment // clearing checksum adjustment
data[8] = data[9] = data[10] = data[11] = 0;
return { return {
tag: tag, tag: tag,
@ -838,7 +847,7 @@ var Font = (function Font() {
encodingID: int16(font.getBytes(2)), encodingID: int16(font.getBytes(2)),
offset: int32(font.getBytes(4)) offset: int32(font.getBytes(4))
}); });
}; }
var encoding = properties.encoding; var encoding = properties.encoding;
var charset = properties.charset; var charset = properties.charset;
@ -861,7 +870,7 @@ var Font = (function Font() {
var index = font.getByte(); var index = font.getByte();
if (index) { if (index) {
deltas.push(index); deltas.push(index);
glyphs.push({ unicode : j }); glyphs.push({ unicode: j });
} }
} }
@ -965,23 +974,19 @@ var Font = (function Font() {
tables.push(table); tables.push(table);
} }
// Create a new file to hold the new version of our truetype with a new
// header and new offsets
var ttf = new Uint8Array(kMaxFontFileSize);
// The offsets object holds at the same time a representation of where
// to write the table entry information about a table and another offset
// representing the offset where to put the actual data of a particular
// table
var numTables = header.numTables + requiredTables.length; var numTables = header.numTables + requiredTables.length;
var offsets = {
currentOffset: 0, // header and new offsets. Table entry information is appended to the
// end of file. The virtualOffset represents where to put the actual
// data of a particular table;
var ttf = {
file: '',
virtualOffset: numTables * (4 * 4) virtualOffset: numTables * (4 * 4)
}; };
// The new numbers of tables will be the last one plus the num // The new numbers of tables will be the last one plus the num
// of missing tables // of missing tables
createOpenTypeHeader('\x00\x01\x00\x00', ttf, offsets, numTables); createOpenTypeHeader('\x00\x01\x00\x00', ttf, numTables);
if (requiredTables.indexOf('OS/2') != -1) { if (requiredTables.indexOf('OS/2') != -1) {
tables.push({ tables.push({
@ -1001,42 +1006,50 @@ var Font = (function Font() {
var numOfHMetrics = int16(font.getBytes(2)); var numOfHMetrics = int16(font.getBytes(2));
var numOfSidebearings = numGlyphs - numOfHMetrics; var numOfSidebearings = numGlyphs - numOfHMetrics;
var numMissing = numOfSidebearings - (hmtx.length - numOfHMetrics * 4); var numMissing = numOfSidebearings -
((hmtx.length - numOfHMetrics * 4) >> 1);
if (numMissing > 0) { if (numMissing > 0) {
font.pos = (font.start ? font.start : 0) + hmtx.offset; font.pos = (font.start ? font.start : 0) + hmtx.offset;
var metrics = ""; var metrics = '';
for (var i = 0; i < hmtx.length; i++) for (var i = 0; i < hmtx.length; i++)
metrics += String.fromCharCode(font.getByte()); metrics += String.fromCharCode(font.getByte());
for (var i = 0; i < numMissing; i++) for (var i = 0; i < numMissing; i++)
metrics += "\x00\x00"; metrics += '\x00\x00';
hmtx.data = stringToArray(metrics); hmtx.data = stringToArray(metrics);
} }
// Sanitizer reduces the glyph advanceWidth to the maxAdvanceWidth
// Sometimes it's 0. That needs to be fixed
if (hhea.data[10] == 0 && hhea.data[11] == 0) {
hhea.data[10] = 0xFF;
hhea.data[11] = 0xFF;
}
// Replace the old CMAP table with a shiny new one // Replace the old CMAP table with a shiny new one
if (properties.type == 'CIDFontType2') { if (properties.type == 'CIDFontType2') {
// Type2 composite fonts map characters directly to glyphs so the cmap // Type2 composite fonts map characters directly to glyphs so the cmap
// table must be replaced. // table must be replaced.
// canvas fillText will reencode some characters even if the font has a
// glyph at that position - e.g. newline is converted to a space and
// U+00AD (soft hyphen) is not drawn.
// So, offset all the glyphs by 0xFF to avoid these cases and use
// the encoding to map incoming characters to the new glyph positions
var glyphs = []; var glyphs = [];
var charset = properties.charset; var encoding = properties.encoding;
if (!charset.length) {
// Type2 composite fonts map characters directly to glyphs so the cmap
for (var i = 1; i < numGlyphs; i++) {
glyphs.push({
unicode: i
});
}
} else {
for (var i = 1; i < charset.length; i++) {
var index = charset.indexOf(i);
if (index == -1)
break;
glyphs.push({ for (var i = 1; i < numGlyphs; i++) {
unicode: index glyphs.push({ unicode: i + kCmapGlyphOffset });
}); }
}
if ('undefined' == typeof(encoding[0])) {
// the font is directly characters to glyphs with no encoding
// so create an identity encoding
for (i = 0; i < numGlyphs; i++)
encoding[i] = i + kCmapGlyphOffset;
} else {
for (var i in encoding)
encoding[i] = encoding[i] + kCmapGlyphOffset;
} }
if (!cmap) { if (!cmap) {
@ -1080,26 +1093,21 @@ var Font = (function Font() {
var tableData = table.data; var tableData = table.data;
for (var j = 0; j < tableData.length; j++) for (var j = 0; j < tableData.length; j++)
data.push(tableData[j]); data.push(tableData[j]);
createTableEntry(ttf, offsets, table.tag, data); createTableEntry(ttf, table.tag, data);
} }
// Add the table datas // Add the table datas
for (var i = 0; i < tables.length; i++) { for (var i = 0; i < tables.length; i++) {
var table = tables[i]; var table = tables[i];
var tableData = table.data; var tableData = table.data;
ttf.set(tableData, offsets.currentOffset); ttf.file += arrayToString(tableData);
offsets.currentOffset += tableData.length;
// 4-byte aligned data // 4-byte aligned data
while (offsets.currentOffset & 3) while (ttf.file.length & 3)
offsets.currentOffset++; ttf.file += String.fromCharCode(0);
} }
var fontData = []; return stringToArray(ttf.file);
for (var i = 0; i < offsets.currentOffset; i++)
fontData.push(ttf[i]);
return fontData;
}, },
convert: function font_convert(fontName, font, properties) { convert: function font_convert(fontName, font, properties) {
@ -1116,13 +1124,13 @@ var Font = (function Font() {
// representing the offset where to draw the actual data of a particular // representing the offset where to draw the actual data of a particular
// table // table
var kRequiredTablesCount = 9; var kRequiredTablesCount = 9;
var offsets = {
currentOffset: 0, var otf = {
file: '',
virtualOffset: 9 * (4 * 4) virtualOffset: 9 * (4 * 4)
}; };
var otf = new Uint8Array(kMaxFontFileSize); createOpenTypeHeader('\x4F\x54\x54\x4F', otf, 9);
createOpenTypeHeader('\x4F\x54\x54\x4F', otf, offsets, 9);
var charstrings = font.charstrings; var charstrings = font.charstrings;
properties.fixedPitch = isFixedPitch(charstrings); properties.fixedPitch = isFixedPitch(charstrings);
@ -1206,18 +1214,14 @@ var Font = (function Font() {
}; };
for (var field in fields) for (var field in fields)
createTableEntry(otf, offsets, field, fields[field]); createTableEntry(otf, field, fields[field]);
for (var field in fields) { for (var field in fields) {
var table = fields[field]; var table = fields[field];
otf.set(table, offsets.currentOffset); otf.file += arrayToString(table);
offsets.currentOffset += table.length;
} }
var fontData = []; return stringToArray(otf.file);
for (var i = 0; i < offsets.currentOffset; i++)
fontData.push(otf[i]);
return fontData;
}, },
bindWorker: function font_bindWorker(data) { bindWorker: function font_bindWorker(data) {
@ -1259,39 +1263,33 @@ var Font = (function Font() {
if (!charsCache) if (!charsCache)
charsCache = this.charsCache = Object.create(null); charsCache = this.charsCache = Object.create(null);
// translate the string using the font's encoding
var encoding = this.encoding;
if (!encoding)
return chars;
str = '';
if (this.compositeFont) { if (this.compositeFont) {
// composite fonts have multi-byte strings // composite fonts have multi-byte strings convert the string from
// convert the string from single-byte to multi-byte // single-byte to multi-byte
// XXX assuming CIDFonts are two-byte - later need to extract the correct byte encoding // XXX assuming CIDFonts are two-byte - later need to extract the
// according to the PDF spec // correct byte encoding according to the PDF spec
str = ''; var length = chars.length - 1; // looping over two bytes at a time so
var multiByteStr = ""; // loop should never end on the last byte
var length = chars.length;
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
var byte1 = chars.charCodeAt(i++) & 0xFF; var charcode = int16([chars.charCodeAt(i++), chars.charCodeAt(i)]);
var byte2; var unicode = encoding[charcode];
if (i == length) str += String.fromCharCode(unicode);
byte2 = 0;
else
byte2 = chars.charCodeAt(i) & 0xFF;
multiByteStr += String.fromCharCode((byte1 << 8) | byte2);
} }
str = multiByteStr;
} }
else { else {
// translate the string using the font's encoding
var encoding = this.encoding;
if (!encoding)
return chars;
str = '';
for (var i = 0; i < chars.length; ++i) { for (var i = 0; i < chars.length; ++i) {
var charcode = chars.charCodeAt(i); var charcode = chars.charCodeAt(i);
var unicode = encoding[charcode]; var unicode = encoding[charcode];
if ('undefined' == typeof(unicode)) { if ('undefined' == typeof(unicode)) {
// FIXME/issue 233: we're hitting this in test/pdf/sizes.pdf // FIXME/issue 233: we're hitting this in test/pdf/sizes.pdf
// at the moment, for unknown reasons. // at the moment, for unknown reasons.
warn('Unencoded charcode '+ charcode); warn('Unencoded charcode ' + charcode);
unicode = charcode; unicode = charcode;
} }
@ -1455,7 +1453,7 @@ var Type1Parser = function() {
var value = ''; var value = '';
var count = array.length; var count = array.length;
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
value = parseInt(array[i]); value = array[i];
if (value < 32) { if (value < 32) {
var command = null; var command = null;
@ -1466,7 +1464,8 @@ var Type1Parser = function() {
if (escape == 16) { if (escape == 16) {
var index = charstring.pop(); var index = charstring.pop();
var argc = charstring.pop(); var argc = charstring.pop();
var data = charstring.pop(); for (var j = 0; j < argc; j++)
charstring.push('drop');
// If the flex mechanishm is not used in a font program, Adobe // 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 // state that that entries 0, 1 and 2 can simply be replace by
@ -1518,11 +1517,11 @@ var Type1Parser = function() {
value = command; value = command;
} else if (value <= 246) { } else if (value <= 246) {
value = parseInt(value) - 139; value = value - 139;
} else if (value <= 250) { } else if (value <= 250) {
value = ((value - 247) * 256) + parseInt(array[++i]) + 108; value = ((value - 247) * 256) + array[++i] + 108;
} else if (value <= 254) { } else if (value <= 254) {
value = -((value - 251) * 256) - parseInt(array[++i]) - 108; value = -((value - 251) * 256) - array[++i] - 108;
} else { } else {
value = (array[++i] & 0xff) << 24 | (array[++i] & 0xff) << 16 | value = (array[++i] & 0xff) << 24 | (array[++i] & 0xff) << 16 |
(array[++i] & 0xff) << 8 | (array[++i] & 0xff) << 0; (array[++i] & 0xff) << 8 | (array[++i] & 0xff) << 0;
@ -1551,8 +1550,8 @@ var Type1Parser = function() {
}; };
function readNumber(str, index) { function readNumber(str, index) {
while (str[index++] == ' ') while (str[index++] == ' ');
;
var start = index; var start = index;
var count = 0; var count = 0;
@ -1584,6 +1583,17 @@ var Type1Parser = function() {
var c = ''; var c = '';
var count = eexecStr.length; var count = eexecStr.length;
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
var getToken = function() {
while (i < count && (eexecStr[i] == ' ' || eexecStr[i] == '\n'))
++i;
var t = '';
while (i < count && !(eexecStr[i] == ' ' || eexecStr[i] == '\n'))
t += eexecStr[i++];
return t;
}
var c = eexecStr[i]; var c = eexecStr[i];
if ((glyphsSection || subrsSection) && c == 'R') { if ((glyphsSection || subrsSection) && c == 'R') {
@ -1613,7 +1623,25 @@ var Type1Parser = function() {
glyphsSection = true; glyphsSection = true;
break; break;
case '/Subrs': case '/Subrs':
subrsSection = true; ++i;
var num = parseInt(getToken());
getToken(); // read in 'array'
for (var j = 0; j < num; ++j) {
var t = getToken(); // read in 'dup'
if (t == 'ND')
break;
var index = parseInt(getToken());
if (index > j)
j = index;
var length = parseInt(getToken());
getToken(); // read in 'RD'
var data = eexec.slice(i + 1, i + 1 + length);
var encoded = decrypt(data, kCharStringsEncryptionKey, 4);
var str = decodeCharString(encoded);
i = i + 1 + length;
getToken(); //read in 'NP'
program.subrs[index] = str.charstring;
}
break; break;
case '/BlueValues': case '/BlueValues':
case '/OtherBlues': case '/OtherBlues':
@ -1621,18 +1649,21 @@ var Type1Parser = function() {
case '/FamilyOtherBlues': case '/FamilyOtherBlues':
case '/StemSnapH': case '/StemSnapH':
case '/StemSnapV': case '/StemSnapV':
program.properties.private[token.substring(1)] = readNumberArray(eexecStr, i + 2); program.properties.private[token.substring(1)] =
readNumberArray(eexecStr, i + 2);
break; break;
case '/StdHW': case '/StdHW':
case '/StdVW': case '/StdVW':
program.properties.private[token.substring(1)] = readNumberArray(eexecStr, i + 2)[0]; program.properties.private[token.substring(1)] =
readNumberArray(eexecStr, i + 2)[0];
break; break;
case '/BlueShift': case '/BlueShift':
case '/BlueFuzz': case '/BlueFuzz':
case '/BlueScale': case '/BlueScale':
case '/LanguageGroup': case '/LanguageGroup':
case '/ExpansionFactor': case '/ExpansionFactor':
program.properties.private[token.substring(1)] = readNumber(eexecStr, i + 1); program.properties.private[token.substring(1)] =
readNumber(eexecStr, i + 1);
break; break;
} }
} else if (c == '/') { } else if (c == '/') {
@ -1803,8 +1834,10 @@ CFF.prototype = {
// Add another offset after this one because we need a new offset // Add another offset after this one because we need a new offset
var relativeOffset = 1; var relativeOffset = 1;
for (var i = 0; i < count + 1; i++) { for (var i = 0; i < count + 1; i++) {
data += String.fromCharCode((relativeOffset >>> 24) & 0xFF, (relativeOffset >> 16) & 0xFF, data += String.fromCharCode((relativeOffset >>> 24) & 0xFF,
(relativeOffset >> 8) & 0xFF, relativeOffset & 0xFF); (relativeOffset >> 16) & 0xFF,
(relativeOffset >> 8) & 0xFF,
relativeOffset & 0xFF);
if (objects[i]) if (objects[i])
relativeOffset += objects[i].length; relativeOffset += objects[i].length;
@ -1812,7 +1845,8 @@ CFF.prototype = {
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
for (var j = 0; j < objects[i].length; j++) for (var j = 0; j < objects[i].length; j++)
data += isByte ? String.fromCharCode(objects[i][j] & 0xFF) : objects[i][j]; data += isByte ? String.fromCharCode(objects[i][j] & 0xFF) :
objects[i][j];
} }
return data; return data;
}, },
@ -1889,8 +1923,13 @@ CFF.prototype = {
for (var i = 0; i < bias; i++) for (var i = 0; i < bias; i++)
type2Subrs.push([0x0B]); type2Subrs.push([0x0B]);
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++) {
type2Subrs.push(this.flattenCharstring(type1Subrs[i], this.commandsMap)); var subr = type1Subrs[i];
if (!subr)
subr = [0x0B];
type2Subrs.push(this.flattenCharstring(subr, this.commandsMap));
}
return type2Subrs; return type2Subrs;
}, },
@ -1912,6 +1951,7 @@ CFF.prototype = {
'sub': [12, 11], 'sub': [12, 11],
'div': [12, 12], 'div': [12, 12],
'pop': [1, 12, 18], 'pop': [1, 12, 18],
'drop' : [12, 18],
'endchar': 14, 'endchar': 14,
'rmoveto': 21, 'rmoveto': 21,
'hmoveto': 22, 'hmoveto': 22,
@ -2036,7 +2076,7 @@ CFF.prototype = {
BlueFuzz: '\x0c\x0b', BlueFuzz: '\x0c\x0b',
BlueScale: '\x0c\x09', BlueScale: '\x0c\x09',
LanguageGroup: '\x0c\x11', LanguageGroup: '\x0c\x11',
ExpansionFactor: '\x0c\x18' ExpansionFactor: '\x0c\x18'
}; };
for (var field in fieldMap) { for (var field in fieldMap) {
if (!properties.private.hasOwnProperty(field)) continue; if (!properties.private.hasOwnProperty(field)) continue;
@ -2082,7 +2122,7 @@ var Type2CFF = (function() {
this.properties = properties; this.properties = properties;
// Other classes expect this.data to be a Javascript array // Other classes expect this.data to be a Javascript array
var data = [] var data = [];
for (var i = 0, ii = bytes.length; i < ii; ++i) for (var i = 0, ii = bytes.length; i < ii; ++i)
data.push(bytes[i]); data.push(bytes[i]);
this.data = data; this.data = data;
@ -2114,7 +2154,7 @@ var Type2CFF = (function() {
var privOffset = privInfo[1], privLength = privInfo[0]; var privOffset = privInfo[1], privLength = privInfo[0];
var privBytes = bytes.subarray(privOffset, privOffset + privLength); var privBytes = bytes.subarray(privOffset, privOffset + privLength);
baseDict = this.parseDict(privBytes); baseDict = this.parseDict(privBytes);
var privDict = this.getPrivDict(baseDict, strings); var privDict = this.getPrivDict(baseDict, strings);
TODO('Parse encoding'); TODO('Parse encoding');
var charStrings = this.parseIndex(topDict['CharStrings']); var charStrings = this.parseIndex(topDict['CharStrings']);
@ -2218,7 +2258,7 @@ var Type2CFF = (function() {
var pair = baseDict[i]; var pair = baseDict[i];
var key = pair[0]; var key = pair[0];
var value = pair[1]; var value = pair[1];
switch(key) { switch (key) {
case 20: case 20:
dict['defaultWidthX'] = value[0]; dict['defaultWidthX'] = value[0];
case 21: case 21:
@ -2240,7 +2280,7 @@ var Type2CFF = (function() {
var pair = baseDict[i]; var pair = baseDict[i];
var key = pair[0]; var key = pair[0];
var value = pair[1]; var value = pair[1];
switch(key) { switch (key) {
case 1: case 1:
dict['Notice'] = strings[value[0]]; dict['Notice'] = strings[value[0]];
break; break;
@ -2276,7 +2316,7 @@ var Type2CFF = (function() {
}, },
getStrings: function cff_getstrings(stringIndex) { getStrings: function cff_getstrings(stringIndex) {
function bytesToString(bytesArr) { function bytesToString(bytesArr) {
var s = ""; var s = '';
for (var i = 0, ii = bytesArr.length; i < ii; ++i) for (var i = 0, ii = bytesArr.length; i < ii; ++i)
s += String.fromCharCode(bytesArr[i]); s += String.fromCharCode(bytesArr[i]);
return s; return s;
@ -2295,11 +2335,11 @@ var Type2CFF = (function() {
var bytes = this.bytes; var bytes = this.bytes;
var offset = 0; var offset = 0;
while(bytes[offset] != 1) while (bytes[offset] != 1)
++offset; ++offset;
if (offset != 0) { if (offset != 0) {
warning("cff data is shifted"); warning('cff data is shifted');
bytes = bytes.subarray(offset); bytes = bytes.subarray(offset);
this.bytes = bytes; this.bytes = bytes;
} }
@ -2307,7 +2347,7 @@ var Type2CFF = (function() {
return { return {
endPos: bytes[2], endPos: bytes[2],
offsetSize: bytes[3] offsetSize: bytes[3]
} };
}, },
parseDict: function cff_parseDict(dict) { parseDict: function cff_parseDict(dict) {
var pos = 0; var pos = 0;
@ -2338,7 +2378,7 @@ var Type2CFF = (function() {
}; };
function parseFloatOperand() { function parseFloatOperand() {
var str = ""; var str = '';
var eof = 15; var eof = 15;
var lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8', var lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', '.', 'E', 'E-', null, '-']; '9', '.', 'E', 'E-', null, '-'];
@ -2414,8 +2454,8 @@ var Type2CFF = (function() {
}, },
length: count, length: count,
endPos: end endPos: end
} };
}, }
}; };
return constructor; return constructor;

977
pdf.js

File diff suppressed because it is too large Load Diff

View File

@ -5,138 +5,151 @@
* A Test Driver for PDF.js * A Test Driver for PDF.js
*/ */
'use strict';
var appPath, browser, canvas, currentTaskIdx, manifest, stdout; var appPath, browser, canvas, currentTaskIdx, manifest, stdout;
function queryParams() { function queryParams() {
var qs = window.location.search.substring(1); var qs = window.location.search.substring(1);
var kvs = qs.split("&"); var kvs = qs.split('&');
var params = { }; var params = { };
for (var i = 0; i < kvs.length; ++i) { for (var i = 0; i < kvs.length; ++i) {
var kv = kvs[i].split("="); var kv = kvs[i].split('=');
params[unescape(kv[0])] = unescape(kv[1]); params[unescape(kv[0])] = unescape(kv[1]);
} }
return params; return params;
} }
function load() { function load() {
var params = queryParams(); var params = queryParams();
browser = params.browser; browser = params.browser;
manifestFile = params.manifestFile; var manifestFile = params.manifestFile;
appPath = params.path; 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("load...\n"); log('load...\n');
log("Harness thinks this browser is '"+ browser + "' with path " + appPath + "\n"); log('Harness thinks this browser is "' + browser + '" with path "' +
log("Fetching manifest "+ manifestFile +"..."); appPath + '"\n');
log('Fetching manifest "' + manifestFile + '"... ');
var r = new XMLHttpRequest(); var r = new XMLHttpRequest();
r.open("GET", manifestFile, false); r.open('GET', manifestFile, false);
r.onreadystatechange = function(e) { r.onreadystatechange = function(e) {
if (r.readyState == 4) { if (r.readyState == 4) {
log("done\n"); log('done\n');
manifest = JSON.parse(r.responseText); manifest = JSON.parse(r.responseText);
currentTaskIdx = 0, nextTask(); currentTaskIdx = 0, nextTask();
} }
}; };
r.send(null); r.send(null);
} }
window.onload = load; window.onload = load;
function nextTask() { function nextTask() {
if (currentTaskIdx == manifest.length) { if (currentTaskIdx == manifest.length) {
return done(); return done();
}
var task = manifest[currentTaskIdx];
task.round = 0;
log('Loading file "' + task.file + '"\n');
var r = new XMLHttpRequest();
r.open('GET', task.file);
r.mozResponseType = r.responseType = 'arraybuffer';
r.onreadystatechange = function() {
var failure;
if (r.readyState == 4) {
var data = r.mozResponseArrayBuffer || r.mozResponse ||
r.responseArrayBuffer || r.response;
try {
task.pdfDoc = new PDFDoc(new Stream(data));
} catch (e) {
failure = 'load PDF doc : ' + e.toString();
}
task.pageNum = 1, nextPage(task, failure);
} }
var task = manifest[currentTaskIdx]; };
task.round = 0; r.send(null);
log("Loading file "+ task.file +"\n");
var r = new XMLHttpRequest();
r.open("GET", task.file);
r.mozResponseType = r.responseType = "arraybuffer";
r.onreadystatechange = function() {
var failure;
if (r.readyState == 4) {
var data = r.mozResponseArrayBuffer || r.mozResponse ||
r.responseArrayBuffer || r.response;
try {
task.pdfDoc = new PDFDoc(new Stream(data));
} catch(e) {
failure = 'load PDF doc: '+ e.toString();
}
task.pageNum = 1, nextPage(task, failure);
}
};
r.send(null);
} }
function isLastPage(task) { function isLastPage(task) {
return (task.pdfDoc && (task.pageNum > task.pdfDoc.numPages)); return (task.pageNum > task.pdfDoc.numPages);
} }
function nextPage(task, loadError) { function nextPage(task, loadError) {
if (isLastPage(task)) { var failure = loadError || '';
if (++task.round < task.rounds) {
log(" Round "+ (1 + task.round) +"\n"); if (!task.pdfDoc) {
task.pageNum = 1; sendTaskResult(canvas.toDataURL('image/png'), task, failure);
} else { log('done' + (failure ? ' (failed !: ' + failure + ')' : '') + '\n');
++currentTaskIdx, nextTask(); ++currentTaskIdx, nextTask();
return; return;
}
if (isLastPage(task)) {
if (++task.round < task.rounds) {
log(' Round ' + (1 + task.round) + '\n');
task.pageNum = 1;
} else {
++currentTaskIdx, nextTask();
return;
}
}
var page = null;
if (!failure) {
try {
log(' loading page ' + task.pageNum + '/' + task.pdfDoc.numPages +
'... ');
var ctx = canvas.getContext('2d');
page = task.pdfDoc.getPage(task.pageNum);
var pdfToCssUnitsCoef = 96.0 / 72.0;
// using mediaBox for the canvas size
var pageWidth = page.width;
var pageHeight = page.height;
canvas.width = pageWidth * pdfToCssUnitsCoef;
canvas.height = pageHeight * pdfToCssUnitsCoef;
clear(ctx);
page.startRendering(
ctx,
function(e) {
snapshotCurrentPage(page, task, (!failure && e) ?
('render : ' + e) : failure);
} }
);
} catch (e) {
failure = 'page setup : ' + e.toString();
} }
}
var failure = loadError || ''; if (failure) {
// Skip right to snapshotting if there was a failure, since the
var ctx = null; // fonts might be in an inconsistent state.
var page = null; snapshotCurrentPage(page, task, failure);
if (!failure) { }
try {
log(" loading page "+ task.pageNum +"... ");
ctx = canvas.getContext("2d");
page = task.pdfDoc.getPage(task.pageNum);
var pdfToCssUnitsCoef = 96.0 / 72.0;
// using mediaBox for the canvas size
var pageWidth = (page.mediaBox[2] - page.mediaBox[0]);
var pageHeight = (page.mediaBox[3] - page.mediaBox[1]);
canvas.width = pageWidth * pdfToCssUnitsCoef;
canvas.height = pageHeight * pdfToCssUnitsCoef;
clear(ctx);
page.startRendering(
ctx,
function(e) {
snapshotCurrentPage(page, task,
(!failure && e) ? ('render: '+ e) : failure);
});
} catch(e) {
failure = 'page setup: '+ e.toString();
}
}
if (failure) {
// Skip right to snapshotting if there was a failure, since the
// fonts might be in an inconsistent state.
snapshotCurrentPage(page, task, failure);
}
} }
function snapshotCurrentPage(page, task, failure) { function snapshotCurrentPage(page, task, failure) {
log("done, snapshotting... "); log('done, snapshotting... ');
sendTaskResult(canvas.toDataURL("image/png"), task, failure); sendTaskResult(canvas.toDataURL('image/png'), task, failure);
log("done"+ (failure ? " (failed!: "+ failure +")" : "") +"\n"); log('done' + (failure ? ' (failed !: ' + failure + ')' : '') + '\n');
// Set up the next request // Set up the next request
backoff = (inFlightRequests > 0) ? inFlightRequests * 10 : 0; var backoff = (inFlightRequests > 0) ? inFlightRequests * 10 : 0;
setTimeout(function() { setTimeout(
++task.pageNum, nextPage(task); function() {
++task.pageNum, nextPage(task);
}, },
backoff backoff
); );
@ -144,13 +157,14 @@ function snapshotCurrentPage(page, task, failure) {
function sendQuitRequest() { function sendQuitRequest() {
var r = new XMLHttpRequest(); var r = new XMLHttpRequest();
r.open("POST", "/tellMeToQuit?path=" + escape(appPath), false); r.open('POST', '/tellMeToQuit?path = ' + escape(appPath), false);
r.send(""); 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>' +
document.body.innerHTML;
if (window.SpecialPowers) { if (window.SpecialPowers) {
SpecialPowers.quitApplication(); SpecialPowers.quitApplication();
} else { } else {
@ -161,7 +175,7 @@ function quitApp() {
function done() { function done() {
if (inFlightRequests > 0) { if (inFlightRequests > 0) {
document.getElementById("inFlightCount").innerHTML = inFlightRequests; document.getElementById('inFlightCount').innerHTML = inFlightRequests;
setTimeout(done, 100); setTimeout(done, 100);
} else { } else {
setTimeout(quitApp, 100); setTimeout(quitApp, 100);
@ -172,7 +186,7 @@ var inFlightRequests = 0;
function sendTaskResult(snapshot, task, failure) { function sendTaskResult(snapshot, task, failure) {
var result = { browser: browser, var result = { browser: browser,
id: task.id, id: task.id,
numPages: task.pdfDoc.numPages, numPages: task.pdfDoc ? task.pdfDoc.numPages : 0,
failure: failure, failure: failure,
file: task.file, file: task.file,
round: task.round, round: task.round,
@ -181,20 +195,20 @@ function sendTaskResult(snapshot, task, failure) {
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", true); r.open('POST', '/submit_task_results', true);
r.setRequestHeader("Content-Type", "application/json"); r.setRequestHeader('Content-Type', 'application/json');
r.onreadystatechange = function(e) { r.onreadystatechange = function(e) {
if (r.readyState == 4) { if (r.readyState == 4) {
inFlightRequests--; inFlightRequests--;
} }
} }
document.getElementById("inFlightCount").innerHTML = inFlightRequests++; document.getElementById('inFlightCount').innerHTML = inFlightRequests++;
r.send(JSON.stringify(result)); r.send(JSON.stringify(result));
} }
function clear(ctx) { function clear(ctx) {
ctx.save(); ctx.save();
ctx.fillStyle = "rgb(255, 255, 255)"; ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore(); ctx.restore();
} }

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf

1
test/pdfs/hmm.pdf.link Normal file
View File

@ -0,0 +1 @@
http://speech.tifr.res.in/tutorials/hmmTutExamplesAlgo.pdf

View File

@ -1 +1 @@
http://www.intel.com/Assets/PDF/manual/253665.pdf http://www.intel.com/content/dam/doc/manual/64-ia-32-architectures-software-developer-vol-1-manual.pdf

1
test/pdfs/jai.pdf.link Normal file
View File

@ -0,0 +1 @@
http://download.oracle.com/docs/cd/E19957-01/806-5413-10/806-5413-10.pdf

BIN
test/pdfs/rotation.pdf Normal file

Binary file not shown.

View File

@ -0,0 +1,10 @@
[
{
"name":"firefox",
"path":"C:/Program Files (x86)/Firefox/firefox.exe"
},
{
"name":"aurora",
"path":"C:/Program Files (x86)/Aurora/firefox.exe"
}
]

View File

@ -1,38 +1,40 @@
user_pref("browser.console.showInPanel", true); 'use strict';
user_pref("browser.dom.window.dump.enabled", true);
user_pref("browser.firstrun.show.localepicker", false); user_pref('browser.console.showInPanel', true);
user_pref("browser.firstrun.show.uidiscovery", false); user_pref('browser.dom.window.dump.enabled', true);
user_pref("dom.allow_scripts_to_close_windows", true); user_pref('browser.firstrun.show.localepicker', false);
user_pref("dom.disable_open_during_load", false); user_pref('browser.firstrun.show.uidiscovery', false);
user_pref("dom.max_script_run_time", 0); // no slow script dialogs user_pref('dom.allow_scripts_to_close_windows', true);
user_pref("dom.max_chrome_script_run_time", 0); user_pref('dom.disable_open_during_load', false);
user_pref("dom.popup_maximum", -1); user_pref('dom.max_script_run_time', 0); // no slow script dialogs
user_pref("dom.send_after_paint_to_content", true); user_pref('dom.max_chrome_script_run_time', 0);
user_pref("dom.successive_dialog_time_limit", 0); user_pref('dom.popup_maximum', -1);
user_pref("security.warn_submit_insecure", false); user_pref('dom.send_after_paint_to_content', true);
user_pref("browser.shell.checkDefaultBrowser", false); user_pref('dom.successive_dialog_time_limit', 0);
user_pref("shell.checkDefaultClient", false); user_pref('security.warn_submit_insecure', false);
user_pref("browser.warnOnQuit", false); user_pref('browser.shell.checkDefaultBrowser', false);
user_pref("accessibility.typeaheadfind.autostart", false); user_pref('shell.checkDefaultClient', false);
user_pref("javascript.options.showInConsole", true); user_pref('browser.warnOnQuit', false);
user_pref("devtools.errorconsole.enabled", true); user_pref('accessibility.typeaheadfind.autostart', false);
user_pref("layout.debug.enable_data_xbl", true); user_pref('javascript.options.showInConsole', true);
user_pref("browser.EULA.override", true); user_pref('devtools.errorconsole.enabled', true);
user_pref("javascript.options.tracejit.content", true); user_pref('layout.debug.enable_data_xbl', true);
user_pref("javascript.options.methodjit.content", true); user_pref('browser.EULA.override', true);
user_pref("javascript.options.jitprofiling.content", true); user_pref('javascript.options.tracejit.content', true);
user_pref("javascript.options.methodjit_always", false); user_pref('javascript.options.methodjit.content', true);
user_pref("gfx.color_management.force_srgb", true); user_pref('javascript.options.jitprofiling.content', true);
user_pref("network.manage-offline-status", false); user_pref('javascript.options.methodjit_always', false);
user_pref("test.mousescroll", true); user_pref('gfx.color_management.force_srgb', true);
user_pref("network.http.prompt-temp-redirect", false); user_pref('network.manage-offline-status', false);
user_pref("media.cache_size", 100); user_pref('test.mousescroll', true);
user_pref("security.warn_viewing_mixed", false); user_pref('network.http.prompt-temp-redirect', false);
user_pref("app.update.enabled", false); user_pref('media.cache_size', 100);
user_pref("browser.panorama.experienced_first_run", true); // Assume experienced user_pref('security.warn_viewing_mixed', false);
user_pref("dom.w3c_touch_events.enabled", true); user_pref('app.update.enabled', false);
user_pref("extensions.checkCompatibility", false); user_pref('browser.panorama.experienced_first_run', true); // Assume experienced
user_pref("extensions.installDistroAddons", false); // prevent testpilot etc user_pref('dom.w3c_touch_events.enabled', true);
user_pref("browser.safebrowsing.enable", false); // prevent traffic to google servers user_pref('extensions.checkCompatibility', false);
user_pref("toolkit.telemetry.prompted", true); // prevent telemetry banner user_pref('extensions.installDistroAddons', false); // prevent testpilot etc
user_pref("toolkit.telemetry.enabled", false); user_pref('browser.safebrowsing.enable', false); // prevent traffic to google servers
user_pref('toolkit.telemetry.prompted', true); // prevent telemetry banner
user_pref('toolkit.telemetry.enabled', false);

View File

@ -469,7 +469,7 @@ def maybeUpdateRefImages(options, browser):
else: else:
print ' Yes! The references in tmp/ can be synced with ref/.' print ' Yes! The references in tmp/ can be synced with ref/.'
if options.reftest: if options.reftest:
startReftest(browser) startReftest(browser, options)
if not prompt('Would you like to update the master copy in ref/?'): if not prompt('Would you like to update the master copy in ref/?'):
print ' OK, not updating.' print ' OK, not updating.'
else: else:

View File

@ -64,10 +64,38 @@
"rounds": 1, "rounds": 1,
"type": "load" "type": "load"
}, },
{ "id": "complexttffont-pdf",
"file": "pdfs/complex_ttf_font.pdf",
"rounds": 1,
"type": "load"
},
{ "id": "i9-pdf", { "id": "i9-pdf",
"file": "pdfs/i9.pdf", "file": "pdfs/i9.pdf",
"link": true, "link": true,
"rounds": 1, "rounds": 1,
"type": "eq" "type": "eq"
},
{ "id": "hmm-pdf",
"file": "pdfs/hmm.pdf",
"link": true,
"rounds": 1,
"type": "load"
},
{ "id": "rotation",
"file": "pdfs/rotation.pdf",
"rounds": 1,
"type": "load"
},
{ "id": "ecma262-pdf",
"file": "pdfs/ecma262.pdf",
"link": true,
"rounds": 1,
"type": "load"
},
{ "id": "jai-pdf",
"file": "pdfs/jai.pdf",
"link": true,
"rounds": 1,
"type": "load"
} }
] ]

View File

@ -40,6 +40,7 @@
// so we can use the TypedArray as well // so we can use the TypedArray as well
window.Uint32Array = TypedArray; window.Uint32Array = TypedArray;
window.Int32Array = TypedArray; window.Int32Array = TypedArray;
window.Uint16Array = TypedArray;
})(); })();
// Object.create() ? // Object.create() ?

View File

@ -50,7 +50,7 @@
</div> </div>
<h2>Try it out!</h2> <h2>Try it out!</h2>
<p>Live <a href="web/multi_page_viewer.html">demo</a> lives here.</p> <p>Live <a href="web/viewer.html">demo</a> lives here.</p>
<h2>Authors</h2> <h2>Authors</h2>
<p>Vivien Nicolas (21@vingtetun.org) <p>Vivien Nicolas (21@vingtetun.org)

View File

@ -38,21 +38,20 @@ var PDFView = {
set page(val) { set page(val) {
var pages = this.pages; var pages = this.pages;
if (val <= 0 || val == this.page || val > pages.length) { var input = document.getElementById('pageNumber');
// TODO If the hash if set to a dumb value, like #123456, the input field if (val <= 0 || val > pages.length) {
// of the UI will be set to it even if no page is changed because its out input.value = this.page;
// of bound. return;
val = this.page || 1;
} else {
// Draw the page before jumping to it in order to avoid seeing the
// possible gap between pages if the page has never been draw before.
pages[val - 1].draw();
document.location.hash = val;
} }
var event = document.createEvent("UIEvents"); document.location.hash = val;
event.initUIEvent("pagechange", false, false, window, val); document.getElementById('previous').disabled = (val == 1);
window.dispatchEvent(event); document.getElementById('next').disabled = (val == pages.length);
if (input.value == val)
return;
input.value = val;
pages[val - 1].draw();
}, },
get page() { get page() {
@ -60,7 +59,7 @@ var PDFView = {
}, },
open: function(url, scale) { open: function(url, scale) {
if (url.indexOf("http") == 0) if (url.indexOf('http') == 0)
return; return;
document.title = url; document.title = url;
@ -82,6 +81,19 @@ var PDFView = {
xhr.send(null); xhr.send(null);
}, },
navigateTo: function(dest) {
if (typeof dest === 'string')
dest = this.destinations[dest];
// dest array looks like that: <page-ref> </XYZ|FitXXX> <args..>
var destRef = dest[0];
var pageNumber = this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'];
if (pageNumber) {
this.page = pageNumber;
// TODO scroll to specific region on the page, the precise scaling
// required.
}
},
load: function(data, scale) { load: function(data, scale) {
var sidebar = document.getElementById('sidebarView'); var sidebar = document.getElementById('sidebarView');
sidebar.parentNode.scrollTop = 0; sidebar.parentNode.scrollTop = 0;
@ -99,18 +111,21 @@ var PDFView = {
document.getElementById('numPages').innerHTML = pagesCount; document.getElementById('numPages').innerHTML = pagesCount;
var pages = this.pages = []; var pages = this.pages = [];
var pagesRefMap = {};
var thumbnails = this.thumbnails = []; var thumbnails = this.thumbnails = [];
for (var i = 1; i <= pagesCount; i++) { for (var i = 1; i <= pagesCount; i++) {
var page = pdf.getPage(i); var page = pdf.getPage(i);
var mediaBox = page.mediaBox; pages.push(new PageView(container, page, i, page.width, page.height,
var width = (mediaBox[2] - mediaBox[0]); page.stats, this.navigateTo.bind(this)));
var height = (mediaBox[3] - mediaBox[1]);
pages.push(new PageView(container, page, i, width, height, page.stats));
thumbnails.push(new ThumbnailView(sidebar, pages[i - 1])); thumbnails.push(new ThumbnailView(sidebar, pages[i - 1]));
}; var pageRef = page.ref;
pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i;
}
this.scale = (scale || kDefaultScale); this.scale = (scale || kDefaultScale);
this.page = parseInt(document.location.hash.substring(1)) || 1; this.page = parseInt(document.location.hash.substring(1)) || 1;
this.pagesRefMap = pagesRefMap;
this.destinations = pdf.catalog.destinations;
}, },
getVisiblePages: function() { getVisiblePages: function() {
@ -137,10 +152,11 @@ var PDFView = {
} }
return visiblePages; return visiblePages;
}, }
}; };
var PageView = function(container, content, id, width, height, stats) { var PageView = function(container, content, id, width, height,
stats, navigateTo) {
this.width = width; this.width = width;
this.height = height; this.height = height;
this.id = id; this.id = id;
@ -158,13 +174,50 @@ var PageView = function(container, content, id, width, height, stats) {
this.update = function(scale) { this.update = function(scale) {
this.scale = scale || this.scale; this.scale = scale || this.scale;
div.style.width = (this.width * this.scale)+ 'px'; div.style.width = (this.width * this.scale) + 'px';
div.style.height = (this.height * this.scale) + 'px'; div.style.height = (this.height * this.scale) + 'px';
while (div.hasChildNodes()) while (div.hasChildNodes())
div.removeChild(div.lastChild); div.removeChild(div.lastChild);
}; };
function setupLinks(canvas, content, scale) {
var links = content.getLinks();
var currentLink = null;
if (links.length > 0) {
canvas.addEventListener('mousemove', function(e) {
var x = e.pageX;
var y = e.pageY;
for (var p = canvas; p; p = p.offsetParent) {
x -= p.offsetLeft;
y -= p.offsetTop;
}
x /= scale;
y /= scale;
var i, n = links.length;
for (i = 0; i < n; i++) {
var link = links[i];
if (link.x <= x && link.y <= y &&
x < link.x + link.width && y < link.y + link.height) {
currentLink = link;
canvas.style.cursor = 'pointer';
return;
}
}
currentLink = null;
canvas.style.cursor = 'default';
}, false);
canvas.addEventListener('mousedown', function(e) {
if (!currentLink)
return;
if (currentLink.url)
window.location.href = currentLink.url;
if (currentLink.dest)
navigateTo(currentLink.dest);
}, false);
}
}
this.draw = function() { this.draw = function() {
if (div.hasChildNodes()) { if (div.hasChildNodes()) {
this.updateStats(); this.updateStats();
@ -188,6 +241,8 @@ var PageView = function(container, content, id, width, height, stats) {
stats.begin = Date.now(); stats.begin = Date.now();
this.content.startRendering(ctx, this.updateStats); this.content.startRendering(ctx, this.updateStats);
setupLinks(canvas, this.content, this.scale);
return true; return true;
}; };
@ -271,11 +326,11 @@ window.addEventListener('scroll', function onscroll(evt) {
PDFView.page = firstPage.id; PDFView.page = firstPage.id;
}, true); }, true);
window.addEventListener("hashchange", function(evt) { window.addEventListener('hashchange', function(evt) {
PDFView.page = PDFView.page; PDFView.page = PDFView.page;
}); });
window.addEventListener("change", function(evt) { window.addEventListener('change', function(evt) {
var files = evt.target.files; var files = evt.target.files;
if (!files || files.length == 0) if (!files || files.length == 0)
return; return;
@ -301,7 +356,7 @@ window.addEventListener("change", function(evt) {
document.location.hash = 1; document.location.hash = 1;
}, true); }, true);
window.addEventListener("transitionend", function(evt) { window.addEventListener('transitionend', function(evt) {
var pageIndex = 0; var pageIndex = 0;
var pagesCount = PDFView.pages.length; var pagesCount = PDFView.pages.length;