+/* -*- 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.
// 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) ) {
// 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;
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 = {};
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;
// 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) {
}
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";
}
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));
// 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));
}
/**
-* 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;
}
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,
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);
}
}
};
}
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() {
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
--- /dev/null
+/* -*- 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;
+function tic() {
+ timer = Date.now();
+}
+
+function toc(msg) {
+ log(msg + ": " + (Date.now() - timer) + "ms");
+ timer = null;
+}
+
+function log() {
+ var args = Array.prototype.slice.call(arguments);
+ postMessage("log");
+ postMessage(JSON.stringify(args))
+}
+
+var console = {
+ log: log
+}
+
+//
+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("pdf_num_page");
+ postMessage(pdfDocument.numPages)
+ return;
+ }
+ // User requested to render a certain page.
+ else {
+ tic();
+
+ // 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);
+
+ // 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);
+ }
+ toc("compiled page");
+
+ tic()
+ page.display(gfx);
+ canvas.flush();
+ toc("displayed page");
+ }
+}
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;
})
+++ /dev/null
-"use strict";
-
-var timer = null;
-function tic() {
- timer = Date.now();
-}
-
-function toc(msg) {
- log(msg + ": " + (Date.now() - timer) + "ms");
- timer = null;
-}
-
-function log() {
- var args = Array.prototype.slice.call(arguments);
- postMessage("log");
- postMessage(JSON.stringify(args))
-}
-
-var console = {
- log: log
-}
-
-//
-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("pdf_num_page");
- postMessage(pdfDocument.numPages)
- return;
- }
- // User requested to render a certain page.
- else {
- tic();
-
- // 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);
-
- // 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);
- }
- toc("compiled page");
-
- tic()
- page.display(gfx);
- canvas.flush();
- toc("displayed page");
- }
-}
+/* -*- 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) {
}
}
- 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) {
* 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;
onMessageState = LOG;
return;
- case "canvas_proxy_stack":
- onMessageState = CANVAS_PROXY_STACK;
+ case "canvas_proxy_cmd_queue":
+ onMessageState = CANVAS_PROXY_CMD_QUEUE;
return;
case "font":
// 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;
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.
// 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;