6 changed files with 749 additions and 40 deletions
@ -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; |
||||||
|
} |
@ -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"); |
||||||
|
} |
||||||
|
} |
@ -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> |
||||||
|
|
@ -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); |
||||||
|
} |
Loading…
Reference in new issue