First pass on review: worker.js -> pdf_worker.js, Font.bind cleanup + other stuff

This commit is contained in:
Julian Viereck 2011-06-23 13:09:36 +02:00
parent 405c367ece
commit 229edf24d4
6 changed files with 77 additions and 123 deletions

View File

@ -1,3 +1,7 @@
/* -*- 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.
@ -29,16 +33,16 @@ var JpegStreamProxy = (function() {
// 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(stack, x0, y0, x1, y1) {
stack.push(["$createLinearGradient", [x0, y0, x1, y1]]);
function GradientProxy(cmdQueue, x0, y0, x1, y1) {
cmdQueue.push(["$createLinearGradient", [x0, y0, x1, y1]]);
this.addColorStop = function(i, rgba) {
stack.push(["$addColorStop", [i, rgba]]);
cmdQueue.push(["$addColorStop", [i, rgba]]);
}
}
// Really simple PatternProxy.
var patternProxyCounter = 0;
function PatternProxy(stack, object, kind) {
function PatternProxy(cmdQueue, object, kind) {
this.id = patternProxyCounter++;
if (!(object instanceof CanvasProxy) ) {
@ -49,7 +53,7 @@ function PatternProxy(stack, object, kind) {
// TODO: Make some kind of dependency management, such that the object
// gets flushed only if needed.
object.flush();
stack.push(["$createPatternFromCanvas", [this.id, object.id, kind]]);
cmdQueue.push(["$createPatternFromCanvas", [this.id, object.id, kind]]);
}
var canvasProxyCounter = 0;
@ -57,7 +61,7 @@ function CanvasProxy(width, height) {
this.id = canvasProxyCounter++;
// The `stack` holds the rendering calls and gets flushed to the main thead.
var stack = this.$stack = [];
var cmdQueue = this.cmdQueue = [];
// Dummy context that gets exposed.
var ctx = {};
@ -119,7 +123,7 @@ function CanvasProxy(width, height) {
function buildFuncCall(name) {
return function() {
// console.log("funcCall", name)
stack.push([name, Array.prototype.slice.call(arguments)]);
cmdQueue.push([name, Array.prototype.slice.call(arguments)]);
}
}
var name;
@ -131,11 +135,11 @@ function CanvasProxy(width, height) {
// Some function calls that need more work.
ctx.createPattern = function(object, kind) {
return new PatternProxy(stack, object, kind);
return new PatternProxy(cmdQueue, object, kind);
}
ctx.createLinearGradient = function(x0, y0, x1, y1) {
return new GradientProxy(stack, x0, y0, x1, y1);
return new GradientProxy(cmdQueue, x0, y0, x1, y1);
}
ctx.getImageData = function(x, y, w, h) {
@ -147,16 +151,16 @@ function CanvasProxy(width, height) {
}
ctx.putImageData = function(data, x, y, width, height) {
stack.push(["$putImageData", [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();
stack.push(["$drawCanvas", [image.id, x, y, sx, sy, swidth, sheight]]);
cmdQueue.push(["$drawCanvas", [image.id, x, y, sx, sy, swidth, sheight]]);
} else if(image instanceof JpegStreamProxy) {
stack.push(["$drawImage", [image.id, x, y, sx, sy, swidth, sheight]])
cmdQueue.push(["$drawImage", [image.id, x, y, sx, sy, swidth, sheight]])
} else {
throw "unkown type to drawImage";
}
@ -192,11 +196,26 @@ function CanvasProxy(width, height) {
function buildSetter(name) {
return function(value) {
stack.push(["$", name, 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));
@ -204,18 +223,6 @@ function CanvasProxy(width, height) {
// Special treatment for `fillStyle` and `strokeStyle`: The passed style
// might be a gradient. Need to check for that.
if (name == "fillStyle" || name == "strokeStyle") {
function buildSetterStyle(name) {
return function(value) {
if (value instanceof GradientProxy) {
stack.push(["$" + name + "Gradient"]);
} else if (value instanceof PatternProxy) {
stack.push(["$" + name + "Pattern", [value.id]]);
} else {
stack.push(["$", name, value]);
return ctx["$" + name] = value;
}
}
}
ctx.__defineSetter__(name, buildSetterStyle(name));
} else {
ctx.__defineSetter__(name, buildSetter(name));
@ -224,16 +231,16 @@ function CanvasProxy(width, height) {
}
/**
* Sends the current stack of the CanvasProxy over to the main thread and
* resets the stack.
* Sends the current cmdQueue of the CanvasProxy over to the main thread and
* resets the cmdQueue.
*/
CanvasProxy.prototype.flush = function() {
postMessage("canvas_proxy_stack");
postMessage("canvas_proxy_cmd_queue");
postMessage({
id: this.id,
stack: this.$stack,
width: this.width,
height: this.height
id: this.id,
cmdQueue: this.cmdQueue,
width: this.width,
height: this.height
});
this.$stack.length = 0;
this.cmdQueue.length = 0;
}

View File

@ -759,88 +759,15 @@ var Font = (function () {
var data = this.font;
var fontName = this.name;
var isWorker = (typeof window == "undefined");
/** Hack begin */
if (!isWorker) {
// 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");
var style = "border: 1px solid black; position:absolute; top: " +
(debug ? (100 * fontCount) : "-200") + "px; left: 2px; width: 340px; height: 100px";
canvas.setAttribute("style", style);
canvas.setAttribute("width", 340);
canvas.setAttribute("heigth", 100);
document.body.appendChild(canvas);
// 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 = " ";
// When debugging use the characters provided by the charsets to visually
// see what's happening instead of 'spaces'
var debug = false;
if (debug) {
var name = document.createElement("font");
name.setAttribute("style", "position: absolute; left: 20px; top: " +
(100 * fontCount + 60) + "px");
name.innerHTML = fontName;
document.body.appendChild(name);
// Retrieve font charset
var charset = Fonts[fontName].properties.charset || [];
// if the charset is too small make it repeat a few times
var count = 30;
while (count-- && charset.length <= 30)
charset = charset.concat(charset.slice());
for (var i = 0; i < charset.length; i++) {
var unicode = GlyphsUnicode[charset[i]];
if (!unicode)
continue;
testString += String.fromCharCode(unicode);
}
ctx.fillText(testString, 20, 20);
}
// Periodicaly check for the width of the testString, it will be
// different once the real font has loaded
var textWidth = ctx.measureText(testString).width;
var interval = window.setInterval(function canvasInterval(self) {
this.start = this.start || Date.now();
ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
// For some reasons the font has not loaded, so mark it loaded for the
// page to proceed but cry
if ((Date.now() - this.start) >= kMaxWaitForFontFace) {
window.clearInterval(interval);
Fonts[fontName].loading = false;
warn("Is " + fontName + " for charset: " + charset + " loaded?");
this.start = 0;
} else if (textWidth != ctx.measureText(testString).width) {
window.clearInterval(interval);
Fonts[fontName].loading = false;
this.start = 0;
}
if (debug)
ctx.fillText(testString, 20, 50);
}, 30, this);
}
/** 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]);
if (isWorker) {
// Insert the font-face css on the page. In a web worker, this needs to
// be forwareded on the main thread.
if (typeof window == "undefined") {
postMessage("font");
postMessage(JSON.stringify({
str: str,
@ -855,6 +782,19 @@ var Font = (function () {
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}";
var styleSheet = document.styleSheets[0];
styleSheet.insertRule(rule, styleSheet.length);
var div = document.createElement("div");
div.innerHTML += "<div style='font-family:" +
fontName +
";'>j</div>";
document.body.appendChild(div);
Fonts[fontName].loading = true;
window.setTimeout(function() {
Fonts[fontName].loading = false;
// Timeout of just `0`, `10` doesn't work here, but for me all values
// above work. Setting value to 50ms.
}, 50);
}
}
};

8
pdf.js
View File

@ -2645,9 +2645,7 @@ var CanvasGraphics = (function() {
}
var fn = Function("objpool", src);
var ret = function (gfx) { fn.call(gfx, objpool); };
ret.src = src;
return ret;
return function (gfx) { fn.call(gfx, objpool); };
},
endDrawing: function() {
@ -3015,8 +3013,8 @@ var CanvasGraphics = (function() {
var botRight = applyMatrix([x0 + xstep, y0 + ystep], matrix);
var tmpCanvas = new this.ScratchCanvas(
Math.ceil(botRight[0] - topLeft[0]), // WIDTH
Math.ceil(botRight[1] - topLeft[1]) // HEIGHT
Math.ceil(botRight[0] - topLeft[0]), // width
Math.ceil(botRight[1] - topLeft[1]) // height
);
// set the new canvas element context as the graphics context

View File

@ -1,3 +1,6 @@
/* -*- 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 timer = null;

View File

@ -14,6 +14,7 @@ window.onload = function() {
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;
})

View File

@ -1,3 +1,6 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
"use strict";
function WorkerPDFDoc(canvas) {
@ -128,10 +131,11 @@ function WorkerPDFDoc(canvas) {
}
}
function renderProxyCanvas(canvas, stack) {
function renderProxyCanvas(canvas, cmdQueue) {
var ctx = canvas.getContext("2d");
for (var i = 0; i < stack.length; i++) {
var opp = stack[i];
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) {
@ -146,7 +150,7 @@ function WorkerPDFDoc(canvas) {
* onMessage state machine.
*/
const WAIT = 0;
const CANVAS_PROXY_STACK = 1;
const CANVAS_PROXY_CMD_QUEUE = 1;
const LOG = 2;
const FONT = 3;
const PDF_NUM_PAGE = 4;
@ -170,8 +174,8 @@ function WorkerPDFDoc(canvas) {
onMessageState = LOG;
return;
case "canvas_proxy_stack":
onMessageState = CANVAS_PROXY_STACK;
case "canvas_proxy_cmd_queue":
onMessageState = CANVAS_PROXY_CMD_QUEUE;
return;
case "font":
@ -215,6 +219,7 @@ function WorkerPDFDoc(canvas) {
// 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");
document.getElementById("fonts").innerHTML += "<div style='font-family:" + data.fontName + "'>j</div>";
onMessageState = WAIT;
@ -225,9 +230,9 @@ function WorkerPDFDoc(canvas) {
onMessageState = WAIT;
break;
case CANVAS_PROXY_STACK:
case CANVAS_PROXY_CMD_QUEUE:
var id = data.id;
var stack = data.stack;
var cmdQueue = data.cmdQueue;
// Check if there is already a canvas with the given id. If not,
// create a new canvas.
@ -242,7 +247,7 @@ function WorkerPDFDoc(canvas) {
// rendering at the end of the event queue ensures this.
setTimeout(function() {
if (id == 0) tic();
renderProxyCanvas(canvasList[id], stack);
renderProxyCanvas(canvasList[id], cmdQueue);
if (id == 0) toc("canvas rendering")
}, 0);
onMessageState = WAIT;