Implement paintReadyJpegXObject + add infrastructure to handle JpegStreams

This commit is contained in:
Julian Viereck 2011-09-07 14:45:38 -07:00
parent 5bfa9e4f3b
commit d887d2bd29
3 changed files with 100 additions and 42 deletions

106
pdf.js
View File

@ -859,6 +859,31 @@ var PredictorStream = (function() {
return constructor; return constructor;
})(); })();
var JpegStreamIR = (function() {
function JpegStreamIR(objId, IR) {
var src = IR;
// create DOM image
var img = new Image();
img.onload = (function() {
this.loaded = true;
Objects[objId] = this;
if (this.onLoad)
this.onLoad();
}).bind(this);
img.src = src;
this.domImage = img;
}
JpegStreamIR.prototype = {
getImage: function() {
return this.domImage;
}
}
return JpegStreamIR;
})()
// A JpegStream can't be read directly. We use the platform to render // A JpegStream can't be read directly. We use the platform to render
// the underlying JPEG data for us. // the underlying JPEG data for us.
var JpegStream = (function() { var JpegStream = (function() {
@ -891,30 +916,23 @@ var JpegStream = (function() {
newBytes.set(embedMarker, 2); newBytes.set(embedMarker, 2);
return newBytes; return newBytes;
} }
function constructor(bytes, dict) { function constructor(bytes, dict) {
// TODO: per poppler, some images may have "junk" before that // TODO: per poppler, some images may have "junk" before that
// need to be removed // need to be removed
this.dict = dict; this.dict = dict;
if (isYcckImage(bytes)) if (isYcckImage(bytes))
bytes = fixYcckImage(bytes); bytes = fixYcckImage(bytes);
// create DOM image this.src = 'data:image/jpeg;base64,' + window.btoa(bytesToString(bytes));
var img = new Image();
img.onload = (function() {
this.loaded = true;
if (this.onLoad)
this.onLoad();
}).bind(this);
img.src = 'data:image/jpeg;base64,' + window.btoa(bytesToString(bytes));
this.domImage = img;
} }
constructor.prototype = { constructor.prototype = {
getImage: function() { getIR: function() {
return this.domImage; return this.src;
}, },
getChar: function() { getChar: function() {
error('internal error: getChar is not valid on JpegStream'); error('internal error: getChar is not valid on JpegStream');
} }
@ -4100,6 +4118,8 @@ var EvalState = (function() {
var FontsMap = {}; var FontsMap = {};
var FontLoadedCounter = 0; var FontLoadedCounter = 0;
var objIdCounter = 0;
var PartialEvaluator = (function() { var PartialEvaluator = (function() {
function constructor() { function constructor() {
this.state = new EvalState(); this.state = new EvalState();
@ -4273,25 +4293,27 @@ var PartialEvaluator = (function() {
fonts, images, uniquePrefix); fonts, images, uniquePrefix);
var matrix = xobj.dict.get('Matrix'); var matrix = xobj.dict.get('Matrix');
var bbox = xobj.dict.get('BBox'); var bbox = xobj.dict.get('BBox');
args = [raw, matrix, bbox]; args = [ raw, matrix, bbox ];
fn = "paintReadyFormXObject"; fn = "paintReadyFormXObject";
} } else if ('Image' == type.name) {
if (xobj instanceof JpegStream) { var image = xobj;
images.bind(xobj); // monitoring image load var dict = image.dict;
// console.log("got xobj that is a JpegStream"); var w = dict.get('Width', 'W');
} var h = dict.get('Height', 'H');
if (xobj.dict.get('Subtype').name == "Image") { if (image instanceof JpegStream) {
// Check if we have an image that is not rendered by the platform. var objId = ++objIdCounter;
// Needs to be rendered ourself. images.push({
if (!(xobj instanceof JpegStream)) { id: objId,
var image = xobj; IR: image.getIR()
var dict = image.dict; });
var w = dict.get('Width', 'W');
var h = dict.get('Height', 'H'); // TODO: Place dependency note in IR queue.
fn = 'paintReadyJpegXObject';
args = [ objId, w, h ];
} else {
// Needs to be rendered ourself.
var inline = false; var inline = false;
var imageObj = new PDFImage(xref, resources, image, inline); var imageObj = new PDFImage(xref, resources, image, inline);
if (imageObj.imageMask) { if (imageObj.imageMask) {
@ -4308,12 +4330,10 @@ var PartialEvaluator = (function() {
fn = "paintReadyImageXObject"; fn = "paintReadyImageXObject";
args = [ imgData ]; args = [ imgData ];
// console.log("xobj subtype image", w, h, imageObj.imageMask);
} }
} else {
error('Unhandled XObject subtype ' + type.name);
} }
// console.log("xobj subtype", xobj.dict.get('Subtype').name);
} }
} else if (cmd == 'Tf') { // eagerly collect all fonts } else if (cmd == 'Tf') { // eagerly collect all fonts
var fontName = args[0].name; var fontName = args[0].name;
@ -5533,6 +5553,24 @@ var CanvasGraphics = (function() {
this.restore(); this.restore();
}, },
paintReadyJpegXObject: function(objId, w, h) {
var image = Objects[objId];
if (!image) {
error("Dependent image isn't ready yet");
}
this.save();
var ctx = this.ctx;
ctx.scale(1 / w, -1 / h);
var domImage = image.getImage();
ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height,
0, -h, w, h);
this.restore();
},
paintImageXObject: function(ref, image, inline) { paintImageXObject: function(ref, image, inline) {
this.save(); this.save();

View File

@ -50,6 +50,9 @@ var WorkerPage = (function() {
return constructor; return constructor;
})(); })();
// This holds a list of objects the IR queue depends on.
var Objects = {};
var WorkerPDFDoc = (function() { var WorkerPDFDoc = (function() {
function constructor(data) { function constructor(data) {
this.data = data; this.data = data;
@ -60,7 +63,7 @@ var WorkerPDFDoc = (function() {
this.pageCache = []; this.pageCache = [];
var useWorker = true; var useWorker = false;
if (useWorker) { if (useWorker) {
var worker = new Worker("../worker/boot.js"); var worker = new Worker("../worker/boot.js");
@ -90,9 +93,27 @@ var WorkerPDFDoc = (function() {
font.file.end - font.file.start, fontFileDict); font.file.end - font.file.start, fontFileDict);
font.file = new FlateStream(fontFile); font.file = new FlateStream(fontFile);
} }
console.log("startRenderingFromPreCompilation:", "numberOfFonts", fonts.length); var imageLoadingDone = function() {
page.startRenderingFromPreCompilation(data.preCompilation, data.fonts, data.images); console.log("startRenderingFromPreCompilation:", "numberOfFonts", fonts.length);
page.startRenderingFromPreCompilation(data.preCompilation, data.fonts, data.images);
}
var images = data.images;
if (images.length != 0) {
// Generate JpegStreams based on IR information and start rendering
// once the compilation is done.
var loader = new ImagesLoader();
loader.onLoad = imageLoadingDone;
for (var i = 0; i < images.length; i++) {
var image = images[i];
var stream = new JpegStreamIR(image.id, image.IR);
loader.bind(stream);
}
} else {
imageLoadingDone();
}
}, this); }, this);
if (!useWorker) { if (!useWorker) {

View File

@ -22,8 +22,7 @@ var WorkerHandler = {
// but stops at one point and sends the result back to the main thread. // but stops at one point and sends the result back to the main thread.
var gfx = new CanvasGraphics(null); var gfx = new CanvasGraphics(null);
var fonts = []; var fonts = [];
// TODO: Figure out how image loading is handled inside the worker. var images = [];
var images = new ImagesLoader();
// Pre compile the pdf page and fetch the fonts/images. // Pre compile the pdf page and fetch the fonts/images.
var preCompilation = page.preCompile(gfx, fonts, images); var preCompilation = page.preCompile(gfx, fonts, images);
@ -76,9 +75,9 @@ var WorkerHandler = {
handler.send("page", { handler.send("page", {
pageNum: pageNum, pageNum: pageNum,
fonts: fontsMin, fonts: fontsMin,
images: [], images: images,
preCompilation: preCompilation, preCompilation: preCompilation,
}); });
}, this); }, this);
} }
} }