Merge pull request #45 from jviereck/master

WebWorker support
This commit is contained in:
Chris Jones 2011-06-23 15:21:10 -07:00
commit ba3c700bec
6 changed files with 749 additions and 40 deletions

250
canvas_proxy.js Normal file
View File

@ -0,0 +1,250 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
"use strict";
var JpegStreamProxyCounter = 0;
// WebWorker Proxy for JpegStream.
var JpegStreamProxy = (function() {
function constructor(bytes, dict) {
this.id = JpegStreamProxyCounter++;
this.dict = dict;
// Tell the main thread to create an image.
postMessage({
action: "jpeg_stream",
data: {
id: this.id,
raw: bytesToString(bytes)
}
});
}
constructor.prototype = {
getImage: function() {
return this;
},
getChar: function() {
error("internal error: getChar is not valid on JpegStream");
}
};
return constructor;
})();
// Really simple GradientProxy. There is currently only one active gradient at
// the time, meaning you can't create a gradient, create a second one and then
// use the first one again. As this isn't used in pdf.js right now, it's okay.
function GradientProxy(cmdQueue, x0, y0, x1, y1) {
cmdQueue.push(["$createLinearGradient", [x0, y0, x1, y1]]);
this.addColorStop = function(i, rgba) {
cmdQueue.push(["$addColorStop", [i, rgba]]);
}
}
// Really simple PatternProxy.
var patternProxyCounter = 0;
function PatternProxy(cmdQueue, object, kind) {
this.id = patternProxyCounter++;
if (!(object instanceof CanvasProxy) ) {
throw "unkown type to createPattern";
}
// Flush the object here to ensure it's available on the main thread.
// TODO: Make some kind of dependency management, such that the object
// gets flushed only if needed.
object.flush();
cmdQueue.push(["$createPatternFromCanvas", [this.id, object.id, kind]]);
}
var canvasProxyCounter = 0;
function CanvasProxy(width, height) {
this.id = canvasProxyCounter++;
// The `stack` holds the rendering calls and gets flushed to the main thead.
var cmdQueue = this.cmdQueue = [];
// Dummy context that gets exposed.
var ctx = {};
this.getContext = function(type) {
if (type != "2d") {
throw "CanvasProxy can only provide a 2d context.";
}
return ctx;
}
// Expose only the minimum of the canvas object - there is no dom to do
// more here.
this.width = width;
this.height = height;
ctx.canvas = this;
// Setup function calls to `ctx`.
var ctxFunc = [
"createRadialGradient",
"arcTo",
"arc",
"fillText",
"strokeText",
"createImageData",
"drawWindow",
"save",
"restore",
"scale",
"rotate",
"translate",
"transform",
"setTransform",
"clearRect",
"fillRect",
"strokeRect",
"beginPath",
"closePath",
"moveTo",
"lineTo",
"quadraticCurveTo",
"bezierCurveTo",
"rect",
"fill",
"stroke",
"clip",
"measureText",
"isPointInPath",
// These functions are necessary to track the rendering currentX state.
// The exact values can be computed on the main thread only, as the
// worker has no idea about text width.
"$setCurrentX",
"$addCurrentX",
"$saveCurrentX",
"$restoreCurrentX",
"$showText"
];
function buildFuncCall(name) {
return function() {
// console.log("funcCall", name)
cmdQueue.push([name, Array.prototype.slice.call(arguments)]);
}
}
var name;
for (var i = 0; i < ctxFunc.length; i++) {
name = ctxFunc[i];
ctx[name] = buildFuncCall(name);
}
// Some function calls that need more work.
ctx.createPattern = function(object, kind) {
return new PatternProxy(cmdQueue, object, kind);
}
ctx.createLinearGradient = function(x0, y0, x1, y1) {
return new GradientProxy(cmdQueue, x0, y0, x1, y1);
}
ctx.getImageData = function(x, y, w, h) {
return {
width: w,
height: h,
data: Uint8ClampedArray(w * h * 4)
};
}
ctx.putImageData = function(data, x, y, width, height) {
cmdQueue.push(["$putImageData", [data, x, y, width, height]]);
}
ctx.drawImage = function(image, x, y, width, height, sx, sy, swidth, sheight) {
if (image instanceof CanvasProxy) {
// Send the image/CanvasProxy to the main thread.
image.flush();
cmdQueue.push(["$drawCanvas", [image.id, x, y, sx, sy, swidth, sheight]]);
} else if(image instanceof JpegStreamProxy) {
cmdQueue.push(["$drawImage", [image.id, x, y, sx, sy, swidth, sheight]])
} else {
throw "unkown type to drawImage";
}
}
// Setup property access to `ctx`.
var ctxProp = {
// "canvas"
"globalAlpha": "1",
"globalCompositeOperation": "source-over",
"strokeStyle": "#000000",
"fillStyle": "#000000",
"lineWidth": "1",
"lineCap": "butt",
"lineJoin": "miter",
"miterLimit": "10",
"shadowOffsetX": "0",
"shadowOffsetY": "0",
"shadowBlur": "0",
"shadowColor": "rgba(0, 0, 0, 0)",
"font": "10px sans-serif",
"textAlign": "start",
"textBaseline": "alphabetic",
"mozTextStyle": "10px sans-serif",
"mozImageSmoothingEnabled": "true"
}
function buildGetter(name) {
return function() {
return ctx["$" + name];
}
}
function buildSetter(name) {
return function(value) {
cmdQueue.push(["$", name, value]);
return ctx["$" + name] = value;
}
}
// Setting the value to `stroke|fillStyle` needs special handling, as it
// might gets an gradient/pattern.
function buildSetterStyle(name) {
return function(value) {
if (value instanceof GradientProxy) {
cmdQueue.push(["$" + name + "Gradient"]);
} else if (value instanceof PatternProxy) {
cmdQueue.push(["$" + name + "Pattern", [value.id]]);
} else {
cmdQueue.push(["$", name, value]);
return ctx["$" + name] = value;
}
}
}
for (var name in ctxProp) {
ctx["$" + name] = ctxProp[name];
ctx.__defineGetter__(name, buildGetter(name));
// Special treatment for `fillStyle` and `strokeStyle`: The passed style
// might be a gradient. Need to check for that.
if (name == "fillStyle" || name == "strokeStyle") {
ctx.__defineSetter__(name, buildSetterStyle(name));
} else {
ctx.__defineSetter__(name, buildSetter(name));
}
}
}
/**
* Sends the current cmdQueue of the CanvasProxy over to the main thread and
* resets the cmdQueue.
*/
CanvasProxy.prototype.flush = function() {
postMessage({
action: "canvas_proxy_cmd_queue",
data: {
id: this.id,
cmdQueue: this.cmdQueue,
width: this.width,
height: this.height
}
});
this.cmdQueue.length = 0;
}

View File

@ -135,15 +135,28 @@ var Font = (function () {
break;
}
var data = this.font;
Fonts[name] = {
data: this.font,
data: data,
properties: properties,
loading: true,
cache: Object.create(null)
}
// Attach the font to the document
this.bind();
// Convert data to a string.
var dataStr = "";
var length = data.length;
for (var i = 0; i < length; ++i)
dataStr += String.fromCharCode(data[i]);
// Attach the font to the document. If this script is runnig in a worker,
// call `bindWorker`, which sends stuff over to the main thread.
if (typeof window != "undefined") {
this.bindDOM(dataStr);
} else {
this.bindWorker(dataStr);
}
};
function stringToArray(str) {
@ -755,12 +768,21 @@ var Font = (function () {
return fontData;
},
bind: function font_bind() {
var data = this.font;
bindWorker: function font_bind_worker(dataStr) {
postMessage({
action: "font",
data: {
raw: dataStr,
fontName: this.name,
mimetype: this.mimetype
}
});
},
bindDOM: function font_bind_dom(dataStr) {
var fontName = this.name;
/** Hack begin */
// Actually there is not event when a font has finished downloading so
// the following code are a dirty hack to 'guess' when a font is ready
var canvas = document.createElement("canvas");
@ -774,7 +796,7 @@ var Font = (function () {
// Get the font size canvas think it will be for 'spaces'
var ctx = canvas.getContext("2d");
ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
var testString = " ";
var testString = " ";
// When debugging use the characters provided by the charsets to visually
// see what's happening instead of 'spaces'
@ -831,13 +853,8 @@ var Font = (function () {
/** Hack end */
// Get the base64 encoding of the binary font data
var str = "";
var length = data.length;
for (var i = 0; i < length; ++i)
str += String.fromCharCode(data[i]);
var base64 = window.btoa(str);
// Convert the data string and add it to the page.
var base64 = window.btoa(dataStr);
// Add the @font-face rule to the document
var url = "url(data:" + this.mimetype + ";base64," + base64 + ");";

84
pdf.js
View File

@ -2269,15 +2269,34 @@ var Encodings = {
}
};
function ScratchCanvas(width, height) {
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
return canvas;
}
var CanvasGraphics = (function() {
function constructor(canvasCtx) {
function constructor(canvasCtx, imageCanvas) {
this.ctx = canvasCtx;
this.current = new CanvasExtraState();
this.stateStack = [ ];
this.pendingClip = null;
this.res = null;
this.xobjs = null;
this.map = {
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: {
// Graphics state
w: "setLineWidth",
J: "setLineCap",
@ -2370,18 +2389,8 @@ var CanvasGraphics = (function() {
// Compatibility
BX: "beginCompat",
EX: "endCompat",
};
}
},
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 = {
translateFont: function(fontDict, xref, resources) {
var fd = fontDict.get("FontDescriptor");
if (!fd)
@ -2669,12 +2678,18 @@ var CanvasGraphics = (function() {
},
save: function() {
this.ctx.save();
if (this.ctx.$saveCurrentX) {
this.ctx.$saveCurrentX();
}
this.stateStack.push(this.current);
this.current = new CanvasExtraState();
},
restore: function() {
var prev = this.stateStack.pop();
if (prev) {
if (this.ctx.$restoreCurrentX) {
this.ctx.$restoreCurrentX();
}
this.current = prev;
this.ctx.restore();
}
@ -2755,6 +2770,9 @@ var CanvasGraphics = (function() {
// Text
beginText: function() {
this.current.textMatrix = IDENTITY_MATRIX;
if (this.ctx.$setCurrentX) {
this.ctx.$setCurrentX(0)
}
this.current.x = this.current.lineX = 0;
this.current.y = this.current.lineY = 0;
},
@ -2809,6 +2827,9 @@ var CanvasGraphics = (function() {
moveText: function (x, y) {
this.current.x = this.current.lineX += x;
this.current.y = this.current.lineY += y;
if (this.ctx.$setCurrentX) {
this.ctx.$setCurrentX(this.current.x)
}
},
setLeadingMoveText: function(x, y) {
this.setLeading(-y);
@ -2816,6 +2837,10 @@ var CanvasGraphics = (function() {
},
setTextMatrix: function(a, b, c, d, e, f) {
this.current.textMatrix = [ a, b, c, d, e, f ];
if (this.ctx.$setCurrentX) {
this.ctx.$setCurrentX(0)
}
this.current.x = this.current.lineX = 0;
this.current.y = this.current.lineY = 0;
},
@ -2826,11 +2851,15 @@ var CanvasGraphics = (function() {
this.ctx.save();
this.ctx.transform.apply(this.ctx, this.current.textMatrix);
this.ctx.scale(1, -1);
this.ctx.translate(0, -2 * this.current.y);
text = Fonts.charsToUnicode(text);
this.ctx.fillText(text, this.current.x, this.current.y);
this.current.x += this.ctx.measureText(text).width;
if (this.ctx.$showText) {
this.ctx.$showText(this.current.y, Fonts.charsToUnicode(text));
} else {
text = Fonts.charsToUnicode(text);
this.ctx.translate(this.current.x, -1 * this.current.y);
this.ctx.fillText(Fonts.charsToUnicode(text), 0, 0);
this.current.x += this.ctx.measureText(text).width;
}
this.ctx.restore();
},
@ -2838,7 +2867,11 @@ var CanvasGraphics = (function() {
for (var i = 0; i < arr.length; ++i) {
var e = arr[i];
if (IsNum(e)) {
this.current.x -= e * 0.001 * this.current.fontSize;
if (this.ctx.$addCurrentX) {
this.ctx.$addCurrentX(-e * 0.001 * this.current.fontSize)
} else {
this.current.x -= e * 0.001 * this.current.fontSize;
}
} else if (IsString(e)) {
this.showText(e);
} else {
@ -2977,9 +3010,10 @@ var CanvasGraphics = (function() {
// we want the canvas to be as large as the step size
var botRight = applyMatrix([x0 + xstep, y0 + ystep], matrix);
var tmpCanvas = document.createElement("canvas");
tmpCanvas.width = Math.ceil(botRight[0] - topLeft[0]);
tmpCanvas.height = Math.ceil(botRight[1] - topLeft[1]);
var tmpCanvas = new this.ScratchCanvas(
Math.ceil(botRight[0] - topLeft[0]), // width
Math.ceil(botRight[1] - topLeft[1]) // height
);
// set the new canvas element context as the graphics context
var tmpCtx = tmpCanvas.getContext("2d");
@ -3041,6 +3075,7 @@ var CanvasGraphics = (function() {
shadingFill: function(entryRef) {
var xref = this.xref;
var res = this.res;
var shadingRes = xref.fetchIfRef(res.get("Shading"));
if (!shadingRes)
error("No shading resource found");
@ -3298,9 +3333,7 @@ var CanvasGraphics = (function() {
// handle matte object
}
var tmpCanvas = document.createElement("canvas");
tmpCanvas.width = w;
tmpCanvas.height = h;
var tmpCanvas = new this.ScratchCanvas(w, h);
var tmpCtx = tmpCanvas.getContext("2d");
var imgData = tmpCtx.getImageData(0, 0, w, h);
var pixels = imgData.data;
@ -3468,6 +3501,7 @@ var ColorSpace = (function() {
break;
case "ICCBased":
var dict = stream.dict;
this.stream = stream;
this.dict = dict;
this.numComps = dict.get("N");
@ -3574,6 +3608,7 @@ var PDFFunction = (function() {
v = encode[i2] + ((v - domain[i2]) *
(encode[i2 + 1] - encode[i2]) /
(domain[i2 + 1] - domain[i2]));
// clip to the size
args[i] = clip(v, 0, size[i] - 1);
}
@ -3601,6 +3636,7 @@ var PDFFunction = (function() {
// decode
v = decode[i2] + (v * (decode[i2 + 1] - decode[i2]) /
((1 << bps) - 1));
// clip to the domain
output.push(clip(v, range[i2], range[i2 + 1]));
}

88
pdf_worker.js Normal file
View File

@ -0,0 +1,88 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
"use strict";
var consoleTimer = {};
var console = {
log: function log() {
var args = Array.prototype.slice.call(arguments);
postMessage({
action: "log",
data: args
});
},
time: function(name) {
consoleTimer[name] = Date.now();
},
timeEnd: function(name) {
var time = consoleTimer[name];
if (time == null) {
throw "Unkown timer name " + name;
}
this.log("Timer:", name, Date.now() - time);
}
}
//
importScripts("canvas_proxy.js");
importScripts("pdf.js");
importScripts("fonts.js");
importScripts("glyphlist.js")
// Use the JpegStreamProxy proxy.
JpegStream = JpegStreamProxy;
// Create the WebWorkerProxyCanvas.
var canvas = new CanvasProxy(1224, 1584);
// Listen for messages from the main thread.
var pdfDocument = null;
onmessage = function(event) {
var data = event.data;
// If there is no pdfDocument yet, then the sent data is the PDFDocument.
if (!pdfDocument) {
pdfDocument = new PDFDoc(new Stream(data));
postMessage({
action: "pdf_num_pages",
data: pdfDocument.numPages
});
return;
}
// User requested to render a certain page.
else {
console.time("compile");
// Let's try to render the first page...
var page = pdfDocument.getPage(parseInt(data));
// 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 = [];
var gfx = new CanvasGraphics(canvas.getContext("2d"), CanvasProxy);
page.compile(gfx, fonts);
console.timeEnd("compile");
console.time("fonts");
// Inspect fonts and translate the missing one.
var count = fonts.length;
for (var i = 0; i < count; i++) {
var font = fonts[i];
if (Fonts[font.name]) {
fontsReady = fontsReady && !Fonts[font.name].loading;
continue;
}
// This "builds" the font and sents it over to the main thread.
new Font(font.name, font.file, font.properties);
}
console.timeEnd("fonts");
console.time("display");
page.display(gfx);
canvas.flush();
console.timeEnd("display");
}
}

46
viewer_worker.html Normal file
View File

@ -0,0 +1,46 @@
<html>
<head>
<title>Simple pdf.js page worker viewer</title>
<script type="text/javascript" src="worker_client.js"></script>
<script>
var pdfDoc;
window.onload = function() {
window.canvas = document.getElementById("canvas");
window.ctx = canvas.getContext("2d");
pdfDoc = new WorkerPDFDoc(window.canvas);
pdfDoc.onChangePage = function(numPage) {
document.getElementById("pageNumber").value = numPage;
}
// pdfDoc.open("canvas.pdf", function() {
pdfDoc.open("compressed.tracemonkey-pldi-09.pdf", function() {
document.getElementById("numPages").innerHTML = "/" + pdfDoc.numPages;
})
}
</script>
<link rel="stylesheet" href="viewer.css"></link>
</head>
<body>
<div id="controls">
<input type="file" style="float: right; margin: auto 32px;" onChange="load(this.value.toString());"></input>
<!-- This only opens supported PDFs from the source path...
-- Can we use JSONP to overcome the same-origin restrictions? -->
<button onclick="pdfDoc.prevPage();">Previous</button>
<button onclick="pdfDoc.nextPage();">Next</button>
<input type="text" id="pageNumber" onchange="pdfDoc.showPage(this.value);"
value="1" size="4"></input>
<span id="numPages">--</span>
<span id="info"></span>
</div>
<div id="viewer">
<!-- Canvas dimensions must be specified in CSS pixels. CSS pixels
are always 96 dpi. 816x1056 is 8.5x11in at 96dpi. -->
<!-- We're rendering here at 1.5x scale. -->
<canvas id="canvas" width="1224" height="1584"></canvas>
</div>
</body>
</html>

272
worker_client.js Normal file
View File

@ -0,0 +1,272 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
"use strict";
if (typeof console.time == "undefined") {
var consoleTimer = {};
console.time = function(name) {
consoleTimer[name] = Date.now();
};
console.timeEnd = function(name) {
var time = consoleTimer[name];
if (time == null) {
throw "Unkown timer name " + name;
}
this.log("Timer:", name, Date.now() - time);
};
}
function WorkerPDFDoc(canvas) {
var timer = null
this.ctx = canvas.getContext("2d");
this.canvas = canvas;
this.worker = new Worker('pdf_worker.js');
this.numPage = 1;
this.numPages = null;
var imagesList = {};
var canvasList = {
0: canvas
};
var patternList = {};
var gradient;
var currentX = 0;
var currentXStack = [];
var ctxSpecial = {
"$setCurrentX": function(value) {
currentX = value;
},
"$addCurrentX": function(value) {
currentX += value;
},
"$saveCurrentX": function() {
currentXStack.push(currentX);
},
"$restoreCurrentX": function() {
currentX = currentXStack.pop();
},
"$showText": function(y, text) {
this.translate(currentX, -1 * y);
this.fillText(text, 0, 0);
currentX += this.measureText(text).width;
},
"$putImageData": function(imageData, x, y) {
var imgData = this.getImageData(0, 0, imageData.width, imageData.height);
// Store the .data property to avaid property lookups.
var imageRealData = imageData.data;
var imgRealData = imgData.data;
// Copy over the imageData.
var len = imageRealData.length;
while (len--)
imgRealData[len] = imageRealData[len]
this.putImageData(imgData, x, y);
},
"$drawImage": function(id, x, y, sx, sy, swidth, sheight) {
var image = imagesList[id];
if (!image) {
throw "Image not found: " + id;
}
this.drawImage(image, x, y, image.width, image.height,
sx, sy, swidth, sheight);
},
"$drawCanvas": function(id, x, y, sx, sy, swidth, sheight) {
var canvas = canvasList[id];
if (!canvas) {
throw "Canvas not found";
}
if (sheight != null) {
this.drawImage(canvas, x, y, canvas.width, canvas.height,
sx, sy, swidth, sheight);
} else {
this.drawImage(canvas, x, y, canvas.width, canvas.height);
}
},
"$createLinearGradient": function(x0, y0, x1, y1) {
gradient = this.createLinearGradient(x0, y0, x1, y1);
},
"$createPatternFromCanvas": function(patternId, canvasId, kind) {
var canvas = canvasList[canvasId];
if (!canvas) {
throw "Canvas not found";
}
patternList[patternId] = this.createPattern(canvas, kind);
},
"$addColorStop": function(i, rgba) {
gradient.addColorStop(i, rgba);
},
"$fillStyleGradient": function() {
this.fillStyle = gradient;
},
"$fillStylePattern": function(id) {
var pattern = patternList[id];
if (!pattern) {
throw "Pattern not found";
}
this.fillStyle = pattern;
},
"$strokeStyleGradient": function() {
this.strokeStyle = gradient;
},
"$strokeStylePattern": function(id) {
var pattern = patternList[id];
if (!pattern) {
throw "Pattern not found";
}
this.strokeStyle = pattern;
}
}
function renderProxyCanvas(canvas, cmdQueue) {
var ctx = canvas.getContext("2d");
var cmdQueueLength = cmdQueue.length;
for (var i = 0; i < cmdQueueLength; i++) {
var opp = cmdQueue[i];
if (opp[0] == "$") {
ctx[opp[1]] = opp[2];
} else if (opp[0] in ctxSpecial) {
ctxSpecial[opp[0]].apply(ctx, opp[1]);
} else {
ctx[opp[0]].apply(ctx, opp[1]);
}
}
}
/**
* Functions to handle data sent by the WebWorker.
*/
var actionHandler = {
"log": function(data) {
console.log.apply(console, data);
},
"pdf_num_pages": function(data) {
this.numPages = parseInt(data);
if (this.loadCallback) {
this.loadCallback();
}
},
"font": function(data) {
var base64 = window.btoa(data.raw);
// Add the @font-face rule to the document
var url = "url(data:" + data.mimetype + ";base64," + base64 + ");";
var rule = "@font-face { font-family:'" + data.fontName + "';src:" + url + "}";
var styleSheet = document.styleSheets[0];
styleSheet.insertRule(rule, styleSheet.length);
// Just adding the font-face to the DOM doesn't make it load. It
// seems it's loaded once Gecko notices it's used. Therefore,
// add a div on the page using the loaded font.
var div = document.createElement("div");
var style = 'font-family:"' + data.fontName +
'";position: absolute;top:-99999;left:-99999;z-index:-99999';
div.setAttribute("style", style);
document.body.appendChild(div);
},
"jpeg_stream": function(data) {
var img = new Image();
img.src = "data:image/jpeg;base64," + window.btoa(data.raw);
imagesList[data.id] = img;
},
"canvas_proxy_cmd_queue": function(data) {
var id = data.id;
var cmdQueue = data.cmdQueue;
// Check if there is already a canvas with the given id. If not,
// create a new canvas.
if (!canvasList[id]) {
var newCanvas = document.createElement("canvas");
newCanvas.width = data.width;
newCanvas.height = data.height;
canvasList[id] = newCanvas;
}
// There might be fonts that need to get loaded. Shedule the
// rendering at the end of the event queue ensures this.
setTimeout(function() {
if (id == 0) {
console.time("canvas rendering");
var ctx = this.ctx;
ctx.save();
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
}
renderProxyCanvas(canvasList[id], cmdQueue);
if (id == 0) console.timeEnd("canvas rendering")
}, 0, this);
}
}
// List to the WebWorker for data and call actionHandler on it.
this.worker.onmessage = function(event) {
var data = event.data;
if (data.action in actionHandler) {
actionHandler[data.action].call(this, data.data);
} else {
throw "Unkown action from worker: " + data.action;
}
}
}
WorkerPDFDoc.prototype.open = function(url, callback) {
var req = new XMLHttpRequest();
req.open("GET", url);
req.mozResponseType = req.responseType = "arraybuffer";
req.expected = (document.URL.indexOf("file:") == 0) ? 0 : 200;
req.onreadystatechange = function() {
if (req.readyState == 4 && req.status == req.expected) {
var data = req.mozResponseArrayBuffer || req.mozResponse ||
req.responseArrayBuffer || req.response;
this.loadCallback = callback;
this.worker.postMessage(data);
this.showPage(this.numPage);
}
}.bind(this);
req.send(null);
}
WorkerPDFDoc.prototype.showPage = function(numPage) {
this.numPage = parseInt(numPage);
this.worker.postMessage(numPage);
if (this.onChangePage) {
this.onChangePage(numPage);
}
}
WorkerPDFDoc.prototype.nextPage = function() {
if (this.numPage == this.numPages) return;
this.showPage(++this.numPage);
}
WorkerPDFDoc.prototype.prevPage = function() {
if (this.numPage == 1) return;
this.showPage(--this.numPage);
}