Browse Source

Merge branch 'master' into shading

Conflicts:
	pdf.js
sbarman 14 years ago
parent
commit
4861a9175d
  1. 10
      Makefile
  2. 132
      fonts.js
  3. 313
      pdf.js
  4. 44
      test/driver.js
  5. 2
      test/test.py
  6. BIN
      web/images/buttons.png
  7. BIN
      web/images/source/Buttons.psd.zip
  8. BIN
      web/images/source/FileButton.psd.zip
  9. 148
      web/multi_page_viewer.css
  10. 16
      web/multi_page_viewer.html
  11. 68
      web/multi_page_viewer.js
  12. 26
      web/viewer.js

10
Makefile

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
REPO = git@github.com:andreasgal/pdf.js.git
BUILD_DIR := build
DEFAULT_BROWSERS := test/resources/browser_manifests/browser_manifest.json
DEFAULT_TESTS := test/test_manifest.json
DEFAULT_BROWSERS := resources/browser_manifests/browser_manifest.json
DEFAULT_TESTS := test_manifest.json
# Let folks define custom rules for their clones.
-include local.mk
@ -43,7 +43,7 @@ PDF_BROWSERS := $(DEFAULT_BROWSERS) @@ -43,7 +43,7 @@ PDF_BROWSERS := $(DEFAULT_BROWSERS)
endif
browser-test:
@if [ ! -f "$(PDF_BROWSERS)" ]; then \
@if [ ! -f "test/$(PDF_BROWSERS)" ]; then \
echo "Browser manifest file $(PDF_BROWSERS) does not exist."; \
echo "Try copying one of the examples" \
"in test/resources/browser_manifests/"; \
@ -52,8 +52,8 @@ browser-test: @@ -52,8 +52,8 @@ browser-test:
cd test; \
python test.py --reftest \
--browserManifestFile=$(abspath $(PDF_BROWSERS)) \
--manifestFile=$(abspath $(PDF_TESTS))
--browserManifestFile=$(PDF_BROWSERS) \
--manifestFile=$(PDF_TESTS)
# make shell-test
#

132
fonts.js

@ -542,21 +542,12 @@ var Font = (function() { @@ -542,21 +542,12 @@ var Font = (function() {
'\x00\x01' + // encodingID
string32(4 + numTables * 8); // start of the table record
var headerSize = (12 * 2 + (ranges.length * 5 * 2));
var segCount = ranges.length + 1;
var segCount2 = segCount * 2;
var searchRange = getMaxPower2(segCount) * 2;
var searchEntry = Math.log(segCount) / Math.log(2);
var rangeShift = 2 * segCount - searchRange;
var format314 = '\x00\x04' + // format
string16(headerSize) + // length
'\x00\x00' + // language
string16(segCount2) +
string16(searchRange) +
string16(searchEntry) +
string16(rangeShift);
// Fill up the 4 parallel arrays describing the segments.
var startCount = '';
var endCount = '';
@ -568,7 +559,7 @@ var Font = (function() { @@ -568,7 +559,7 @@ var Font = (function() {
var range = ranges[i];
var start = range[0];
var end = range[1];
var delta = (bias - start) % 0xffff;
var delta = (bias - start) & 0xffff;
bias += (end - start + 1);
startCount += string16(start);
@ -585,10 +576,19 @@ var Font = (function() { @@ -585,10 +576,19 @@ var Font = (function() {
startCount += '\xFF\xFF';
idDeltas += '\x00\x01';
idRangeOffsets += '\x00\x00';
format314 += endCount + '\x00\x00' + startCount +
var format314 = '\x00\x00' + // language
string16(segCount2) +
string16(searchRange) +
string16(searchEntry) +
string16(rangeShift) +
endCount + '\x00\x00' + startCount +
idDeltas + idRangeOffsets + glyphsIds;
return stringToArray(cmap + format314);
return stringToArray(cmap +
'\x00\x04' + // format
string16(format314.length + 4) + // length
format314);
};
function createOS2Table(properties) {
@ -705,6 +705,9 @@ var Font = (function() { @@ -705,6 +705,9 @@ var Font = (function() {
var data = file.getBytes(length);
file.pos = previousPosition;
if (tag == 'head')
data[8] = data[9] = data[10] = data[11] = 0; // clearing checksum adjustment
return {
tag: tag,
checksum: checksum,
@ -907,11 +910,6 @@ var Font = (function() { @@ -907,11 +910,6 @@ var Font = (function() {
convert: function font_convert(fontName, font, properties) {
function createNameTable(name) {
// All the strings of the name table should be an odd number
// of bytes
if (name.length % 2)
name = name.slice(0, name.length - 1);
var strings = [
'Original licence', // 0.Copyright
name, // 1.Font family
@ -919,7 +917,7 @@ var Font = (function() { @@ -919,7 +917,7 @@ var Font = (function() {
'uniqueID', // 3.Unique ID
name, // 4.Full font name
'Version 0.11', // 5.Version
'Unknown', // 6.Postscript name
'', // 6.Postscript name
'Unknown', // 7.Trademark
'Unknown', // 8.Manufacturer
'Unknown' // 9.Designer
@ -958,7 +956,7 @@ var Font = (function() { @@ -958,7 +956,7 @@ var Font = (function() {
platforms[i] + // platform ID
encodings[i] + // encoding ID
languages[i] + // language ID
string16(i) + // name ID
string16(j) + // name ID
string16(str.length) +
string16(strOffset);
nameTable += nameRecord;
@ -1133,6 +1131,12 @@ var Font = (function() { @@ -1133,6 +1131,12 @@ var Font = (function() {
for (var i = 0; i < chars.length; ++i) {
var charcode = chars.charCodeAt(i);
var unicode = encoding[charcode];
if ('undefined' == typeof(unicode)) {
// FIXME/issue 233: we're hitting this in test/pdf/sizes.pdf
// at the moment, for unknown reasons.
warn('Unencoded charcode '+ charcode);
unicode = charcode;
}
// Check if the glyph has already been converted
if (!IsNum(unicode))
@ -1388,6 +1392,18 @@ var Type1Parser = function() { @@ -1388,6 +1392,18 @@ var Type1Parser = function() {
return array;
};
function readNumber(str, index) {
while (str[index++] == ' ')
;
var start = index;
var count = 0;
while (str[index++] != ' ')
count++;
return parseFloat(str.substr(start, count) || 0);
};
this.extractFontProgram = function t1_extractFontProgram(stream) {
var eexec = decrypt(stream, kEexecEncryptionKey, 4);
var eexecStr = '';
@ -1399,8 +1415,7 @@ var Type1Parser = function() { @@ -1399,8 +1415,7 @@ var Type1Parser = function() {
subrs: [],
charstrings: [],
properties: {
stemSnapH: [0, 0],
stemSnapV: [0, 0]
'private': {}
}
};
@ -1442,17 +1457,24 @@ var Type1Parser = function() { @@ -1442,17 +1457,24 @@ var Type1Parser = function() {
case '/Subrs':
subrsSection = true;
break;
case '/StdHW':
program.properties.stdHW = readNumberArray(eexecStr, i + 2)[0];
case '/BlueValues':
case '/OtherBlues':
case '/FamilyBlues':
case '/FamilyOtherBlues':
case '/StemSnapH':
case '/StemSnapV':
program.properties.private[token.substring(1)] = readNumberArray(eexecStr, i + 2);
break;
case '/StdHW':
case '/StdVW':
program.properties.stdVW = readNumberArray(eexecStr, i + 2)[0];
program.properties.private[token.substring(1)] = readNumberArray(eexecStr, i + 2)[0];
break;
case '/StemSnapH':
program.properties.stemSnapH = readNumberArray(eexecStr, i + 2);
break;
case '/StemSnapV':
program.properties.stemSnapV = readNumberArray(eexecStr, i + 2);
case '/BlueShift':
case '/BlueFuzz':
case '/BlueScale':
case '/LanguageGroup':
case '/ExpansionFactor':
program.properties.private[token.substring(1)] = readNumber(eexecStr, i + 1);
break;
}
} else if (c == '/') {
@ -1642,7 +1664,7 @@ CFF.prototype = { @@ -1642,7 +1664,7 @@ CFF.prototype = {
return '\x1c' +
String.fromCharCode((value >> 8) & 0xFF) +
String.fromCharCode(value & 0xFF);
} else if (value >= (-2147483647-1) && value <= 2147483647) {
} else if (value >= (-2147483648) && value <= 2147483647) {
return '\xff' +
String.fromCharCode((value >>> 24) & 0xFF) +
String.fromCharCode((value >> 16) & 0xFF) +
@ -1769,10 +1791,10 @@ CFF.prototype = { @@ -1769,10 +1791,10 @@ CFF.prototype = {
var dict =
'\x00\x01\x01\x01\x30' +
'\xf8\x1b\x00' + // version
'\xf8\x1b\x01' + // Notice
'\xf8\x1b\x02' + // FullName
'\xf8\x1b\x03' + // FamilyName
'\xf8\x1b\x04' + // Weight
'\xf8\x1c\x01' + // Notice
'\xf8\x1d\x02' + // FullName
'\xf8\x1e\x03' + // FamilyName
'\xf8\x1f\x04' + // Weight
'\x1c\x00\x00\x10'; // Encoding
var boundingBox = properties.bbox;
@ -1791,7 +1813,7 @@ CFF.prototype = { @@ -1791,7 +1813,7 @@ CFF.prototype = {
dict += self.encodeNumber(offset) + '\x11'; // Charstrings
dict += self.encodeNumber(fields.private.length);
var offset = offset + fields.charstrings.length;
offset = offset + fields.charstrings.length;
dict += self.encodeNumber(offset) + '\x12'; // Private
return dict;
@ -1835,19 +1857,33 @@ CFF.prototype = { @@ -1835,19 +1857,33 @@ CFF.prototype = {
'private': (function(self) {
var data =
'\x8b\x14' + // defaultWidth
'\x8b\x15' + // nominalWidth
self.encodeNumber(properties.stdHW || 0) + '\x0a' + // StdHW
self.encodeNumber(properties.stdVW || 0) + '\x0b'; // StdVW
var stemH = properties.stemSnapH;
for (var i = 0; i < stemH.length; i++)
data += self.encodeNumber(stemH[i]);
data += '\x0c\x0c'; // StemSnapH
var stemV = properties.stemSnapV;
for (var i = 0; i < stemV.length; i++)
data += self.encodeNumber(stemV[i]);
data += '\x0c\x0d'; // StemSnapV
'\x8b\x15'; // nominalWidth
var fieldMap = {
BlueValues: '\x06',
OtherBlues: '\x07',
FamilyBlues: '\x08',
FamilyOtherBlues: '\x09',
StemSnapH: '\x0c\x0c',
StemSnapV: '\x0c\x0d',
BlueShift: '\x0c\x0a',
BlueFuzz: '\x0c\x0b',
BlueScale: '\x0c\x09',
LanguageGroup: '\x0c\x11',
ExpansionFactor: '\x0c\x18'
};
for (var field in fieldMap) {
if (!properties.private.hasOwnProperty(field)) continue;
var value = properties.private[field];
if (IsArray(value)) {
data += self.encodeNumber(value[0]);
for (var i = 1; i < value.length; i++)
data += self.encodeNumber(value[i] - value[i - 1]);
} else {
data += self.encodeNumber(value);
}
data += fieldMap[field];
}
data += self.encodeNumber(data.length + 4) + '\x13'; // Subrs offset

313
pdf.js

@ -44,7 +44,7 @@ function assertWellFormed(cond, msg) { @@ -44,7 +44,7 @@ function assertWellFormed(cond, msg) {
}
function shadow(obj, prop, value) {
Object.defineProperty(obj, prop, { value: value, enumerable: true });
Object.defineProperty(obj, prop, { value: value, enumerable: true, configurable: true, writable: false });
return value;
}
@ -2923,9 +2923,15 @@ var XRef = (function() { @@ -2923,9 +2923,15 @@ var XRef = (function() {
var Page = (function() {
function constructor(xref, pageNumber, pageDict) {
this.xref = xref;
this.pageNumber = pageNumber;
this.pageDict = pageDict;
this.stats = {
create: Date.now(),
compile: 0.0,
fonts: 0.0,
render: 0.0,
};
this.xref = xref;
}
constructor.prototype = {
@ -2954,6 +2960,32 @@ var Page = (function() { @@ -2954,6 +2960,32 @@ var Page = (function() {
return shadow(this, 'mediaBox',
((IsArray(obj) && obj.length == 4) ? obj : null));
},
startRendering: function(canvasCtx, continuation) {
var self = this;
var stats = self.stats;
stats.compile = stats.fonts = stats.render = 0;
var gfx = new CanvasGraphics(canvasCtx);
var fonts = [ ];
this.compile(gfx, fonts);
stats.compile = Date.now();
FontLoader.bind(
fonts,
function() {
stats.fonts = Date.now();
// Always defer call to display() to work around bug in
// Firefox error reporting from XHR callbacks.
setTimeout(function () {
self.display(gfx);
stats.render = Date.now();
continuation();
});
});
},
compile: function(gfx, fonts) {
if (this.code) {
// content was compiled
@ -3385,18 +3417,13 @@ var Encodings = { @@ -3385,18 +3417,13 @@ var Encodings = {
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
// <canvas> contexts store most of the state we need natively.
// However, PDF needs a bit more state, which we store here.
var CanvasExtraState = (function() {
var EvalState = (function() {
function constructor() {
// Are soft masks and alpha values shapes or opacities?
this.alphaIsShape = false;
this.fontSize = 0;
this.textMatrix = IDENTITY_MATRIX;
this.leading = 0;
// Current point (in user coordinates)
this.x = 0;
this.y = 0;
// Start of text line (in text coordinates)
this.lineX = 0;
this.lineY = 0;
@ -3413,34 +3440,13 @@ var CanvasExtraState = (function() { @@ -3413,34 +3440,13 @@ var CanvasExtraState = (function() {
return constructor;
})();
function ScratchCanvas(width, height) {
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
return canvas;
}
var CanvasGraphics = (function() {
function constructor(canvasCtx, imageCanvas) {
this.ctx = canvasCtx;
this.current = new CanvasExtraState();
var PartialEvaluator = (function() {
function constructor() {
this.state = new EvalState();
this.stateStack = [ ];
this.pendingClip = null;
this.res = null;
this.xobjs = null;
this.ScratchCanvas = imageCanvas || ScratchCanvas;
}
var LINE_CAP_STYLES = ['butt', 'round', 'square'];
var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
var NORMAL_CLIP = {};
var EO_CLIP = {};
// Used for tiling patterns
var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2;
constructor.prototype = {
map: {
var OP_MAP = {
// Graphics state
w: 'setLineWidth',
J: 'setLineCap',
@ -3533,6 +3539,88 @@ var CanvasGraphics = (function() { @@ -3533,6 +3539,88 @@ var CanvasGraphics = (function() {
// Compatibility
BX: 'beginCompat',
EX: 'endCompat'
};
constructor.prototype = {
eval: function(stream, xref, resources, fonts) {
resources = xref.fetchIfRef(resources) || new Dict();
var xobjs = xref.fetchIfRef(resources.get('XObject')) || new Dict();
var parser = new Parser(new Lexer(stream), false);
var objpool = [];
function emitArg(arg) {
if (typeof arg == 'object' || typeof arg == 'string') {
var index = objpool.length;
objpool[index] = arg;
return 'objpool[' + index + ']';
}
return arg;
}
var src = '';
var args = [];
var obj;
while (!IsEOF(obj = parser.getObj())) {
if (IsCmd(obj)) {
var cmd = obj.cmd;
var fn = OP_MAP[cmd];
assertWellFormed(fn, "Unknown command '" + cmd + "'");
// TODO figure out how to type-check vararg functions
if (cmd == 'Do' && !args[0].code) { // eagerly compile XForm objects
var name = args[0].name;
var xobj = xobjs.get(name);
if (xobj) {
xobj = xref.fetchIfRef(xobj);
assertWellFormed(IsStream(xobj), 'XObject should be a stream');
var type = xobj.dict.get('Subtype');
assertWellFormed(
IsName(type),
'XObject should have a Name subtype'
);
if ('Form' == type.name) {
args[0].code = this.eval(xobj,
xref,
xobj.dict.get('Resources'),
fonts);
}
}
} else if (cmd == 'Tf') { // eagerly collect all fonts
var fontRes = resources.get('Font');
if (fontRes) {
fontRes = xref.fetchIfRef(fontRes);
var font = xref.fetchIfRef(fontRes.get(args[0].name));
assertWellFormed(IsDict(font));
if (!font.translated) {
font.translated = this.translateFont(font, xref, resources);
if (fonts && font.translated) {
// keep track of each font we translated so the caller can
// load them asynchronously before calling display on a page
fonts.push(font.translated);
}
}
}
}
src += 'this.';
src += fn;
src += '(';
src += args.map(emitArg).join(',');
src += ');\n';
args.length = 0;
} else {
assertWellFormed(args.length <= 33, 'Too many arguments');
args.push(obj);
}
}
var fn = Function('objpool', src);
return function(gfx) { fn.call(gfx, objpool); };
},
translateFont: function(fontDict, xref, resources) {
@ -3697,7 +3785,66 @@ var CanvasGraphics = (function() { @@ -3697,7 +3785,66 @@ var CanvasGraphics = (function() {
properties: properties
};
},
};
return constructor;
})();
// <canvas> contexts store most of the state we need natively.
// However, PDF needs a bit more state, which we store here.
var CanvasExtraState = (function() {
function constructor() {
// Are soft masks and alpha values shapes or opacities?
this.alphaIsShape = false;
this.fontSize = 0;
this.textMatrix = IDENTITY_MATRIX;
this.leading = 0;
// Current point (in user coordinates)
this.x = 0;
this.y = 0;
// Start of text line (in text coordinates)
this.lineX = 0;
this.lineY = 0;
// Character and word spacing
this.charSpace = 0;
this.wordSpace = 0;
this.textHScale = 100;
// Color spaces
this.fillColorSpace = null;
this.strokeColorSpace = null;
}
constructor.prototype = {
};
return constructor;
})();
function ScratchCanvas(width, height) {
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
return canvas;
}
var CanvasGraphics = (function() {
function constructor(canvasCtx, imageCanvas) {
this.ctx = canvasCtx;
this.current = new CanvasExtraState();
this.stateStack = [];
this.pendingClip = null;
this.res = null;
this.xobjs = null;
this.ScratchCanvas = imageCanvas || ScratchCanvas;
}
var LINE_CAP_STYLES = ['butt', 'round', 'square'];
var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
var NORMAL_CLIP = {};
var EO_CLIP = {};
// Used for tiling patterns
var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2;
constructor.prototype = {
beginDrawing: function(mediaBox) {
var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height;
this.ctx.save();
@ -3705,6 +3852,11 @@ var CanvasGraphics = (function() { @@ -3705,6 +3852,11 @@ var CanvasGraphics = (function() {
this.ctx.translate(0, -mediaBox.height);
},
compile: function(stream, xref, resources, fonts) {
var pe = new PartialEvaluator();
return pe.eval(stream, xref, resources, fonts);
},
execute: function(code, xref, resources) {
resources = xref.fetchIfRef(resources) || new Dict();
var savedXref = this.xref, savedRes = this.res, savedXobjs = this.xobjs;
@ -3719,88 +3871,6 @@ var CanvasGraphics = (function() { @@ -3719,88 +3871,6 @@ var CanvasGraphics = (function() {
this.xref = savedXref;
},
compile: function(stream, xref, resources, fonts) {
resources = xref.fetchIfRef(resources) || new Dict();
var xobjs = xref.fetchIfRef(resources.get('XObject')) || new Dict();
var parser = new Parser(new Lexer(stream), false);
var objpool = [];
function emitArg(arg) {
if (typeof arg == 'object' || typeof arg == 'string') {
var index = objpool.length;
objpool[index] = arg;
return 'objpool[' + index + ']';
}
return arg;
}
var src = '';
var args = [];
var map = this.map;
var obj;
while (!IsEOF(obj = parser.getObj())) {
if (IsCmd(obj)) {
var cmd = obj.cmd;
var fn = map[cmd];
assertWellFormed(fn, "Unknown command '" + cmd + "'");
// TODO figure out how to type-check vararg functions
if (cmd == 'Do' && !args[0].code) { // eagerly compile XForm objects
var name = args[0].name;
var xobj = xobjs.get(name);
if (xobj) {
xobj = xref.fetchIfRef(xobj);
assertWellFormed(IsStream(xobj), 'XObject should be a stream');
var type = xobj.dict.get('Subtype');
assertWellFormed(
IsName(type),
'XObject should have a Name subtype'
);
if ('Form' == type.name) {
args[0].code = this.compile(xobj,
xref,
xobj.dict.get('Resources'),
fonts);
}
}
} else if (cmd == 'Tf') { // eagerly collect all fonts
var fontRes = resources.get('Font');
if (fontRes) {
fontRes = xref.fetchIfRef(fontRes);
var font = xref.fetchIfRef(fontRes.get(args[0].name));
assertWellFormed(IsDict(font));
if (!font.translated) {
font.translated = this.translateFont(font, xref, resources);
if (fonts && font.translated) {
// keep track of each font we translated so the caller can
// load them asynchronously before calling display on a page
fonts.push(font.translated);
}
}
}
}
src += 'this.';
src += fn;
src += '(';
src += args.map(emitArg).join(',');
src += ');\n';
args.length = 0;
} else {
assertWellFormed(args.length <= 33, 'Too many arguments');
args.push(obj);
}
}
var fn = Function('objpool', src);
return function(gfx) { fn.call(gfx, objpool); };
},
endDrawing: function() {
this.ctx.restore();
},
@ -4349,8 +4419,8 @@ var CanvasGraphics = (function() { @@ -4349,8 +4419,8 @@ var CanvasGraphics = (function() {
// Most likely we will not implement other types of shading
// unless the browser supports them
if (!shadingFn) {
TODO("Unknown or NYI type of shading '"+ typeNum +"'");
return this.makeCssRgb(0, 0, 0);
warn("Unknown or NYI type of shading '"+ typeNum +"'");
return 'hotpink';
}
return shadingFn.call(this, shading, cs);
@ -5310,13 +5380,16 @@ var PDFFunction = (function() { @@ -5310,13 +5380,16 @@ var PDFFunction = (function() {
return array;
},
constructInterpolated: function() {
error('unhandled type of function');
TODO('unhandled type of function');
this.func = function () { return [ 255, 105, 180 ]; }
},
constructStiched: function() {
error('unhandled type of function');
TODO('unhandled type of function');
this.func = function () { return [ 255, 105, 180 ]; }
},
constructPostScript: function() {
error('unhandled type of function');
TODO('unhandled type of function');
this.func = function () { return [ 255, 105, 180 ]; }
}
};

44
test/driver.js

@ -1,8 +1,10 @@ @@ -1,8 +1,10 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
* A Test Driver for PDF.js
*/
var appPath, browser, canvas, currentTaskIdx, manifest, stdout;
function queryParams() {
@ -92,25 +94,13 @@ function nextPage(task, loadError) { @@ -92,25 +94,13 @@ function nextPage(task, loadError) {
var failure = loadError || '';
var ctx = null;
var fonts;
var gfx = null;
var page = null;
if (!failure) {
try {
log(" loading page "+ task.pageNum +"... ");
ctx = canvas.getContext("2d");
fonts = [];
try {
gfx = new CanvasGraphics(ctx);
page = task.pdfDoc.getPage(task.pageNum);
page.compile(gfx, fonts);
} catch(e) {
failure = 'compile: '+ e.toString();
}
}
if (!failure) {
try {
var pdfToCssUnitsCoef = 96.0 / 72.0;
// using mediaBox for the canvas size
var pageWidth = (page.mediaBox[2] - page.mediaBox[0]);
@ -118,39 +108,25 @@ function nextPage(task, loadError) { @@ -118,39 +108,25 @@ function nextPage(task, loadError) {
canvas.width = pageWidth * pdfToCssUnitsCoef;
canvas.height = pageHeight * pdfToCssUnitsCoef;
clear(ctx);
} catch(e) {
failure = 'page setup: '+ e.toString();
}
}
if (!failure) {
try {
FontLoader.bind(fonts, function() {
snapshotCurrentPage(gfx, page, task, failure);
});
page.startRendering(
ctx,
function() { snapshotCurrentPage(page, task, failure); });
} catch(e) {
failure = 'fonts: '+ e.toString();
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(gfx, page, task, failure);
snapshotCurrentPage(page, task, failure);
}
}
function snapshotCurrentPage(gfx, page, task, failure) {
function snapshotCurrentPage(page, task, failure) {
log("done, snapshotting... ");
if (!failure) {
try {
page.display(gfx);
} catch(e) {
failure = 'render: '+ e.toString();
}
}
sendTaskResult(canvas.toDataURL("image/png"), task, failure);
log("done"+ (failure ? " (failed!: "+ failure +")" : "") +"\n");

2
test/test.py

@ -189,7 +189,7 @@ class BaseBrowserCommand(object): @@ -189,7 +189,7 @@ class BaseBrowserCommand(object):
self._fixupMacPath()
if not os.path.exists(self.path):
throw("Path to browser '%s' does not exist." % self.path)
raise Exception("Path to browser '%s' does not exist." % self.path)
def setup(self):
self.tempDir = tempfile.mkdtemp()

BIN
web/images/buttons.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
web/images/source/Buttons.psd.zip

Binary file not shown.

BIN
web/images/source/FileButton.psd.zip

Binary file not shown.

148
web/multi_page_viewer.css

@ -16,6 +16,7 @@ canvas { @@ -16,6 +16,7 @@ canvas {
span {
font-size: 0.8em;
text-shadow: 0px 1px 0px #fff;
}
.control {
@ -31,12 +32,12 @@ span { @@ -31,12 +32,12 @@ span {
height: 20px;
padding: 0px;
margin: 0px 2px 0px 0px;
border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
-moz-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
-webkit-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.3);
-moz-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.3);
}
.control > select {
@ -45,12 +46,12 @@ span { @@ -45,12 +46,12 @@ span {
height: 22px;
padding: 2px 0px 0px;
margin: 0px 0px 1px;
border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
-moz-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
-webkit-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.3);
-moz-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.3);
}
.control > span {
@ -96,6 +97,8 @@ span { @@ -96,6 +97,8 @@ span {
#controls {
background-color: #eee;
background: -moz-linear-gradient(center bottom, #ddd 0%, #fff 100%);
background: -webkit-gradient(linear, left bottom, left top, color-stop(0.0, #ddd), color-stop(1.0, #fff));
border-bottom: 1px solid #666;
padding: 4px 0px 0px 8px;
position: fixed;
@ -114,58 +117,121 @@ span { @@ -114,58 +117,121 @@ span {
-webkit-user-select: text;
}
#previousPageButton {
background: url('images/buttons.png') no-repeat 0px -23px;
button {
background-color: #ddd;
background: -moz-linear-gradient(center bottom, #c3c3c3 0%, #f3f3f3 100%);
background: -webkit-gradient(linear, left bottom, left top, color-stop(0.0, #c3c3c3), color-stop(1.0, #f3f3f3));
border: 1px solid #4d4d4d;
cursor: default;
display: inline-block;
float: left;
margin: 0px;
width: 28px;
height: 23px;
margin: 0px 0px 1px;
width: 29px;
height: 22px;
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
}
#previousPageButton.down {
background: url('images/buttons.png') no-repeat 0px -46px;
button:disabled {
background-color: #eee;
background: -moz-linear-gradient(center bottom, #ddd 0%, #fff 100%);
background: -webkit-gradient(linear, left bottom, left top, color-stop(0.0, #ddd), color-stop(1.0, #fff));
}
button:disabled > span {
opacity: 0.3;
-moz-opacity: 0.3;
-webkit-opacity: 0.3;
}
button.down {
background-color: #777;
background: -moz-linear-gradient(center bottom, #888 0%, #555 100%);
background: -webkit-gradient(linear, left bottom, left top, color-stop(0.0, #888), color-stop(1.0, #555));
box-shadow: inset 0px 0px 2px rgba(0, 0, 0, 0.8);
-moz-box-shadow: inset 0px 0px 2px rgba(0, 0, 0, 0.8);
-webkit-box-shadow: inset 0px 0px 2px rgba(0, 0, 0, 0.8);
}
#previousPageButton {
width: 28px;
border-right: 0px;
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
-moz-border-radius-topright: 0px;
-moz-border-radius-bottomright: 0px;
-webkit-border-top-right-radius: 0px;
-webkit-border-bottom-right-radius: 0px;
}
#previousPageButton.disabled {
#previousPageButton > span {
background: url('images/buttons.png') no-repeat 0px 0px;
display: inline-block;
width: 19px;
height: 19px;
}
#nextPageButton {
background: url('images/buttons.png') no-repeat -28px -23px;
cursor: default;
width: 28px;
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
-moz-border-radius-topleft: 0px;
-moz-border-radius-bottomleft: 0px;
-webkit-border-top-left-radius: 0px;
-webkit-border-bottom-left-radius: 0px;
}
#nextPageButton > span {
background: url('images/buttons.png') no-repeat -19px 0px;
display: inline-block;
float: left;
margin: 0px;
width: 19px;
height: 19px;
}
#singleLayoutButton {
width: 28px;
height: 23px;
border-right: 0px;
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
-moz-border-radius-topright: 0px;
-moz-border-radius-bottomright: 0px;
-webkit-border-top-right-radius: 0px;
-webkit-border-bottom-right-radius: 0px;
}
#nextPageButton.down {
background: url('images/buttons.png') no-repeat -28px -46px;
#singleLayoutButton > span {
background: url('images/buttons.png') no-repeat -57px 0px;
display: inline-block;
width: 19px;
height: 19px;
}
#nextPageButton.disabled {
background: url('images/buttons.png') no-repeat -28px 0px;
#splitLayoutButton {
width: 28px;
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
-moz-border-radius-topleft: 0px;
-moz-border-radius-bottomleft: 0px;
-webkit-border-top-left-radius: 0px;
-webkit-border-bottom-left-radius: 0px;
}
#openFileButton {
background: url('images/buttons.png') no-repeat -56px -23px;
cursor: default;
#splitLayoutButton > span {
background: url('images/buttons.png') no-repeat -76px 0px;
display: inline-block;
float: left;
margin: 0px 0px 0px 3px;
width: 29px;
height: 23px;
width: 19px;
height: 19px;
}
#openFileButton.down {
background: url('images/buttons.png') no-repeat -56px -46px;
#openFileButton {
margin-left: 3px;
}
#openFileButton.disabled {
background: url('images/buttons.png') no-repeat -56px 0px;
#openFileButton > span {
background: url('images/buttons.png') no-repeat -38px 0px;
display: inline-block;
width: 19px;
height: 19px;
}
#fileInput {

16
web/multi_page_viewer.html

@ -14,8 +14,8 @@ @@ -14,8 +14,8 @@
<body>
<div id="controls">
<span class="control">
<span id="previousPageButton" class="disabled"></span>
<span id="nextPageButton" class="disabled"></span>
<button id="previousPageButton" disabled="disabled"><span></span></button>
<button id="nextPageButton" disabled="disabled"><span></span></button>
<span class="label">Previous/Next</span>
</span>
<span class="control">
@ -35,8 +35,18 @@ @@ -35,8 +35,18 @@
</select>
<span class="label">Zoom</span>
</span>
<!-- WIP: Leave commented out until implemented -->
<!--
<span class="control">
<button id="singleLayoutButton" class="selected"><span></span></button>
<button id="splitLayoutButton"><span></span></button>
<span class="label">Page Layout</span>
</span>
-->
<span class="control" id="fileWrapper">
<span id="openFileButton"></span>
<button id="openFileButton"><span></span></button>
<input type="file" id="fileInput"/>
<span class="label">Open File</span>
</span>

68
web/multi_page_viewer.js

@ -43,7 +43,7 @@ var PDFViewer = { @@ -43,7 +43,7 @@ var PDFViewer = {
lastPagesDrawn: [],
visiblePages: function() {
const pageBottomMargin = 10;
var pageBottomMargin = 10;
var windowTop = window.pageYOffset;
var windowBottom = window.pageYOffset + window.innerHeight;
@ -123,14 +123,7 @@ var PDFViewer = { @@ -123,14 +123,7 @@ var PDFViewer = {
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
var gfx = new CanvasGraphics(ctx);
// page.compile will collect all fonts for us, once we have loaded them
// we can trigger the actual page rendering with page.display
var fonts = [];
page.compile(gfx, fonts);
FontLoader.bind(fonts, function() { page.display(gfx); });
page.startRendering(ctx, function() { });
}
},
@ -183,14 +176,7 @@ var PDFViewer = { @@ -183,14 +176,7 @@ var PDFViewer = {
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
var gfx = new CanvasGraphics(ctx);
// page.compile will collect all fonts for us, once we have loaded them
// we can trigger the actual page rendering with page.display
var fonts = [];
page.compile(gfx, fonts);
FontLoader.bind(fonts, function() { page.display(gfx); });
page.startRendering(ctx, function() { });
}
},
@ -243,10 +229,8 @@ var PDFViewer = { @@ -243,10 +229,8 @@ var PDFViewer = {
setTimeout(window.onscroll, 0);
document.location.hash = PDFViewer.pageNumber;
PDFViewer.previousPageButton.className =
(PDFViewer.pageNumber === 1) ? 'disabled' : '';
PDFViewer.nextPageButton.className =
(PDFViewer.pageNumber === PDFViewer.numberOfPages) ? 'disabled' : '';
PDFViewer.previousPageButton.disabled = (PDFViewer.pageNumber === 1);
PDFViewer.nextPageButton.disabled = (PDFViewer.pageNumber === PDFViewer.numberOfPages);
}
},
@ -333,10 +317,8 @@ var PDFViewer = { @@ -333,10 +317,8 @@ var PDFViewer = {
}).bind(this), 500);
}
PDFViewer.previousPageButton.className =
(PDFViewer.pageNumber === 1) ? 'disabled' : '';
PDFViewer.nextPageButton.className =
(PDFViewer.pageNumber === PDFViewer.numberOfPages) ? 'disabled' : '';
PDFViewer.previousPageButton.disabled = (PDFViewer.pageNumber === 1);
PDFViewer.nextPageButton.disabled = (PDFViewer.pageNumber === PDFViewer.numberOfPages);
}
};
@ -404,42 +386,30 @@ window.onload = function() { @@ -404,42 +386,30 @@ window.onload = function() {
PDFViewer.previousPageButton = document.getElementById('previousPageButton');
PDFViewer.previousPageButton.onclick = function(evt) {
if (this.className.indexOf('disabled') === -1) {
PDFViewer.goToPreviousPage();
}
};
PDFViewer.previousPageButton.onmousedown = function(evt) {
if (this.className.indexOf('disabled') === -1) {
this.className = 'down';
}
};
PDFViewer.previousPageButton.onmouseup = function(evt) {
this.className =
(this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
this.className = '';
};
PDFViewer.previousPageButton.onmouseout = function(evt) {
this.className =
(this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
this.className = '';
};
PDFViewer.nextPageButton = document.getElementById('nextPageButton');
PDFViewer.nextPageButton.onclick = function(evt) {
if (this.className.indexOf('disabled') === -1) {
PDFViewer.goToNextPage();
}
};
PDFViewer.nextPageButton.onmousedown = function(evt) {
if (this.className.indexOf('disabled') === -1) {
this.className = 'down';
}
};
PDFViewer.nextPageButton.onmouseup = function(evt) {
this.className =
(this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
this.className = '';
};
PDFViewer.nextPageButton.onmouseout = function(evt) {
this.className =
(this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
this.className = '';
};
PDFViewer.scaleSelect = document.getElementById('scaleSelect');
@ -450,22 +420,16 @@ window.onload = function() { @@ -450,22 +420,16 @@ window.onload = function() {
if (window.File && window.FileReader && window.FileList && window.Blob) {
var openFileButton = document.getElementById('openFileButton');
openFileButton.onclick = function(evt) {
if (this.className.indexOf('disabled') === -1) {
PDFViewer.fileInput.click();
}
};
openFileButton.onmousedown = function(evt) {
if (this.className.indexOf('disabled') === -1) {
this.className = 'down';
}
};
openFileButton.onmouseup = function(evt) {
this.className =
(this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
this.className = '';
};
openFileButton.onmouseout = function(evt) {
this.className =
(this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
this.className = '';
};
PDFViewer.fileInput = document.getElementById('fileInput');
@ -540,10 +504,8 @@ window.onload = function() { @@ -540,10 +504,8 @@ window.onload = function() {
// Update the page number input with the current page number.
if (!PDFViewer.willJumpToPage && visiblePages.length > 0) {
PDFViewer.pageNumber = PDFViewer.pageNumberInput.value = visiblePages[0];
PDFViewer.previousPageButton.className =
(PDFViewer.pageNumber === 1) ? 'disabled' : '';
PDFViewer.nextPageButton.className =
(PDFViewer.pageNumber === PDFViewer.numberOfPages) ? 'disabled' : '';
PDFViewer.previousPageButton.disabled = (PDFViewer.pageNumber === 1);
PDFViewer.nextPageButton.disabled = (PDFViewer.pageNumber === PDFViewer.numberOfPages);
} else {
PDFViewer.willJumpToPage = false;
}

26
web/viewer.js

@ -72,29 +72,15 @@ function displayPage(num) { @@ -72,29 +72,15 @@ function displayPage(num) {
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
var gfx = new CanvasGraphics(ctx);
// page.compile will collect all fonts for us, once we have loaded them
// we can trigger the actual page rendering with page.display
var fonts = [];
page.compile(gfx, fonts);
var t2 = Date.now();
function displayPage() {
var t3 = Date.now();
page.display(gfx);
var t4 = Date.now();
page.startRendering(
ctx,
function() {
var infoDisplay = document.getElementById('info');
var stats = page.stats;
var t2 = stats.compile, t3 = stats.fonts, t4 = stats.render;
infoDisplay.innerHTML = 'Time to load/compile/fonts/render: ' +
(t1 - t0) + '/' + (t2 - t1) + '/' + (t3 - t2) + '/' + (t4 - t3) + ' ms';
}
// Always defer call to displayPage() to work around bug in
// Firefox error reporting from XHR callbacks.
FontLoader.bind(fonts, function() { setTimeout(displayPage, 0); });
});
}
function nextPage() {

Loading…
Cancel
Save