From d887d2bd29ecc2d89e104b3a814c8b5997151b8c Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Wed, 7 Sep 2011 14:45:38 -0700 Subject: [PATCH] Implement paintReadyJpegXObject + add infrastructure to handle JpegStreams --- pdf.js | 106 +++++++++++++++++++++++++++++++--------------- worker.js | 29 +++++++++++-- worker/handler.js | 7 ++- 3 files changed, 100 insertions(+), 42 deletions(-) diff --git a/pdf.js b/pdf.js index 7af3e6493..805122f9e 100644 --- a/pdf.js +++ b/pdf.js @@ -859,6 +859,31 @@ var PredictorStream = (function() { 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 // the underlying JPEG data for us. var JpegStream = (function() { @@ -891,30 +916,23 @@ var JpegStream = (function() { newBytes.set(embedMarker, 2); return newBytes; } - + function constructor(bytes, dict) { // TODO: per poppler, some images may have "junk" before that // need to be removed this.dict = dict; - + if (isYcckImage(bytes)) bytes = fixYcckImage(bytes); - // create DOM image - 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; + this.src = 'data:image/jpeg;base64,' + window.btoa(bytesToString(bytes)); } constructor.prototype = { - getImage: function() { - return this.domImage; + getIR: function() { + return this.src; }, + getChar: function() { error('internal error: getChar is not valid on JpegStream'); } @@ -4100,6 +4118,8 @@ var EvalState = (function() { var FontsMap = {}; var FontLoadedCounter = 0; +var objIdCounter = 0; + var PartialEvaluator = (function() { function constructor() { this.state = new EvalState(); @@ -4273,25 +4293,27 @@ var PartialEvaluator = (function() { fonts, images, uniquePrefix); var matrix = xobj.dict.get('Matrix'); var bbox = xobj.dict.get('BBox'); - args = [raw, matrix, bbox]; + args = [ raw, matrix, bbox ]; fn = "paintReadyFormXObject"; - } - if (xobj instanceof JpegStream) { - images.bind(xobj); // monitoring image load - // console.log("got xobj that is a JpegStream"); - } - - if (xobj.dict.get('Subtype').name == "Image") { - // Check if we have an image that is not rendered by the platform. - // Needs to be rendered ourself. - if (!(xobj instanceof JpegStream)) { - var image = xobj; - var dict = image.dict; - var w = dict.get('Width', 'W'); - var h = dict.get('Height', 'H'); - + } else if ('Image' == type.name) { + var image = xobj; + var dict = image.dict; + var w = dict.get('Width', 'W'); + var h = dict.get('Height', 'H'); + + if (image instanceof JpegStream) { + var objId = ++objIdCounter; + images.push({ + id: objId, + IR: image.getIR() + }); + + // TODO: Place dependency note in IR queue. + fn = 'paintReadyJpegXObject'; + args = [ objId, w, h ]; + } else { + // Needs to be rendered ourself. var inline = false; - var imageObj = new PDFImage(xref, resources, image, inline); if (imageObj.imageMask) { @@ -4308,12 +4330,10 @@ var PartialEvaluator = (function() { fn = "paintReadyImageXObject"; 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 var fontName = args[0].name; @@ -5533,6 +5553,24 @@ var CanvasGraphics = (function() { 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) { this.save(); diff --git a/worker.js b/worker.js index dae87106c..55c18c8fc 100644 --- a/worker.js +++ b/worker.js @@ -50,6 +50,9 @@ var WorkerPage = (function() { return constructor; })(); +// This holds a list of objects the IR queue depends on. +var Objects = {}; + var WorkerPDFDoc = (function() { function constructor(data) { this.data = data; @@ -60,7 +63,7 @@ var WorkerPDFDoc = (function() { this.pageCache = []; - var useWorker = true; + var useWorker = false; if (useWorker) { var worker = new Worker("../worker/boot.js"); @@ -90,9 +93,27 @@ var WorkerPDFDoc = (function() { font.file.end - font.file.start, fontFileDict); font.file = new FlateStream(fontFile); } - - console.log("startRenderingFromPreCompilation:", "numberOfFonts", fonts.length); - page.startRenderingFromPreCompilation(data.preCompilation, data.fonts, data.images); + + var imageLoadingDone = function() { + 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); if (!useWorker) { diff --git a/worker/handler.js b/worker/handler.js index ca8413ede..4fcb8c1f7 100644 --- a/worker/handler.js +++ b/worker/handler.js @@ -22,8 +22,7 @@ var WorkerHandler = { // but stops at one point and sends the result back to the main thread. var gfx = new CanvasGraphics(null); var fonts = []; - // TODO: Figure out how image loading is handled inside the worker. - var images = new ImagesLoader(); + var images = []; // Pre compile the pdf page and fetch the fonts/images. var preCompilation = page.preCompile(gfx, fonts, images); @@ -76,9 +75,9 @@ var WorkerHandler = { handler.send("page", { pageNum: pageNum, fonts: fontsMin, - images: [], + images: images, preCompilation: preCompilation, }); }, this); } -} \ No newline at end of file +}