Merge remote branch 'upstream/master'
Conflicts: web/viewer.js
This commit is contained in:
commit
62e7d2f608
2
LICENSE
2
LICENSE
@ -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.
|
||||||
|
|
||||||
|
2
Makefile
2
Makefile
@ -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
392
fonts.js
@ -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;
|
||||||
|
240
test/driver.js
240
test/driver.js
@ -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();
|
||||||
}
|
}
|
||||||
|
575
test/pdfs/complex_ttf_font.pdf
Normal file
575
test/pdfs/complex_ttf_font.pdf
Normal file
File diff suppressed because one or more lines are too long
1
test/pdfs/ecma262.pdf.link
Normal file
1
test/pdfs/ecma262.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
|
1
test/pdfs/hmm.pdf.link
Normal file
1
test/pdfs/hmm.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
http://speech.tifr.res.in/tutorials/hmmTutExamplesAlgo.pdf
|
@ -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
1
test/pdfs/jai.pdf.link
Normal 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
BIN
test/pdfs/rotation.pdf
Normal file
Binary file not shown.
@ -0,0 +1,10 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name":"firefox",
|
||||||
|
"path":"C:/Program Files (x86)/Firefox/firefox.exe"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"aurora",
|
||||||
|
"path":"C:/Program Files (x86)/Aurora/firefox.exe"
|
||||||
|
}
|
||||||
|
]
|
@ -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);
|
||||||
|
@ -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:
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -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() ?
|
||||||
|
@ -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)
|
||||||
|
105
web/viewer.js
105
web/viewer.js
@ -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;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user