Fix
This commit is contained in:
parent
d639a9a94a
commit
9eb7858586
18
worker/boot_processor.js
Normal file
18
worker/boot_processor.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
importScripts('console.js');
|
||||||
|
importScripts('message_handler.js');
|
||||||
|
importScripts('../pdf.js');
|
||||||
|
importScripts('../fonts.js');
|
||||||
|
importScripts('../crypto.js');
|
||||||
|
importScripts('../glyphlist.js');
|
||||||
|
importScripts('handler.js');
|
||||||
|
|
||||||
|
// Listen for messages from the main thread.
|
||||||
|
var pdfDoc = null;
|
||||||
|
|
||||||
|
var handler = new MessageHandler("worker", this);
|
||||||
|
WorkerHandler.setup(handler);
|
252
worker/canvas.js
252
worker/canvas.js
@ -1,252 +0,0 @@
|
|||||||
/* -*- 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',
|
|
||||||
'$setFont'
|
|
||||||
];
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
417
worker/client.js
417
worker/client.js
@ -1,417 +0,0 @@
|
|||||||
/* -*- 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 FontWorker() {
|
|
||||||
this.worker = new Worker('worker/font.js');
|
|
||||||
this.fontsWaiting = 0;
|
|
||||||
this.fontsWaitingCallbacks = [];
|
|
||||||
|
|
||||||
// Listen to the WebWorker for data and call actionHandler on it.
|
|
||||||
this.worker.onmessage = function(event) {
|
|
||||||
var data = event.data;
|
|
||||||
var actionHandler = this.actionHandler;
|
|
||||||
if (data.action in actionHandler) {
|
|
||||||
actionHandler[data.action].call(this, data.data);
|
|
||||||
} else {
|
|
||||||
throw 'Unkown action from worker: ' + data.action;
|
|
||||||
}
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
this.$handleFontLoadedCallback = this.handleFontLoadedCallback.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
FontWorker.prototype = {
|
|
||||||
handleFontLoadedCallback: function() {
|
|
||||||
// Decrease the number of fonts wainting to be loaded.
|
|
||||||
this.fontsWaiting--;
|
|
||||||
// If all fonts are available now, then call all the callbacks.
|
|
||||||
if (this.fontsWaiting == 0) {
|
|
||||||
var callbacks = this.fontsWaitingCallbacks;
|
|
||||||
for (var i = 0; i < callbacks.length; i++) {
|
|
||||||
callbacks[i]();
|
|
||||||
}
|
|
||||||
this.fontsWaitingCallbacks.length = 0;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
actionHandler: {
|
|
||||||
'log': function(data) {
|
|
||||||
console.log.apply(console, data);
|
|
||||||
},
|
|
||||||
|
|
||||||
'fonts': function(data) {
|
|
||||||
// console.log("got processed fonts from worker", Object.keys(data));
|
|
||||||
for (name in data) {
|
|
||||||
// Update the encoding property.
|
|
||||||
var font = Fonts.lookup(name);
|
|
||||||
font.properties = {
|
|
||||||
encoding: data[name].encoding
|
|
||||||
};
|
|
||||||
|
|
||||||
// Call `Font.prototype.bindDOM` to make the font get loaded
|
|
||||||
// on the page.
|
|
||||||
Font.prototype.bindDOM.call(
|
|
||||||
font,
|
|
||||||
data[name].str,
|
|
||||||
// IsLoadedCallback.
|
|
||||||
this.$handleFontLoadedCallback
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
ensureFonts: function(data, callback) {
|
|
||||||
var font;
|
|
||||||
var notLoaded = [];
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
|
||||||
font = data[i];
|
|
||||||
if (Fonts[font.name]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the font but don't pass in any real data. The idea is to
|
|
||||||
// store as less data as possible to reduce memory usage.
|
|
||||||
Fonts.registerFont(font.name, Object.create(null), Object.create(null));
|
|
||||||
|
|
||||||
// Mark this font to be handled later.
|
|
||||||
notLoaded.push(font);
|
|
||||||
// Increate the number of fonts to wait for.
|
|
||||||
this.fontsWaiting++;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.time('ensureFonts');
|
|
||||||
// If there are fonts, that need to get loaded, tell the FontWorker to get
|
|
||||||
// started and push the callback on the waiting-callback-stack.
|
|
||||||
if (notLoaded.length != 0) {
|
|
||||||
console.log('fonts -> FontWorker');
|
|
||||||
// Send the worker the fonts to work on.
|
|
||||||
this.worker.postMessage({
|
|
||||||
action: 'fonts',
|
|
||||||
data: notLoaded
|
|
||||||
});
|
|
||||||
if (callback) {
|
|
||||||
this.fontsWaitingCallbacks.push(callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// All fonts are present? Well, then just call the callback if there is one.
|
|
||||||
else {
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function WorkerPDFDoc(canvas) {
|
|
||||||
var timer = null;
|
|
||||||
|
|
||||||
this.ctx = canvas.getContext('2d');
|
|
||||||
this.canvas = canvas;
|
|
||||||
this.worker = new Worker('worker/pdf.js');
|
|
||||||
this.fontWorker = new FontWorker();
|
|
||||||
this.waitingForFonts = false;
|
|
||||||
this.waitingForFontsCallback = [];
|
|
||||||
|
|
||||||
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) {
|
|
||||||
text = Fonts.charsToUnicode(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;
|
|
||||||
},
|
|
||||||
|
|
||||||
'$setFont': function(name, size) {
|
|
||||||
this.font = size + 'px "' + name + '"';
|
|
||||||
Fonts.setActive(name, size);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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.cssRules.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);
|
|
||||||
},
|
|
||||||
|
|
||||||
'setup_page': function(data) {
|
|
||||||
var size = data.split(',');
|
|
||||||
var canvas = this.canvas, ctx = this.ctx;
|
|
||||||
canvas.width = parseInt(size[0]);
|
|
||||||
canvas.height = parseInt(size[1]);
|
|
||||||
},
|
|
||||||
|
|
||||||
'fonts': function(data) {
|
|
||||||
this.waitingForFonts = true;
|
|
||||||
this.fontWorker.ensureFonts(data, function() {
|
|
||||||
this.waitingForFonts = false;
|
|
||||||
var callbacks = this.waitingForFontsCallback;
|
|
||||||
for (var i = 0; i < callbacks.length; i++) {
|
|
||||||
callbacks[i]();
|
|
||||||
}
|
|
||||||
this.waitingForFontsCallback.length = 0;
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
'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;
|
|
||||||
}
|
|
||||||
|
|
||||||
var renderData = function() {
|
|
||||||
if (id == 0) {
|
|
||||||
console.time('main 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('main canvas rendering');
|
|
||||||
console.timeEnd('>>> total page display time:');
|
|
||||||
}
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
if (this.waitingForFonts) {
|
|
||||||
if (id == 0) {
|
|
||||||
console.log('want to render, but not all fonts are there', id);
|
|
||||||
this.waitingForFontsCallback.push(renderData);
|
|
||||||
} else {
|
|
||||||
// console.log("assume canvas doesn't have fonts", id);
|
|
||||||
renderData();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
renderData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Listen 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;
|
|
||||||
}
|
|
||||||
}.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
console.log('=== start rendering page ' + numPage + ' ===');
|
|
||||||
console.time('>>> total page display time:');
|
|
||||||
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);
|
|
||||||
};
|
|
@ -1,66 +0,0 @@
|
|||||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
importScripts('console.js');
|
|
||||||
|
|
||||||
importScripts('../pdf.js');
|
|
||||||
importScripts('../fonts.js');
|
|
||||||
importScripts('../glyphlist.js');
|
|
||||||
|
|
||||||
function fontDataToString(font) {
|
|
||||||
// Doing postMessage on objects make them lose their "shape". This adds the
|
|
||||||
// "shape" for all required objects agains, such that the encoding works as
|
|
||||||
// expected.
|
|
||||||
var fontFileDict = new Dict();
|
|
||||||
fontFileDict.map = font.file.dict.map;
|
|
||||||
|
|
||||||
var fontFile = new Stream(font.file.bytes, font.file.start,
|
|
||||||
font.file.end - font.file.start, fontFileDict);
|
|
||||||
font.file = new FlateStream(fontFile);
|
|
||||||
|
|
||||||
// This will encode the font.
|
|
||||||
var fontObj = new Font(font.name, font.file, font.properties);
|
|
||||||
|
|
||||||
// Create string that is used for css later.
|
|
||||||
var str = '';
|
|
||||||
var data = fontObj.data;
|
|
||||||
var length = data.length;
|
|
||||||
for (var j = 0; j < length; j++)
|
|
||||||
str += String.fromCharCode(data[j]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
str: str,
|
|
||||||
encoding: font.properties.encoding
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Functions to handle data sent by the MainThread.
|
|
||||||
*/
|
|
||||||
var actionHandler = {
|
|
||||||
'fonts': function(data) {
|
|
||||||
var fontData;
|
|
||||||
var result = {};
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
|
||||||
fontData = data[i];
|
|
||||||
result[fontData.name] = fontDataToString(fontData);
|
|
||||||
}
|
|
||||||
|
|
||||||
postMessage({
|
|
||||||
action: 'fonts',
|
|
||||||
data: result
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Listen to the MainThread for data and call actionHandler on it.
|
|
||||||
this.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;
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
x
Reference in New Issue
Block a user