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

132
fonts.js

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

315
pdf.js

@ -44,7 +44,7 @@ function assertWellFormed(cond, msg) {
} }
function shadow(obj, prop, value) { 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; return value;
} }
@ -2923,9 +2923,15 @@ var XRef = (function() {
var Page = (function() { var Page = (function() {
function constructor(xref, pageNumber, pageDict) { function constructor(xref, pageNumber, pageDict) {
this.xref = xref;
this.pageNumber = pageNumber; this.pageNumber = pageNumber;
this.pageDict = pageDict; this.pageDict = pageDict;
this.stats = {
create: Date.now(),
compile: 0.0,
fonts: 0.0,
render: 0.0,
};
this.xref = xref;
} }
constructor.prototype = { constructor.prototype = {
@ -2954,6 +2960,32 @@ var Page = (function() {
return shadow(this, 'mediaBox', return shadow(this, 'mediaBox',
((IsArray(obj) && obj.length == 4) ? obj : null)); ((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) { compile: function(gfx, fonts) {
if (this.code) { if (this.code) {
// content was compiled // content was compiled
@ -3385,18 +3417,13 @@ var Encodings = {
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
// <canvas> contexts store most of the state we need natively. var EvalState = (function() {
// However, PDF needs a bit more state, which we store here.
var CanvasExtraState = (function() {
function constructor() { function constructor() {
// Are soft masks and alpha values shapes or opacities? // Are soft masks and alpha values shapes or opacities?
this.alphaIsShape = false; this.alphaIsShape = false;
this.fontSize = 0; this.fontSize = 0;
this.textMatrix = IDENTITY_MATRIX; this.textMatrix = IDENTITY_MATRIX;
this.leading = 0; this.leading = 0;
// Current point (in user coordinates)
this.x = 0;
this.y = 0;
// Start of text line (in text coordinates) // Start of text line (in text coordinates)
this.lineX = 0; this.lineX = 0;
this.lineY = 0; this.lineY = 0;
@ -3413,34 +3440,13 @@ var CanvasExtraState = (function() {
return constructor; return constructor;
})(); })();
function ScratchCanvas(width, height) { var PartialEvaluator = (function() {
var canvas = document.createElement('canvas'); function constructor() {
canvas.width = width; this.state = new EvalState();
canvas.height = height; this.stateStack = [ ];
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 OP_MAP = {
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: {
// Graphics state // Graphics state
w: 'setLineWidth', w: 'setLineWidth',
J: 'setLineCap', J: 'setLineCap',
@ -3533,6 +3539,88 @@ var CanvasGraphics = (function() {
// Compatibility // Compatibility
BX: 'beginCompat', BX: 'beginCompat',
EX: 'endCompat' 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) { translateFont: function(fontDict, xref, resources) {
@ -3697,7 +3785,66 @@ var CanvasGraphics = (function() {
properties: properties 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) { beginDrawing: function(mediaBox) {
var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height; var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height;
this.ctx.save(); this.ctx.save();
@ -3705,6 +3852,11 @@ var CanvasGraphics = (function() {
this.ctx.translate(0, -mediaBox.height); 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) { execute: function(code, xref, resources) {
resources = xref.fetchIfRef(resources) || new Dict(); resources = xref.fetchIfRef(resources) || new Dict();
var savedXref = this.xref, savedRes = this.res, savedXobjs = this.xobjs; var savedXref = this.xref, savedRes = this.res, savedXobjs = this.xobjs;
@ -3719,88 +3871,6 @@ var CanvasGraphics = (function() {
this.xref = savedXref; 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() { endDrawing: function() {
this.ctx.restore(); this.ctx.restore();
}, },
@ -4349,8 +4419,8 @@ var CanvasGraphics = (function() {
// Most likely we will not implement other types of shading // Most likely we will not implement other types of shading
// unless the browser supports them // unless the browser supports them
if (!shadingFn) { if (!shadingFn) {
TODO("Unknown or NYI type of shading '"+ typeNum +"'"); warn("Unknown or NYI type of shading '"+ typeNum +"'");
return this.makeCssRgb(0, 0, 0); return 'hotpink';
} }
return shadingFn.call(this, shading, cs); return shadingFn.call(this, shading, cs);
@ -5310,13 +5380,16 @@ var PDFFunction = (function() {
return array; return array;
}, },
constructInterpolated: function() { constructInterpolated: function() {
error('unhandled type of function'); TODO('unhandled type of function');
this.func = function () { return [ 255, 105, 180 ]; }
}, },
constructStiched: function() { constructStiched: function() {
error('unhandled type of function'); TODO('unhandled type of function');
this.func = function () { return [ 255, 105, 180 ]; }
}, },
constructPostScript: function() { 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 @@
/* -*- 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 * A Test Driver for PDF.js
*/ */
var appPath, browser, canvas, currentTaskIdx, manifest, stdout; var appPath, browser, canvas, currentTaskIdx, manifest, stdout;
function queryParams() { function queryParams() {
@ -92,25 +94,13 @@ function nextPage(task, loadError) {
var failure = loadError || ''; var failure = loadError || '';
var ctx = null; var ctx = null;
var fonts;
var gfx = null;
var page = null; var page = null;
if (!failure) { if (!failure) {
try {
log(" loading page "+ task.pageNum +"... "); log(" loading page "+ task.pageNum +"... ");
ctx = canvas.getContext("2d"); ctx = canvas.getContext("2d");
fonts = [];
try {
gfx = new CanvasGraphics(ctx);
page = task.pdfDoc.getPage(task.pageNum); 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; var pdfToCssUnitsCoef = 96.0 / 72.0;
// using mediaBox for the canvas size // using mediaBox for the canvas size
var pageWidth = (page.mediaBox[2] - page.mediaBox[0]); var pageWidth = (page.mediaBox[2] - page.mediaBox[0]);
@ -118,39 +108,25 @@ function nextPage(task, loadError) {
canvas.width = pageWidth * pdfToCssUnitsCoef; canvas.width = pageWidth * pdfToCssUnitsCoef;
canvas.height = pageHeight * pdfToCssUnitsCoef; canvas.height = pageHeight * pdfToCssUnitsCoef;
clear(ctx); clear(ctx);
} catch(e) {
failure = 'page setup: '+ e.toString();
}
}
if (!failure) { page.startRendering(
try { ctx,
FontLoader.bind(fonts, function() { function() { snapshotCurrentPage(page, task, failure); });
snapshotCurrentPage(gfx, page, task, failure);
});
} catch(e) { } catch(e) {
failure = 'fonts: '+ e.toString(); failure = 'page setup: '+ e.toString();
} }
} }
if (failure) { if (failure) {
// Skip right to snapshotting if there was a failure, since the // Skip right to snapshotting if there was a failure, since the
// fonts might be in an inconsistent state. // 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... "); log("done, snapshotting... ");
if (!failure) {
try {
page.display(gfx);
} catch(e) {
failure = 'render: '+ e.toString();
}
}
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");

2
test/test.py

@ -189,7 +189,7 @@ class BaseBrowserCommand(object):
self._fixupMacPath() self._fixupMacPath()
if not os.path.exists(self.path): 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): def setup(self):
self.tempDir = tempfile.mkdtemp() 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 {
span { span {
font-size: 0.8em; font-size: 0.8em;
text-shadow: 0px 1px 0px #fff;
} }
.control { .control {
@ -31,12 +32,12 @@ span {
height: 20px; height: 20px;
padding: 0px; padding: 0px;
margin: 0px 2px 0px 0px; margin: 0px 2px 0px 0px;
border-radius: 4px; border-radius: 3px;
-moz-border-radius: 4px; -moz-border-radius: 3px;
-webkit-border-radius: 4px; -webkit-border-radius: 3px;
box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25); box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.3);
-moz-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25); -moz-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25); -webkit-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.3);
} }
.control > select { .control > select {
@ -45,12 +46,12 @@ span {
height: 22px; height: 22px;
padding: 2px 0px 0px; padding: 2px 0px 0px;
margin: 0px 0px 1px; margin: 0px 0px 1px;
border-radius: 4px; border-radius: 3px;
-moz-border-radius: 4px; -moz-border-radius: 3px;
-webkit-border-radius: 4px; -webkit-border-radius: 3px;
box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25); box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.3);
-moz-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25); -moz-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25); -webkit-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.3);
} }
.control > span { .control > span {
@ -96,6 +97,8 @@ span {
#controls { #controls {
background-color: #eee; 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; border-bottom: 1px solid #666;
padding: 4px 0px 0px 8px; padding: 4px 0px 0px 8px;
position: fixed; position: fixed;
@ -114,58 +117,121 @@ span {
-webkit-user-select: text; -webkit-user-select: text;
} }
#previousPageButton { button {
background: url('images/buttons.png') no-repeat 0px -23px; 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; cursor: default;
display: inline-block;
float: left; float: left;
margin: 0px; margin: 0px 0px 1px;
width: 28px; width: 29px;
height: 23px; height: 22px;
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
} }
#previousPageButton.down { button:disabled {
background: url('images/buttons.png') no-repeat 0px -46px; 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; background: url('images/buttons.png') no-repeat 0px 0px;
display: inline-block;
width: 19px;
height: 19px;
} }
#nextPageButton { #nextPageButton {
background: url('images/buttons.png') no-repeat -28px -23px; width: 28px;
cursor: default; 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; display: inline-block;
float: left; width: 19px;
margin: 0px; height: 19px;
}
#singleLayoutButton {
width: 28px; 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 { #singleLayoutButton > span {
background: url('images/buttons.png') no-repeat -28px -46px; background: url('images/buttons.png') no-repeat -57px 0px;
display: inline-block;
width: 19px;
height: 19px;
} }
#nextPageButton.disabled { #splitLayoutButton {
background: url('images/buttons.png') no-repeat -28px 0px; 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 { #splitLayoutButton > span {
background: url('images/buttons.png') no-repeat -56px -23px; background: url('images/buttons.png') no-repeat -76px 0px;
cursor: default;
display: inline-block; display: inline-block;
float: left; width: 19px;
margin: 0px 0px 0px 3px; height: 19px;
width: 29px;
height: 23px;
} }
#openFileButton.down { #openFileButton {
background: url('images/buttons.png') no-repeat -56px -46px; margin-left: 3px;
} }
#openFileButton.disabled { #openFileButton > span {
background: url('images/buttons.png') no-repeat -56px 0px; background: url('images/buttons.png') no-repeat -38px 0px;
display: inline-block;
width: 19px;
height: 19px;
} }
#fileInput { #fileInput {

16
web/multi_page_viewer.html

@ -14,8 +14,8 @@
<body> <body>
<div id="controls"> <div id="controls">
<span class="control"> <span class="control">
<span id="previousPageButton" class="disabled"></span> <button id="previousPageButton" disabled="disabled"><span></span></button>
<span id="nextPageButton" class="disabled"></span> <button id="nextPageButton" disabled="disabled"><span></span></button>
<span class="label">Previous/Next</span> <span class="label">Previous/Next</span>
</span> </span>
<span class="control"> <span class="control">
@ -35,8 +35,18 @@
</select> </select>
<span class="label">Zoom</span> <span class="label">Zoom</span>
</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 class="control" id="fileWrapper">
<span id="openFileButton"></span> <button id="openFileButton"><span></span></button>
<input type="file" id="fileInput"/> <input type="file" id="fileInput"/>
<span class="label">Open File</span> <span class="label">Open File</span>
</span> </span>

68
web/multi_page_viewer.js

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

26
web/viewer.js

@ -72,29 +72,15 @@ function displayPage(num) {
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore(); ctx.restore();
var gfx = new CanvasGraphics(ctx); page.startRendering(
ctx,
// page.compile will collect all fonts for us, once we have loaded them function() {
// 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();
var infoDisplay = document.getElementById('info'); 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: ' + infoDisplay.innerHTML = 'Time to load/compile/fonts/render: ' +
(t1 - t0) + '/' + (t2 - t1) + '/' + (t3 - t2) + '/' + (t4 - t3) + ' ms'; (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() { function nextPage() {

Loading…
Cancel
Save