Merge branch 'master' of github.com:andreasgal/pdf.js

This commit is contained in:
Andreas Gal 2011-06-28 14:56:53 -07:00
commit 35f5fe3a16
8 changed files with 308 additions and 57 deletions

View File

@ -64,7 +64,7 @@ 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 (!IsNum(unicode))
unicode = encoding[unicode] = GlyphsUnicode[unicode.name]; unicode = encoding[unicode] = GlyphsUnicode[unicode.name];
// Handle surrogate pairs // Handle surrogate pairs
@ -165,6 +165,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,
@ -779,9 +780,18 @@ var Font = (function () {
}); });
}, },
bindDOM: function font_bindDom(data) { bindDOM: function font_bindDom(data, callback) {
var fontName = this.name; var fontName = this.name;
// 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);
/** Hack begin */ /** Hack begin */
// Actually there is not event when a font has finished downloading so // 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 // the following code are a dirty hack to 'guess' when a font is ready
@ -801,15 +811,19 @@ var Font = (function () {
// For some reasons the font has not loaded, so mark it loaded for the // For some reasons the font has not loaded, so mark it loaded for the
// page to proceed but cry // page to proceed but cry
if ((Date.now() - this.start) >= kMaxWaitForFontFace) { if (textWidth == ctx.measureText(testString).width) {
window.clearInterval(interval); if ((Date.now() - this.start) < kMaxWaitForFontFace) {
Fonts[fontName].loading = false; return;
} else {
warn("Is " + fontName + " loaded?"); warn("Is " + fontName + " loaded?");
this.start = 0; }
} else if (textWidth != ctx.measureText(testString).width) { }
window.clearInterval(interval); window.clearInterval(interval);
Fonts[fontName].loading = false; Fonts[fontName].loading = false;
this.start = 0; this.start = 0;
if (callback) {
callback();
} }
}, 30, this); }, 30, this);

37
pdf.js
View File

@ -816,7 +816,7 @@ var DecryptStream = (function() {
DecodeStream.call(this); DecodeStream.call(this);
} }
const chunkSize = 512; var chunkSize = 512;
constructor.prototype = Object.create(DecodeStream.prototype); constructor.prototype = Object.create(DecodeStream.prototype);
constructor.prototype.readBlock = function() { constructor.prototype.readBlock = function() {
@ -910,18 +910,18 @@ var Ascii85Stream = (function() {
var CCITTFaxStream = (function() { var CCITTFaxStream = (function() {
const ccittEOL = -2; var ccittEOL = -2;
const twoDimPass = 0; var twoDimPass = 0;
const twoDimHoriz = 1; var twoDimHoriz = 1;
const twoDimVert0 = 2; var twoDimVert0 = 2;
const twoDimVertR1 = 3; var twoDimVertR1 = 3;
const twoDimVertL1 = 4; var twoDimVertL1 = 4;
const twoDimVertR2 = 5; var twoDimVertR2 = 5;
const twoDimVertL2 = 6; var twoDimVertL2 = 6;
const twoDimVertR3 = 7; var twoDimVertR3 = 7;
const twoDimVertL3 = 8; var twoDimVertL3 = 8;
const twoDimTable = [ var twoDimTable = [
[-1, -1], [-1, -1], // 000000x [-1, -1], [-1, -1], // 000000x
[7, twoDimVertL3], // 0000010 [7, twoDimVertL3], // 0000010
[7, twoDimVertR3], // 0000011 [7, twoDimVertR3], // 0000011
@ -989,7 +989,7 @@ var CCITTFaxStream = (function() {
[1, twoDimVert0], [1, twoDimVert0] [1, twoDimVert0], [1, twoDimVert0]
]; ];
const whiteTable1 = [ var whiteTable1 = [
[-1, -1], // 00000 [-1, -1], // 00000
[12, ccittEOL], // 00001 [12, ccittEOL], // 00001
[-1, -1], [-1, -1], // 0001x [-1, -1], [-1, -1], // 0001x
@ -1011,7 +1011,7 @@ var CCITTFaxStream = (function() {
[12, 2560] // 11111 [12, 2560] // 11111
]; ];
const whiteTable2 = [ var whiteTable2 = [
[-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000000xx [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000000xx
[8, 29], [8, 29], // 00000010x [8, 29], [8, 29], // 00000010x
[8, 30], [8, 30], // 00000011x [8, 30], [8, 30], // 00000011x
@ -1175,7 +1175,7 @@ var CCITTFaxStream = (function() {
[4, 7], [4, 7], [4, 7], [4, 7] [4, 7], [4, 7], [4, 7], [4, 7]
]; ];
const blackTable1 = [ var blackTable1 = [
[-1, -1], [-1, -1], // 000000000000x [-1, -1], [-1, -1], // 000000000000x
[12, ccittEOL], [12, ccittEOL], // 000000000001x [12, ccittEOL], [12, ccittEOL], // 000000000001x
[-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000001xx [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000001xx
@ -1236,7 +1236,7 @@ var CCITTFaxStream = (function() {
[10, 64], [10, 64], [10, 64], [10, 64] [10, 64], [10, 64], [10, 64], [10, 64]
]; ];
const blackTable2 = [ var blackTable2 = [
[8, 13], [8, 13], [8, 13], [8, 13], // 00000100xxxx [8, 13], [8, 13], [8, 13], [8, 13], // 00000100xxxx
[8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13],
[8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13],
@ -1315,7 +1315,7 @@ var CCITTFaxStream = (function() {
[7, 12], [7, 12], [7, 12], [7, 12] [7, 12], [7, 12], [7, 12], [7, 12]
]; ];
const blackTable3 = [ var blackTable3 = [
[-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000xx [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000xx
[6, 9], // 000100 [6, 9], // 000100
[6, 8], // 000101 [6, 8], // 000101
@ -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");

View File

@ -1,7 +1,10 @@
<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="worker_client.js"></script> <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> <script>

View File

@ -119,7 +119,8 @@ function CanvasProxy(width, height) {
"$addCurrentX", "$addCurrentX",
"$saveCurrentX", "$saveCurrentX",
"$restoreCurrentX", "$restoreCurrentX",
"$showText" "$showText",
"$setFont"
]; ];
function buildFuncCall(name) { function buildFuncCall(name) {

View File

@ -18,12 +18,124 @@ 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);
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
Fonts[name].properties = {
encoding: data[name].encoding
}
// Call `Font.prototype.bindDOM` to make the font get loaded on the page.
Font.prototype.bindDOM.call(
Fonts[name],
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;
}
// 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] = {
name: font.name,
mimetype: font.mimetype,
// This is set later on the worker replay. For some fonts, the encoding
// is calculated during the conversion process happening on the worker
// and therefore is not available right now.
// properties: {
// encoding: font.properties.encoding
// },
cache: 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) { 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('pdf_worker.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 +168,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 +249,10 @@ function WorkerPDFDoc(canvas) {
throw "Pattern not found"; throw "Pattern not found";
} }
this.strokeStyle = pattern; this.strokeStyle = pattern;
},
"$setFont": function(name) {
Fonts.active = name;
} }
} }
@ -188,6 +305,18 @@ function WorkerPDFDoc(canvas) {
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]();
}
this.waitingForFontsCallback.length = 0;
}.bind(this));
},
"jpeg_stream": function(data) { "jpeg_stream": function(data) {
var img = new Image(); var img = new Image();
img.src = "data:image/jpeg;base64," + window.btoa(data.raw); img.src = "data:image/jpeg;base64," + window.btoa(data.raw);
@ -207,11 +336,9 @@ 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("main canvas rendering");
var ctx = this.ctx; var ctx = this.ctx;
ctx.save(); ctx.save();
ctx.fillStyle = "rgb(255, 255, 255)"; ctx.fillStyle = "rgb(255, 255, 255)";
@ -219,12 +346,27 @@ function WorkerPDFDoc(canvas) {
ctx.restore(); ctx.restore();
} }
renderProxyCanvas(canvasList[id], cmdQueue); renderProxyCanvas(canvasList[id], cmdQueue);
if (id == 0) console.timeEnd("canvas rendering") if (id == 0) {
}, 0, this); 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();
}
} }
} }
// 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 +374,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) {
@ -255,6 +397,8 @@ WorkerPDFDoc.prototype.open = function(url, callback) {
WorkerPDFDoc.prototype.showPage = function(numPage) { WorkerPDFDoc.prototype.showPage = function(numPage) {
this.numPage = parseInt(numPage); this.numPage = parseInt(numPage);
console.log("=== start rendering page " + numPage + " ===");
console.time(">>> total page display time:");
this.worker.postMessage(numPage); this.worker.postMessage(numPage);
if (this.onChangePage) { if (this.onChangePage) {
this.onChangePage(numPage); this.onChangePage(numPage);

27
worker/console.js Normal file
View 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
View 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;
}
}

View File

@ -27,10 +27,11 @@ var console = {
} }
// //
importScripts("canvas_proxy.js"); importScripts("console.js")
importScripts("pdf.js"); importScripts("canvas.js");
importScripts("fonts.js"); importScripts("../pdf.js");
importScripts("glyphlist.js") importScripts("../fonts.js");
importScripts("../glyphlist.js")
// Use the JpegStreamProxy proxy. // Use the JpegStreamProxy proxy.
JpegStream = JpegStreamProxy; JpegStream = JpegStreamProxy;
@ -65,19 +66,12 @@ 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");