First pass on FontWorker
This commit is contained in:
parent
c712712e67
commit
b00df76044
12
fonts.js
12
fonts.js
@ -64,8 +64,15 @@ var Fonts = {
|
|||||||
var unicode = encoding[charcode];
|
var unicode = encoding[charcode];
|
||||||
|
|
||||||
// Check if the glyph has already been converted
|
// Check if the glyph has already been converted
|
||||||
if (unicode instanceof Name)
|
// if (unicode instanceof Name)
|
||||||
unicode = encoding[unicode] = GlyphsUnicode[unicode.name];
|
try {
|
||||||
|
if (!IsNum(unicode))
|
||||||
|
// if ("name" in unicode)
|
||||||
|
unicode = encoding[unicode] = GlyphsUnicode[unicode.name];
|
||||||
|
|
||||||
|
} catch(e) {
|
||||||
|
console.log("FAIL");
|
||||||
|
}
|
||||||
|
|
||||||
// Handle surrogate pairs
|
// Handle surrogate pairs
|
||||||
if (unicode > 0xFFFF) {
|
if (unicode > 0xFFFF) {
|
||||||
@ -165,6 +172,7 @@ var Font = (function () {
|
|||||||
warn("Font " + properties.type + " is not supported");
|
warn("Font " + properties.type + " is not supported");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
this.data = data;
|
||||||
|
|
||||||
Fonts[name] = {
|
Fonts[name] = {
|
||||||
data: data,
|
data: data,
|
||||||
|
3
pdf.js
3
pdf.js
@ -3819,6 +3819,9 @@ var CanvasGraphics = (function() {
|
|||||||
|
|
||||||
this.current.fontSize = size;
|
this.current.fontSize = size;
|
||||||
this.ctx.font = this.current.fontSize +'px "' + fontName + '", Symbol';
|
this.ctx.font = this.current.fontSize +'px "' + fontName + '", Symbol';
|
||||||
|
if (this.ctx.$setFont) {
|
||||||
|
this.ctx.$setFont(fontName);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
setTextRenderingMode: function(mode) {
|
setTextRenderingMode: function(mode) {
|
||||||
TODO("text rendering mode");
|
TODO("text rendering mode");
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Simple pdf.js page worker viewer</title>
|
<title>Simple pdf.js page worker viewer</title>
|
||||||
|
<script type="text/javascript" src="fonts.js"></script>
|
||||||
|
<script type="text/javascript" src="glyphlist.js"></script>
|
||||||
|
<script type="text/javascript" src="pdf.js"></script>
|
||||||
<script type="text/javascript" src="worker/client.js"></script>
|
<script type="text/javascript" src="worker/client.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
@ -119,7 +119,8 @@ function CanvasProxy(width, height) {
|
|||||||
"$addCurrentX",
|
"$addCurrentX",
|
||||||
"$saveCurrentX",
|
"$saveCurrentX",
|
||||||
"$restoreCurrentX",
|
"$restoreCurrentX",
|
||||||
"$showText"
|
"$showText",
|
||||||
|
"$setFont"
|
||||||
];
|
];
|
||||||
|
|
||||||
function buildFuncCall(name) {
|
function buildFuncCall(name) {
|
||||||
|
136
worker/client.js
136
worker/client.js
@ -18,12 +18,115 @@ if (typeof console.time == "undefined") {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
FontWorker.prototype = {
|
||||||
|
actionHandler: {
|
||||||
|
"fonts": function(data) {
|
||||||
|
// console.log("got processed fonts from worker", Object.keys(data));
|
||||||
|
for (name in data) {
|
||||||
|
var base64 = window.btoa(data[name]);
|
||||||
|
|
||||||
|
// Add the @font-face rule to the document
|
||||||
|
var url = "url(data:font/opentype;base64," + base64 + ");";
|
||||||
|
var rule = "@font-face { font-family:'" + name + "';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:"' + name +
|
||||||
|
'";position: absolute;top:-99999;left:-99999;z-index:-99999';
|
||||||
|
div.setAttribute("style", style);
|
||||||
|
document.body.appendChild(div);
|
||||||
|
this.fontsWaiting --;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This timeout is necessary right now to make sure the fonts are really
|
||||||
|
// loaded at the point the callbacks are called.
|
||||||
|
setTimeout(function() {
|
||||||
|
// 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]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this), 100);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ensureFonts: function(data, callback) {
|
||||||
|
var font;
|
||||||
|
var notLoaded = [];
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
font = data[i];
|
||||||
|
if (Fonts[font.name]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store only the data on Fonts that is needed later on, such that we
|
||||||
|
// hold track on as lease memory as possible.
|
||||||
|
Fonts[font.name] = {
|
||||||
|
properties: {
|
||||||
|
charset: font.properties.charset
|
||||||
|
},
|
||||||
|
cache: Object.create(null)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mark this font to be handled later.
|
||||||
|
notLoaded.push(font);
|
||||||
|
// Increate the number of fonts to wait for.
|
||||||
|
this.fontsWaiting++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
// 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) {
|
function WorkerPDFDoc(canvas) {
|
||||||
var timer = null
|
var timer = null
|
||||||
|
|
||||||
this.ctx = canvas.getContext("2d");
|
this.ctx = canvas.getContext("2d");
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
this.worker = new Worker('worker/pdf.js');
|
this.worker = new Worker('worker/pdf.js');
|
||||||
|
this.fontWorker = new FontWorker();
|
||||||
|
this.waitingForFonts = false;
|
||||||
|
this.waitingForFontsCallback = [];
|
||||||
|
|
||||||
this.numPage = 1;
|
this.numPage = 1;
|
||||||
this.numPages = null;
|
this.numPages = null;
|
||||||
@ -56,6 +159,7 @@ function WorkerPDFDoc(canvas) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
"$showText": function(y, text) {
|
"$showText": function(y, text) {
|
||||||
|
text = Fonts.charsToUnicode(text);
|
||||||
this.translate(currentX, -1 * y);
|
this.translate(currentX, -1 * y);
|
||||||
this.fillText(text, 0, 0);
|
this.fillText(text, 0, 0);
|
||||||
currentX += this.measureText(text).width;
|
currentX += this.measureText(text).width;
|
||||||
@ -136,6 +240,10 @@ function WorkerPDFDoc(canvas) {
|
|||||||
throw "Pattern not found";
|
throw "Pattern not found";
|
||||||
}
|
}
|
||||||
this.strokeStyle = pattern;
|
this.strokeStyle = pattern;
|
||||||
|
},
|
||||||
|
|
||||||
|
"$setFont": function(name) {
|
||||||
|
Fonts.active = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,6 +295,17 @@ function WorkerPDFDoc(canvas) {
|
|||||||
div.setAttribute("style", style);
|
div.setAttribute("style", style);
|
||||||
document.body.appendChild(div);
|
document.body.appendChild(div);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"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]();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
"jpeg_stream": function(data) {
|
"jpeg_stream": function(data) {
|
||||||
var img = new Image();
|
var img = new Image();
|
||||||
@ -207,9 +326,7 @@ function WorkerPDFDoc(canvas) {
|
|||||||
canvasList[id] = newCanvas;
|
canvasList[id] = newCanvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
// There might be fonts that need to get loaded. Shedule the
|
var renderData = function() {
|
||||||
// rendering at the end of the event queue ensures this.
|
|
||||||
setTimeout(function() {
|
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
console.time("canvas rendering");
|
console.time("canvas rendering");
|
||||||
var ctx = this.ctx;
|
var ctx = this.ctx;
|
||||||
@ -220,11 +337,18 @@ function WorkerPDFDoc(canvas) {
|
|||||||
}
|
}
|
||||||
renderProxyCanvas(canvasList[id], cmdQueue);
|
renderProxyCanvas(canvasList[id], cmdQueue);
|
||||||
if (id == 0) console.timeEnd("canvas rendering")
|
if (id == 0) console.timeEnd("canvas rendering")
|
||||||
}, 0, this);
|
}.bind(this);
|
||||||
|
|
||||||
|
if (this.waitingForFonts) {
|
||||||
|
console.log("want to render, but not all fonts are there", id);
|
||||||
|
this.waitingForFontsCallback.push(renderData);
|
||||||
|
} else {
|
||||||
|
renderData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// List to the WebWorker for data and call actionHandler on it.
|
// Listen to the WebWorker for data and call actionHandler on it.
|
||||||
this.worker.onmessage = function(event) {
|
this.worker.onmessage = function(event) {
|
||||||
var data = event.data;
|
var data = event.data;
|
||||||
if (data.action in actionHandler) {
|
if (data.action in actionHandler) {
|
||||||
@ -232,7 +356,7 @@ function WorkerPDFDoc(canvas) {
|
|||||||
} else {
|
} else {
|
||||||
throw "Unkown action from worker: " + data.action;
|
throw "Unkown action from worker: " + data.action;
|
||||||
}
|
}
|
||||||
}
|
}.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkerPDFDoc.prototype.open = function(url, callback) {
|
WorkerPDFDoc.prototype.open = function(url, callback) {
|
||||||
|
27
worker/console.js
Normal file
27
worker/console.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/* -*- 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);
|
||||||
|
}
|
||||||
|
}
|
65
worker/font.js
Normal file
65
worker/font.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/* -*- 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;
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ var console = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
importScripts("console.js")
|
||||||
importScripts("canvas.js");
|
importScripts("canvas.js");
|
||||||
importScripts("../pdf.js");
|
importScripts("../pdf.js");
|
||||||
importScripts("../fonts.js");
|
importScripts("../fonts.js");
|
||||||
@ -65,21 +66,14 @@ onmessage = function(event) {
|
|||||||
page.compile(gfx, fonts);
|
page.compile(gfx, fonts);
|
||||||
console.timeEnd("compile");
|
console.timeEnd("compile");
|
||||||
|
|
||||||
|
// Send fonts to the main thread.
|
||||||
console.time("fonts");
|
console.time("fonts");
|
||||||
// Inspect fonts and translate the missing one.
|
postMessage({
|
||||||
var count = fonts.length;
|
action: "fonts",
|
||||||
for (var i = 0; i < count; i++) {
|
data: fonts
|
||||||
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.timeEnd("fonts");
|
||||||
|
|
||||||
console.time("display");
|
console.time("display");
|
||||||
page.display(gfx);
|
page.display(gfx);
|
||||||
canvas.flush();
|
canvas.flush();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user