249 lines
7.3 KiB
HTML
249 lines
7.3 KiB
HTML
<html>
|
|
<head>
|
|
<title>Simple pdf.js page viewer worker</title>
|
|
<script>
|
|
|
|
var timer = null
|
|
function tic() {
|
|
timer = Date.now();
|
|
}
|
|
|
|
function toc(msg) {
|
|
console.log(msg + ": " + (Date.now() - timer) + "ms");
|
|
}
|
|
|
|
var myWorker = new Worker('worker.js');
|
|
var images = {};
|
|
|
|
var currentX = 0;
|
|
var currentXStack = [];
|
|
var special = {
|
|
"$setCurrentX": function(value) {
|
|
currentX = value;
|
|
},
|
|
|
|
"$addCurrentX": function(value) {
|
|
currentX += value;
|
|
},
|
|
|
|
"$saveCurrentX": function() {
|
|
currentXStack.push(currentX);
|
|
},
|
|
|
|
"$restoreCurrentX": function() {
|
|
currentX = currentXStack.pop();
|
|
},
|
|
|
|
"$showText": function(y, text, uniText) {
|
|
this.translate(currentX, -1 * y);
|
|
this.fillText(uniText, 0, 0);
|
|
currentX += this.measureText(text).width;
|
|
},
|
|
|
|
"$drawCanvas": function(data, x, y, width, height) {
|
|
// Ugly: getImageData is called here only to get an object of the right
|
|
// shape - we are not interessted in the data, as we set it the line
|
|
// afterwards to something custome.
|
|
// Can we do better here?
|
|
var imgData = ctx.getImageData(0, 0, width, height);
|
|
imgData.data = data;
|
|
ctx.putImageData(imgData, x, y);
|
|
},
|
|
|
|
"$drawImage": function(id, x, y, sx, sy, swidth, sheight) {
|
|
var image = images[id];
|
|
if (!image) {
|
|
throw "Image not found";
|
|
}
|
|
ctx.drawImage(image, x, y, image.width, image.height,
|
|
sx, sy, swidth, sheight);
|
|
}
|
|
}
|
|
|
|
var gStack;
|
|
function renderProxyCanvas(stack) {
|
|
for (var i = 0; i < stack.length; i++) {
|
|
// for (var i = 0; i < 1000; i++) {
|
|
var opp = stack[i];
|
|
if (opp[0] == "$") {
|
|
ctx[opp[1]] = opp[2];
|
|
} else if (opp[0] in special) {
|
|
special[opp[0]].apply(ctx, opp[1]);
|
|
} else {
|
|
ctx[opp[0]].apply(ctx, opp[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
const WAIT = 0;
|
|
const CANVAS_PROXY_STACK = 1;
|
|
const LOG = 2;
|
|
const FONT = 3;
|
|
const PDF_NUM_PAGE = 4;
|
|
const JPEG_STREAM = 5;
|
|
|
|
var onMessageState = WAIT;
|
|
var fontStr = null;
|
|
var first = true;
|
|
var intervals = [];
|
|
myWorker.onmessage = function(event) {
|
|
var data = event.data;
|
|
// console.log("onMessageRaw", data);
|
|
switch (onMessageState) {
|
|
case WAIT:
|
|
if (typeof data != "string") {
|
|
throw "expecting to get an string";
|
|
}
|
|
switch (data) {
|
|
case "pdf_num_page":
|
|
onMessageState = PDF_NUM_PAGE;
|
|
return;
|
|
case "log":
|
|
onMessageState = LOG;
|
|
return;
|
|
case "canvas_proxy_stack":
|
|
onMessageState = CANVAS_PROXY_STACK;
|
|
return;
|
|
case "font":
|
|
onMessageState = FONT;
|
|
return;
|
|
case "jpeg_stream":
|
|
onMessageState = JPEG_STREAM;
|
|
return;
|
|
default:
|
|
throw "unkown state: " + data
|
|
}
|
|
break;
|
|
|
|
case JPEG_STREAM:
|
|
var img = new Image();
|
|
img.src = "data:image/jpeg;base64," + window.btoa(data.str);
|
|
images[data.id] = img;
|
|
console.log("got image", data.id)
|
|
break;
|
|
|
|
case PDF_NUM_PAGE:
|
|
console.log(data);
|
|
maxPages = parseInt(data);
|
|
document.getElementById("numPages").innerHTML = "/" + data;
|
|
onMessageState = WAIT;
|
|
break;
|
|
|
|
case FONT:
|
|
data = JSON.parse(data);
|
|
var base64 = window.btoa(data.str);
|
|
|
|
// 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.
|
|
document.getElementById("fonts").innerHTML += "<div style='font-family:" + data.fontName + "'>j</div>";
|
|
console.log("setup font", data.fontName);
|
|
|
|
onMessageState = WAIT;
|
|
break;
|
|
|
|
case LOG:
|
|
console.log.apply(console, JSON.parse(data));
|
|
onMessageState = WAIT;
|
|
break;
|
|
|
|
case CANVAS_PROXY_STACK:
|
|
var stack = data;
|
|
gStack = stack;
|
|
console.log("canvas stack size", stack.length)
|
|
|
|
// There might be fonts that need to get loaded. Shedule the
|
|
// rendering at the end of the event queue ensures this.
|
|
setTimeout(function() {
|
|
tic();
|
|
renderProxyCanvas(stack);
|
|
toc("canvas rendering")
|
|
}, 0);
|
|
onMessageState = WAIT;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// myWorker.postMessage(array);
|
|
|
|
var currentPage = 1;
|
|
var maxPages = 1;
|
|
function showPage(num) {
|
|
ctx.save();
|
|
ctx.fillStyle = "rgb(255, 255, 255)";
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
ctx.restore();
|
|
console.log("worker: page=" + num)
|
|
document.getElementById('pageNumber').value = num;
|
|
currentPage = parseInt(num);
|
|
myWorker.postMessage(num);
|
|
}
|
|
|
|
function open(url) {
|
|
document.title = url;
|
|
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;
|
|
myWorker.postMessage(data);
|
|
showPage("1");
|
|
}
|
|
};
|
|
req.send(null);
|
|
}
|
|
|
|
function nextPage() {
|
|
if (currentPage == maxPages) return;
|
|
currentPage++;
|
|
showPage(currentPage);
|
|
}
|
|
|
|
function prevPage() {
|
|
if (currentPage == 1) return;
|
|
currentPage--;
|
|
showPage(currentPage);
|
|
}
|
|
|
|
window.onload = function() {
|
|
window.canvas = document.getElementById("canvas");
|
|
window.ctx = canvas.getContext("2d");
|
|
open("compressed.tracemonkey-pldi-09.pdf");
|
|
}
|
|
</script>
|
|
<link rel="stylesheet" href="viewer.css"></link>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="fonts" style="position: absolute;display:block;z-index:-1;"></div>
|
|
<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="prevPage();">Previous</button>
|
|
<button onclick="nextPage();">Next</button>
|
|
<input type="text" id="pageNumber" onchange="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>
|
|
|