From 2a632d4ab257d1c0eeed8384a7d8c6af2d14dcc9 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Wed, 7 Dec 2011 15:36:27 -0800 Subject: [PATCH 01/13] First stage of trying to support smasks on native jpegs. --- src/canvas.js | 26 +++++++++++++++++++++---- src/core.js | 35 ++++++++++++++++++++++++++++----- src/evaluator.js | 17 ++++++++-------- src/image.js | 27 -------------------------- src/stream.js | 2 ++ src/worker.js | 50 ++++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 109 insertions(+), 48 deletions(-) diff --git a/src/canvas.js b/src/canvas.js index 6007d0031..06c3e6557 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -1038,21 +1038,24 @@ var CanvasGraphics = (function canvasGraphics() { paintJpegXObject: function canvasGraphicsPaintJpegXObject(objId, w, h) { var image = this.objs.get(objId); + debugger; if (!image) { error('Dependent image isn\'t ready yet'); } + + this.paintNormalImageXObject(image); + /* this.save(); var ctx = this.ctx; // scale the image to the unit square ctx.scale(1 / w, -1 / h); - var domImage = image.getImage(); - ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height, + ctx.drawImage(image.data, 0, 0, image.width, image.height, 0, -h, w, h); - this.restore(); + this.restore(); */ }, paintImageMaskXObject: function canvasGraphicsPaintImageMaskXObject( @@ -1104,7 +1107,7 @@ var CanvasGraphics = (function canvasGraphics() { this.restore(); }, - paintImageXObject: function canvasGraphicsPaintImageXObject(imgData) { + paintNormalImageXObject: function canvasGraphicsPaintImageXObject(imgData) { this.save(); var ctx = this.ctx; var w = imgData.width; @@ -1134,6 +1137,21 @@ var CanvasGraphics = (function canvasGraphics() { this.restore(); }, + paintImageXObject: function canvasGraphicsPaintImageXObject(type, data) { + debugger; + switch(type) { + case 'jpeg': + this.paintJpegXObject.apply(this, data); + break; + case 'imageMask': + this.paintImageMaskXObject.apply(this, data); + break; + default: + this.paintNormalImageXObject.apply(this, data); + break; + } + }, + // Marked content markPoint: function canvasGraphicsMarkPoint(tag) { diff --git a/src/core.js b/src/core.js index 284b3cb40..ea9589505 100644 --- a/src/core.js +++ b/src/core.js @@ -6,7 +6,7 @@ var globalScope = (typeof window === 'undefined') ? this : window; var ERRORS = 0, WARNINGS = 1, TODOS = 5; -var verbosity = WARNINGS; +var verbosity = TODOS; // The global PDFJS object exposes the API // In production, it will be declared outside a global wrapper @@ -14,7 +14,7 @@ var verbosity = WARNINGS; if (!globalScope.PDFJS) { globalScope.PDFJS = {}; } - +PDFJS.disableWorker = true; // getPdf() // Convenience function to perform binary Ajax GET // Usage: getPdf('http://...', callback) @@ -440,6 +440,7 @@ var PDFDocModel = (function pdfDoc() { this.startXRef, this.mainXRefEntriesOffset); this.catalog = new Catalog(this.xref); + this.objs = new PDFObjects(); }, get numPages() { var linearization = this.linearization; @@ -559,9 +560,9 @@ var PDFDoc = (function pdfDoc() { var type = data[1]; switch (type) { - case 'JpegStream': - var IR = data[2]; - new JpegImageLoader(id, IR, this.objs); + case 'Jpeg': + var imageData = data[2]; + this.objs.resolve(id, imageData); break; case 'Font': var name = data[2]; @@ -607,6 +608,30 @@ var PDFDoc = (function pdfDoc() { throw data.error; }, this); + messageHandler.on('jpeg_decode', function(message) { + var imageData = message.data[0]; + var img = new Image(); + img.onload = (function jpegImageLoaderOnload() { + var width = img.width; + var height = img.height; + var length = width * height * 4; + var buf = new Uint8Array(length); + var tempCanvas = new ScratchCanvas(width, height); + var tempCtx = tempCanvas.getContext('2d'); + tempCtx.drawImage(img, 0, 0); + var data = tempCtx.getImageData(0, 0, width, height).data; + for (var i = 0; i < length; i += 4) { + buf[i] = data[i]; + buf[i + 1] = data[i + 1]; + buf[i + 2] = data[i + 2]; + buf[i + 3] = data[i + 3]; + } + message.resolve({ data: buf, width: width, height: height}); + }).bind(this); + var src = 'data:image/jpeg;base64,' + window.btoa(imageData); + img.src = src; + }); + setTimeout(function pdfDocFontReadySetTimeout() { messageHandler.send('doc', this.data); this.workerReadyPromise.resolve(true); diff --git a/src/evaluator.js b/src/evaluator.js index 954c3bec3..224d823a4 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -179,21 +179,24 @@ var PartialEvaluator = (function partialEvaluator() { return loadedName; } - function buildPaintImageXObject(image, inline) { + function buildPaintImageXObject(image, inline) { var dict = image.dict; var w = dict.get('Width', 'W'); var h = dict.get('Height', 'H'); + fn = 'paintImageXObject'; if (image instanceof JpegStream && image.isNative) { var objId = 'img_' + uniquePrefix + (++self.objIdCounter); - handler.send('obj', [objId, 'JpegStream', image.getIR()]); + debugger; + handler.send('jpeg_decode', [image.getIR()], function(data) { + handler.send('obj', [objId, 'Jpeg', data]); + }); // Add the dependency on the image object. insertDependency([objId]); // The normal fn. - fn = 'paintJpegXObject'; - args = [objId, w, h]; + args = ['jpeg', [objId, w, h]]; return; } @@ -220,8 +223,7 @@ var PartialEvaluator = (function partialEvaluator() { var pixels = imgData.data; imageObj.fillRgbaBuffer(pixels, imageObj.decode); - fn = 'paintImageXObject'; - args = [imgData]; + args = ['normal', [imgData]]; return; } @@ -230,7 +232,6 @@ var PartialEvaluator = (function partialEvaluator() { // data can't be done here. Instead of creating a // complete PDFImage, only read the information needed // for later. - fn = 'paintImageMaskXObject'; var width = dict.get('Width', 'W'); var height = dict.get('Height', 'H'); @@ -239,7 +240,7 @@ var PartialEvaluator = (function partialEvaluator() { var decode = dict.get('Decode', 'D'); var inverseDecode = !!decode && decode[0] > 0; - args = [imgArray, inverseDecode, width, height]; + args = ['imageMask', [imgArray, inverseDecode, width, height]]; } uniquePrefix = uniquePrefix || ''; diff --git a/src/image.js b/src/image.js index 17ef7b06d..dbb62c99f 100644 --- a/src/image.js +++ b/src/image.js @@ -228,30 +228,3 @@ var PDFImage = (function pdfImage() { }; return constructor; })(); - -var JpegImageLoader = (function jpegImage() { - function JpegImageLoader(objId, imageData, objs) { - var src = 'data:image/jpeg;base64,' + window.btoa(imageData); - - var img = new Image(); - img.onload = (function jpegImageLoaderOnload() { - this.loaded = true; - - objs.resolve(objId, this); - - if (this.onLoad) - this.onLoad(); - }).bind(this); - img.src = src; - this.domImage = img; - } - - JpegImageLoader.prototype = { - getImage: function jpegImageLoaderGetImage() { - return this.domImage; - } - }; - - return JpegImageLoader; -})(); - diff --git a/src/stream.js b/src/stream.js index 559fb2ca2..e4732d87a 100644 --- a/src/stream.js +++ b/src/stream.js @@ -804,6 +804,8 @@ var JpegStream = (function jpegStream() { this.colorTransform = -1; + this.bytes = bytes; + if (isAdobeImage(bytes)) { // when bug 674619 land, let's check if browser can do // normal cmyk and then we won't have to the following diff --git a/src/worker.js b/src/worker.js index 8e4c14fbc..ea314744f 100644 --- a/src/worker.js +++ b/src/worker.js @@ -6,6 +6,8 @@ function MessageHandler(name, comObj) { this.name = name; this.comObj = comObj; + this.callbackIndex = 1; + this.callbacks = {}; var ah = this.actionHandler = {}; ah['console_log'] = [function ahConsoleLog(data) { @@ -14,11 +16,38 @@ function MessageHandler(name, comObj) { ah['console_error'] = [function ahConsoleError(data) { console.error.apply(console, data); }]; + ah['__resolve__'] = [ function(data) { + var callbackId = data.callbackId; + if (data.callbackId in this.callbacks) { + var callback = this.callbacks[callbackId]; + delete this.callbacks[callbackId]; + callback(data.data); + } else { + throw 'Cannot resolve callback ' + callbackId; + } + }, this]; + comObj.onmessage = function messageHandlerComObjOnMessage(event) { var data = event.data; if (data.action in ah) { var action = ah[data.action]; - action[0].call(action[1], data.data); + if (data.callbackId) { + action[0].call(action[1], { + data: data.data, + resolve: (function(callbackId) { + return function(resolvedData) { + comObj.postMessage({ + action: '__resolve__', + data: { + data: resolvedData, + callbackId: data.callbackId + } + }); + }})(data.callbackId) + }); + } else { + action[0].call(action[1], data.data); + } } else { throw 'Unkown action from worker: ' + data.action; } @@ -34,11 +63,17 @@ MessageHandler.prototype = { ah[actionName] = [handler, scope]; }, - send: function messageHandlerSend(actionName, data) { - this.comObj.postMessage({ + send: function messageHandlerSend(actionName, data, callback) { + var message = { action: actionName, data: data - }); + }; + if (callback) { + var callbackId = this.callbackIndex++; + this.callbacks[callbackId] = callback; + message.callbackId = callbackId; + } + this.comObj.postMessage(message); } }; @@ -160,6 +195,13 @@ var WorkerMessageHandler = { handler.send('font_ready', [objId, obj]); }); + + handler.on('jpeg_decoded', function jpegDecoded(data) { + var objId = data[0]; + var imageData = data[1]; + console.log('worker recieved decoded jpeg'); + debugger; + }, this); } }; From 146cd8c8617d254a533fe6967a1cbaa303c38a5a Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Thu, 8 Dec 2011 12:50:34 -0800 Subject: [PATCH 02/13] Second stage, smasks now working. Still needs optimization. --- src/canvas.js | 43 ++++-------------------------- src/core.js | 27 +++++++++++++------ src/evaluator.js | 69 ++++++++++++++++++++---------------------------- src/image.js | 50 +++++++++++++++++++++++++---------- 4 files changed, 88 insertions(+), 101 deletions(-) diff --git a/src/canvas.js b/src/canvas.js index 06c3e6557..128f4fbc9 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -1036,28 +1036,6 @@ var CanvasGraphics = (function canvasGraphics() { this.restore(); }, - paintJpegXObject: function canvasGraphicsPaintJpegXObject(objId, w, h) { - var image = this.objs.get(objId); - debugger; - if (!image) { - error('Dependent image isn\'t ready yet'); - } - - - this.paintNormalImageXObject(image); - /* - this.save(); - - var ctx = this.ctx; - // scale the image to the unit square - ctx.scale(1 / w, -1 / h); - - ctx.drawImage(image.data, 0, 0, image.width, image.height, - 0, -h, w, h); - - this.restore(); */ - }, - paintImageMaskXObject: function canvasGraphicsPaintImageMaskXObject( imgArray, inverseDecode, width, height) { function applyStencilMask(buffer, inverseDecode) { @@ -1107,7 +1085,11 @@ var CanvasGraphics = (function canvasGraphics() { this.restore(); }, - paintNormalImageXObject: function canvasGraphicsPaintImageXObject(imgData) { + paintImageXObject: function canvasGraphicsPaintImageXObject(objId) { + var imgData = this.objs.get(objId); + if (!imgData) { + error('Dependent image isn\'t ready yet'); + } this.save(); var ctx = this.ctx; var w = imgData.width; @@ -1137,21 +1119,6 @@ var CanvasGraphics = (function canvasGraphics() { this.restore(); }, - paintImageXObject: function canvasGraphicsPaintImageXObject(type, data) { - debugger; - switch(type) { - case 'jpeg': - this.paintJpegXObject.apply(this, data); - break; - case 'imageMask': - this.paintImageMaskXObject.apply(this, data); - break; - default: - this.paintNormalImageXObject.apply(this, data); - break; - } - }, - // Marked content markPoint: function canvasGraphicsMarkPoint(tag) { diff --git a/src/core.js b/src/core.js index ea9589505..8063841f2 100644 --- a/src/core.js +++ b/src/core.js @@ -560,7 +560,7 @@ var PDFDoc = (function pdfDoc() { var type = data[1]; switch (type) { - case 'Jpeg': + case 'Image': var imageData = data[2]; this.objs.resolve(id, imageData); break; @@ -610,21 +610,32 @@ var PDFDoc = (function pdfDoc() { messageHandler.on('jpeg_decode', function(message) { var imageData = message.data[0]; + var components = message.data[1]; + if (components != 3 && components != 1) + error('Only 3 component or 1 component can be returned'); + var img = new Image(); img.onload = (function jpegImageLoaderOnload() { var width = img.width; var height = img.height; - var length = width * height * 4; - var buf = new Uint8Array(length); + var size = width * height; + var rgbaLength = size * 4; + var buf = new Uint8Array(size * components); var tempCanvas = new ScratchCanvas(width, height); var tempCtx = tempCanvas.getContext('2d'); tempCtx.drawImage(img, 0, 0); var data = tempCtx.getImageData(0, 0, width, height).data; - for (var i = 0; i < length; i += 4) { - buf[i] = data[i]; - buf[i + 1] = data[i + 1]; - buf[i + 2] = data[i + 2]; - buf[i + 3] = data[i + 3]; + + if (components == 3) { + for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) { + buf[j] = data[i]; + buf[j + 1] = data[i + 1]; + buf[j + 2] = data[i + 2]; + } + } else if(components == 1) { + for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) { + buf[j] = data[i]; + } } message.resolve({ data: buf, width: width, height: height}); }).bind(this); diff --git a/src/evaluator.js b/src/evaluator.js index 224d823a4..a53d15c50 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -183,38 +183,42 @@ var PartialEvaluator = (function partialEvaluator() { var dict = image.dict; var w = dict.get('Width', 'W'); var h = dict.get('Height', 'H'); - fn = 'paintImageXObject'; - if (image instanceof JpegStream && image.isNative) { - var objId = 'img_' + uniquePrefix + (++self.objIdCounter); - debugger; - handler.send('jpeg_decode', [image.getIR()], function(data) { - handler.send('obj', [objId, 'Jpeg', data]); - }); + var imageMask = dict.get('ImageMask', 'IM') || false; + if (imageMask) { + // This depends on a tmpCanvas beeing filled with the + // current fillStyle, such that processing the pixel + // data can't be done here. Instead of creating a + // complete PDFImage, only read the information needed + // for later. - // Add the dependency on the image object. - insertDependency([objId]); - - // The normal fn. - args = ['jpeg', [objId, w, h]]; + var width = dict.get('Width', 'W'); + var height = dict.get('Height', 'H'); + var bitStrideLength = (width + 7) >> 3; + var imgArray = image.getBytes(bitStrideLength * height); + var decode = dict.get('Decode', 'D'); + var inverseDecode = !!decode && decode[0] > 0; + fn = 'paintImageMaskXObject'; + args = [imgArray, inverseDecode, width, height]; return; } - // Needs to be rendered ourself. - - // Figure out if the image has an imageMask. - var imageMask = dict.get('ImageMask', 'IM') || false; - // If there is no imageMask, create the PDFImage and a lot // of image processing can be done here. - if (!imageMask) { - var imageObj = new PDFImage(xref, resources, image, inline); + var objId = 'img_' + uniquePrefix + (++self.objIdCounter); + insertDependency([objId]); + fn = 'paintImageXObject'; + args = [objId, w, h]; + var resolve = (function(objId) { + return function resolve(data) { + handler.send('obj', [objId, 'Image', data]); + }; + })(objId); - if (imageObj.imageMask) { - throw 'Can\'t handle this in the web worker :/'; - } + var imageObj = new PDFImage(xref, resources, image, inline, handler); + imageObj.ready(function() { var imgData = { width: w, height: h, @@ -222,25 +226,8 @@ var PartialEvaluator = (function partialEvaluator() { }; var pixels = imgData.data; imageObj.fillRgbaBuffer(pixels, imageObj.decode); - - args = ['normal', [imgData]]; - return; - } - - // This depends on a tmpCanvas beeing filled with the - // current fillStyle, such that processing the pixel - // data can't be done here. Instead of creating a - // complete PDFImage, only read the information needed - // for later. - - var width = dict.get('Width', 'W'); - var height = dict.get('Height', 'H'); - var bitStrideLength = (width + 7) >> 3; - var imgArray = image.getBytes(bitStrideLength * height); - var decode = dict.get('Decode', 'D'); - var inverseDecode = !!decode && decode[0] > 0; - - args = ['imageMask', [imgArray, inverseDecode, width, height]]; + resolve(imgData); + }); } uniquePrefix = uniquePrefix || ''; diff --git a/src/image.js b/src/image.js index dbb62c99f..adae24292 100644 --- a/src/image.js +++ b/src/image.js @@ -4,8 +4,12 @@ 'use strict'; var PDFImage = (function pdfImage() { - function constructor(xref, res, image, inline) { + function constructor(xref, res, image, inline, handler) { this.image = image; + this.imageReady = true; + this.smaskReady = true; + this.callbacks = []; + if (image.getParams) { // JPX/JPEG2000 streams directly contain bits per component // and color space mode information. @@ -56,7 +60,24 @@ var PDFImage = (function pdfImage() { if (mask) { TODO('masked images'); } else if (smask) { - this.smask = new PDFImage(xref, res, smask); + this.smaskReady = false; + this.smask = new PDFImage(xref, res, smask, false, handler); + this.smask.ready(function() { + this.smaskReady = true; + if (this.isReady()) + this.fireReady(); + }.bind(this)); + } + + if (image instanceof JpegStream && image.isNative) { + this.imageReady = false; + handler.send('jpeg_decode', [image.getIR(), this.numComps], function(message) { + var data = message.data; + this.image = new Stream(data, 0, data.length); + this.imageReady = true; + if (this.isReady()) + this.fireReady(); + }.bind(this)); } } @@ -130,18 +151,6 @@ var PDFImage = (function pdfImage() { var buf = new Uint8Array(width * height); if (smask) { - if (smask.image.getImage) { - // smask is a DOM image - var tempCanvas = new ScratchCanvas(width, height); - var tempCtx = tempCanvas.getContext('2d'); - var domImage = smask.image.getImage(); - tempCtx.drawImage(domImage, 0, 0, domImage.width, domImage.height, - 0, 0, width, height); - var data = tempCtx.getImageData(0, 0, width, height).data; - for (var i = 0, j = 0, ii = width * height; i < ii; ++i, j += 4) - buf[i] = data[j]; // getting first component value - return buf; - } var sw = smask.width; var sh = smask.height; if (sw != this.width || sh != this.height) @@ -224,6 +233,19 @@ var PDFImage = (function pdfImage() { for (var i = 0; i < length; ++i) buffer[i] = comps[i]; + }, + isReady: function isReady() { + return this.imageReady && this.smaskReady; + }, + fireReady: function fireReady() { + for (var i = 0; i < this.callbacks.length; ++i) { + this.callbacks[i](); + this.callbacks = []; + }, + ready: function ready(callback) { + this.callbacks.push(callback); + if (this.isReady()) + this.fireReady(); } }; return constructor; From f7207a51f89761113c8c07dcb48f6bfde7a6f057 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Thu, 8 Dec 2011 12:54:02 -0800 Subject: [PATCH 03/13] Fix missing bracket. --- src/image.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/image.js b/src/image.js index adae24292..c1eaf1c8f 100644 --- a/src/image.js +++ b/src/image.js @@ -238,7 +238,7 @@ var PDFImage = (function pdfImage() { return this.imageReady && this.smaskReady; }, fireReady: function fireReady() { - for (var i = 0; i < this.callbacks.length; ++i) { + for (var i = 0; i < this.callbacks.length; ++i) this.callbacks[i](); this.callbacks = []; }, From d1c823efcec0abe1e43dbc571242250db486b297 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Thu, 8 Dec 2011 14:51:26 -0800 Subject: [PATCH 04/13] Cleaned up how messages are passed and replies work. --- src/core.js | 17 +++++++---- src/worker.js | 84 ++++++++++++++++++++++++++++----------------------- 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/src/core.js b/src/core.js index 8063841f2..29bc56e32 100644 --- a/src/core.js +++ b/src/core.js @@ -509,7 +509,8 @@ var PDFDoc = (function pdfDoc() { // Tell the worker the file it was created from. messageHandler.send('workerSrc', workerSrc); - messageHandler.on('test', function pdfDocTest(supportTypedArray) { + messageHandler.on('test', function pdfDocTest(message) { + var supportTypedArray = message.data; if (supportTypedArray) { this.worker = worker; this.setupMessageHandler(messageHandler); @@ -547,7 +548,8 @@ var PDFDoc = (function pdfDoc() { setupMessageHandler: function(messageHandler) { this.messageHandler = messageHandler; - messageHandler.on('page', function pdfDocPage(data) { + messageHandler.on('page', function pdfDocPage(message) { + var data = message.data; var pageNum = data.pageNum; var page = this.pageCache[pageNum]; var depFonts = data.depFonts; @@ -555,7 +557,8 @@ var PDFDoc = (function pdfDoc() { page.startRenderingFromIRQueue(data.IRQueue, depFonts); }, this); - messageHandler.on('obj', function pdfDocObj(data) { + messageHandler.on('obj', function pdfDocObj(message) { + var data = message.data; var id = data[0]; var type = data[1]; @@ -588,7 +591,8 @@ var PDFDoc = (function pdfDoc() { } }, this); - messageHandler.on('font_ready', function pdfDocFontReady(data) { + messageHandler.on('font_ready', function pdfDocFontReady(message) { + var data = message.data; var id = data[0]; var font = new FontShape(data[1]); @@ -600,7 +604,8 @@ var PDFDoc = (function pdfDoc() { } }.bind(this)); - messageHandler.on('page_error', function pdfDocError(data) { + messageHandler.on('page_error', function pdfDocError(message) { + var data = message.data; var page = this.pageCache[data.pageNum]; if (page.callback) page.callback(data.error); @@ -637,7 +642,7 @@ var PDFDoc = (function pdfDoc() { buf[j] = data[i]; } } - message.resolve({ data: buf, width: width, height: height}); + message.reply({ data: buf, width: width, height: height}); }).bind(this); var src = 'data:image/jpeg;base64,' + window.btoa(imageData); img.src = src; diff --git a/src/worker.js b/src/worker.js index ea314744f..a3a539894 100644 --- a/src/worker.js +++ b/src/worker.js @@ -3,11 +3,35 @@ 'use strict'; +function Message(data) { + this.data = data; + this.allowsReply = false; + this.messager; + this.id; +} +Message.prototype = { + reply: function messageReply(data) { + if (!this.allowsReply) + error('This message does not accept replies.'); + + this.messager({ + isReply: true, + callbackId: this.id, + data: data + }); + }, + setupReply: function setupReply(messager, id) { + this.allowsReply = true; + this.messager = messager; + this.id = id; + } +} + function MessageHandler(name, comObj) { this.name = name; this.comObj = comObj; this.callbackIndex = 1; - this.callbacks = {}; + var callbacks = this.callbacks = {}; var ah = this.actionHandler = {}; ah['console_log'] = [function ahConsoleLog(data) { @@ -16,38 +40,25 @@ function MessageHandler(name, comObj) { ah['console_error'] = [function ahConsoleError(data) { console.error.apply(console, data); }]; - ah['__resolve__'] = [ function(data) { + + comObj.onmessage = function messageHandlerComObjOnMessage(event) { + var data = event.data; + if (data.isReply) { var callbackId = data.callbackId; - if (data.callbackId in this.callbacks) { - var callback = this.callbacks[callbackId]; - delete this.callbacks[callbackId]; + if (data.callbackId in callbacks) { + var callback = callbacks[callbackId]; + delete callbacks[callbackId]; callback(data.data); } else { throw 'Cannot resolve callback ' + callbackId; } - }, this]; - - comObj.onmessage = function messageHandlerComObjOnMessage(event) { - var data = event.data; - if (data.action in ah) { + } else if (data.action in ah) { var action = ah[data.action]; - if (data.callbackId) { - action[0].call(action[1], { - data: data.data, - resolve: (function(callbackId) { - return function(resolvedData) { - comObj.postMessage({ - action: '__resolve__', - data: { - data: resolvedData, - callbackId: data.callbackId - } - }); - }})(data.callbackId) - }); - } else { - action[0].call(action[1], data.data); - } + var message = new Message(data.data); + if (data.callbackId) + message.setupReply(this.postMessage, data.callbackId); + + action[0].call(action[1], message); } else { throw 'Unkown action from worker: ' + data.action; } @@ -81,7 +92,8 @@ var WorkerMessageHandler = { setup: function wphSetup(handler) { var pdfDoc = null; - handler.on('test', function wphSetupTest(data) { + handler.on('test', function wphSetupTest(message) { + var data = message.data; handler.send('test', data instanceof Uint8Array); }); @@ -92,13 +104,15 @@ var WorkerMessageHandler = { // undefined action `workerSrc`. }); - handler.on('doc', function wphSetupDoc(data) { + handler.on('doc', function wphSetupDoc(message) { + var data = message.data; // Create only the model of the PDFDoc, which is enough for // processing the content of the pdf. pdfDoc = new PDFDocModel(new Stream(data)); }); - handler.on('page_request', function wphSetupPageRequest(pageNum) { + handler.on('page_request', function wphSetupPageRequest(message) { + var pageNum = message.data; pageNum = parseInt(pageNum); @@ -147,7 +161,8 @@ var WorkerMessageHandler = { }); }, this); - handler.on('font', function wphSetupFont(data) { + handler.on('font', function wphSetupFont(message) { + var data = message.data; var objId = data[0]; var name = data[1]; var file = data[2]; @@ -195,13 +210,6 @@ var WorkerMessageHandler = { handler.send('font_ready', [objId, obj]); }); - - handler.on('jpeg_decoded', function jpegDecoded(data) { - var objId = data[0]; - var imageData = data[1]; - console.log('worker recieved decoded jpeg'); - debugger; - }, this); } }; From 7d1cddf371c968ec03bc116e4af273971ca2dcdc Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Thu, 8 Dec 2011 21:18:04 -0800 Subject: [PATCH 05/13] Add ability to fast track natively supported jpegs. --- src/canvas.js | 17 +++++++++++++++++ src/core.js | 4 ++++ src/evaluator.js | 35 ++++++++++++++++++++--------------- src/image.js | 24 ++++++++++++++++++------ src/stream.js | 2 -- 5 files changed, 59 insertions(+), 23 deletions(-) diff --git a/src/canvas.js b/src/canvas.js index 128f4fbc9..5ec9435c7 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -1036,6 +1036,23 @@ var CanvasGraphics = (function canvasGraphics() { this.restore(); }, + paintJpegXObject: function canvasGraphicsPaintJpegXObject(objId, w, h) { + var domImage = this.objs.get(objId); + if (!domImage) { + error('Dependent image isn\'t ready yet'); + } + + this.save(); + + var ctx = this.ctx; + // scale the image to the unit square + ctx.scale(1 / w, -1 / h); + + ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height, + 0, -h, w, h); + + this.restore(); + }, paintImageMaskXObject: function canvasGraphicsPaintImageMaskXObject( imgArray, inverseDecode, width, height) { function applyStencilMask(buffer, inverseDecode) { diff --git a/src/core.js b/src/core.js index 29bc56e32..cbe4d0fa5 100644 --- a/src/core.js +++ b/src/core.js @@ -563,6 +563,10 @@ var PDFDoc = (function pdfDoc() { var type = data[1]; switch (type) { + case 'JpegStream': + var imageData = data[2]; + loadJpegStream(id, imageData, this.objs); + break; case 'Image': var imageData = data[2]; this.objs.resolve(id, imageData); diff --git a/src/evaluator.js b/src/evaluator.js index a53d15c50..cb46d1b3b 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -208,26 +208,31 @@ var PartialEvaluator = (function partialEvaluator() { // of image processing can be done here. var objId = 'img_' + uniquePrefix + (++self.objIdCounter); insertDependency([objId]); - fn = 'paintImageXObject'; args = [objId, w, h]; - var resolve = (function(objId) { - return function resolve(data) { - handler.send('obj', [objId, 'Image', data]); - }; - })(objId); + var softMask = dict.get('SMask', 'IM') || false; + if (!softMask && image instanceof JpegStream && image.isNative) { + // These JPEGs don't need any more processing so we can just send it. + fn = 'paintJpegXObject'; + handler.send('obj', [objId, 'JpegStream', image.getIR()]); + return; + } + + fn = 'paintImageXObject'; var imageObj = new PDFImage(xref, resources, image, inline, handler); - imageObj.ready(function() { - var imgData = { - width: w, - height: h, - data: new Uint8Array(w * h * 4) + imageObj.ready((function() { + return function(data) { + var imgData = { + width: w, + height: h, + data: new Uint8Array(w * h * 4) + }; + var pixels = imgData.data; + imageObj.fillRgbaBuffer(pixels, imageObj.decode); + handler.send('obj', [objId, 'Image', imgData]); }; - var pixels = imgData.data; - imageObj.fillRgbaBuffer(pixels, imageObj.decode); - resolve(imgData); - }); + })(objId)); } uniquePrefix = uniquePrefix || ''; diff --git a/src/image.js b/src/image.js index c1eaf1c8f..7b9b12c18 100644 --- a/src/image.js +++ b/src/image.js @@ -151,6 +151,8 @@ var PDFImage = (function pdfImage() { var buf = new Uint8Array(width * height); if (smask) { + if (!smask.isReady()) + error('Soft mask is not ready.'); var sw = smask.width; var sh = smask.height; if (sw != this.width || sh != this.height) @@ -168,8 +170,7 @@ var PDFImage = (function pdfImage() { applyStencilMask: function applyStencilMask(buffer, inverseDecode) { var width = this.width, height = this.height; var bitStrideLength = (width + 7) >> 3; - this.image.reset(); - var imgArray = this.image.getBytes(bitStrideLength * height); + var imgArray = this.getImageBytes(bitStrideLength * height); var imgArrayPos = 0; var i, j, mask, buf; // removing making non-masked pixels transparent @@ -197,8 +198,7 @@ var PDFImage = (function pdfImage() { // rows start at byte boundary; var rowBytes = (width * numComps * bpc + 7) >> 3; - this.image.reset(); - var imgArray = this.image.getBytes(height * rowBytes); + var imgArray = this.getImageBytes(height * rowBytes); var comps = this.colorSpace.getRgbBuffer( this.getComponents(imgArray, decodeMap), bpc); @@ -225,8 +225,7 @@ var PDFImage = (function pdfImage() { // rows start at byte boundary; var rowBytes = (width * numComps * bpc + 7) >> 3; - this.image.reset(); - var imgArray = this.image.getBytes(height * rowBytes); + var imgArray = this.getImageBytes(height * rowBytes); var comps = this.getComponents(imgArray); var length = width * height; @@ -234,6 +233,12 @@ var PDFImage = (function pdfImage() { for (var i = 0; i < length; ++i) buffer[i] = comps[i]; }, + getImageBytes: function getImageBytes(length) { + if (!this.isReady()) + error('Image is not ready to be read.'); + this.image.reset(); + return this.image.getBytes(length); + }, isReady: function isReady() { return this.imageReady && this.smaskReady; }, @@ -250,3 +255,10 @@ var PDFImage = (function pdfImage() { }; return constructor; })(); +function loadJpegStream(id, imageData, objs) { + var img = new Image(); + img.onload = (function jpegImageLoaderOnload() { + objs.resolve(id, img); + }); + img.src = 'data:image/jpeg;base64,' + window.btoa(imageData); +} diff --git a/src/stream.js b/src/stream.js index e4732d87a..559fb2ca2 100644 --- a/src/stream.js +++ b/src/stream.js @@ -804,8 +804,6 @@ var JpegStream = (function jpegStream() { this.colorTransform = -1; - this.bytes = bytes; - if (isAdobeImage(bytes)) { // when bug 674619 land, let's check if browser can do // normal cmyk and then we won't have to the following From 683f64d54f7724d75ad919ed00db6306c557d90e Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Sun, 11 Dec 2011 16:56:45 -0800 Subject: [PATCH 06/13] Use promises to track completion of decoding. --- src/evaluator.js | 7 ++-- src/image.js | 86 ++++++++++++++++++++++++++---------------------- src/util.js | 28 +++++++++++++++- 3 files changed, 75 insertions(+), 46 deletions(-) diff --git a/src/evaluator.js b/src/evaluator.js index cb46d1b3b..8b860175e 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -219,10 +219,8 @@ var PartialEvaluator = (function partialEvaluator() { } fn = 'paintImageXObject'; - var imageObj = new PDFImage(xref, resources, image, inline, handler); - imageObj.ready((function() { - return function(data) { + PDFImage.buildImage(function(imageObj) { var imgData = { width: w, height: h, @@ -231,8 +229,7 @@ var PartialEvaluator = (function partialEvaluator() { var pixels = imgData.data; imageObj.fillRgbaBuffer(pixels, imageObj.decode); handler.send('obj', [objId, 'Image', imgData]); - }; - })(objId)); + }, handler, xref, resources, image, inline); } uniquePrefix = uniquePrefix || ''; diff --git a/src/image.js b/src/image.js index 7b9b12c18..ac00a1cb4 100644 --- a/src/image.js +++ b/src/image.js @@ -4,11 +4,28 @@ 'use strict'; var PDFImage = (function pdfImage() { - function constructor(xref, res, image, inline, handler) { + /** + * Decode the image in the main thread if it supported. Resovles the promise + * when the image data is ready. + */ + function handleImageData(handler, xref, res, image, promise) { + if (image instanceof JpegStream && image.isNative) { + // For natively supported jpegs send them to the main thread for decoding. + var dict = image.dict; + var colorSpace = dict.get('ColorSpace', 'CS'); + colorSpace = ColorSpace.parse(colorSpace, xref, res); + var numComps = colorSpace.numComps; + handler.send('jpeg_decode', [image.getIR(), numComps], function(message) { + var data = message.data; + var stream = new Stream(data, 0, data.length, image.dict); + promise.resolve(stream); + }); + } else { + promise.resolve(image); + } + } + function constructor(xref, res, image, inline, smask) { this.image = image; - this.imageReady = true; - this.smaskReady = true; - this.callbacks = []; if (image.getParams) { // JPX/JPEG2000 streams directly contain bits per component @@ -55,31 +72,37 @@ var PDFImage = (function pdfImage() { this.decode = dict.get('Decode', 'D'); var mask = xref.fetchIfRef(dict.get('Mask')); - var smask = xref.fetchIfRef(dict.get('SMask')); if (mask) { TODO('masked images'); } else if (smask) { - this.smaskReady = false; - this.smask = new PDFImage(xref, res, smask, false, handler); - this.smask.ready(function() { - this.smaskReady = true; - if (this.isReady()) - this.fireReady(); - }.bind(this)); - } - - if (image instanceof JpegStream && image.isNative) { - this.imageReady = false; - handler.send('jpeg_decode', [image.getIR(), this.numComps], function(message) { - var data = message.data; - this.image = new Stream(data, 0, data.length); - this.imageReady = true; - if (this.isReady()) - this.fireReady(); - }.bind(this)); + this.smask = new PDFImage(xref, res, smask, false); } } + /** + * Handles processing of image data and calls the callback with an argument + * of a PDFImage when the image is ready to be used. + */ + constructor.buildImage = function buildImage(callback, handler, xref, res, + image, inline) { + var promise = new Promise(); + var smaskPromise = new Promise(); + var promises = [promise, smaskPromise]; + // The image data and smask data may not be ready yet, wait till both are + // resolved. + Promise.all(promises).then(function(results) { + var image = new PDFImage(xref, res, results[0], inline, results[1]); + callback(image); + }); + + handleImageData(handler, xref, res, image, promise); + + var smask = xref.fetchIfRef(image.dict.get('SMask')); + if (smask) + handleImageData(handler, xref, res, smask, smaskPromise); + else + smaskPromise.resolve(null); + }; constructor.prototype = { getComponents: function getComponents(buffer, decodeMap) { @@ -151,8 +174,6 @@ var PDFImage = (function pdfImage() { var buf = new Uint8Array(width * height); if (smask) { - if (!smask.isReady()) - error('Soft mask is not ready.'); var sw = smask.width; var sh = smask.height; if (sw != this.width || sh != this.height) @@ -234,23 +255,8 @@ var PDFImage = (function pdfImage() { buffer[i] = comps[i]; }, getImageBytes: function getImageBytes(length) { - if (!this.isReady()) - error('Image is not ready to be read.'); this.image.reset(); return this.image.getBytes(length); - }, - isReady: function isReady() { - return this.imageReady && this.smaskReady; - }, - fireReady: function fireReady() { - for (var i = 0; i < this.callbacks.length; ++i) - this.callbacks[i](); - this.callbacks = []; - }, - ready: function ready(callback) { - this.callbacks.push(callback); - if (this.isReady()) - this.fireReady(); } }; return constructor; diff --git a/src/util.js b/src/util.js index 4fb96f062..75cc87c6d 100644 --- a/src/util.js +++ b/src/util.js @@ -217,7 +217,33 @@ var Promise = (function promise() { } this.callbacks = []; }; - + /** + * Builds a promise that is resolved when all the passed in promises are + * resolved. + * @param Array promises + * @return Promise + */ + Promise.all = function(promises) { + var deferred = new Promise(); + var unresolved = promises.length; + var results = []; + if (unresolved === 0) { + deferred.resolve(results); + return deferred; + } + for (var i = 0; i < unresolved; ++i) { + var promise = promises[i]; + promise.then((function(i) { + return function(value) { + results[i] = value; + unresolved--; + if (unresolved === 0) + deferred.resolve(results); + }; + })(i)); + } + return deferred; + }; Promise.prototype = { hasData: false, From 0d370fd20624990840e50aa87f644cea1b202d60 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Mon, 12 Dec 2011 09:17:40 -0800 Subject: [PATCH 07/13] Remove debugging. --- src/core.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core.js b/src/core.js index fd3dec059..4dd7a90b8 100644 --- a/src/core.js +++ b/src/core.js @@ -8,7 +8,7 @@ var globalScope = (typeof window === 'undefined') ? this : window; var isWorker = (typeof window == 'undefined'); var ERRORS = 0, WARNINGS = 1, TODOS = 5; -var verbosity = TODOS; +var verbosity = WARNINGS; // The global PDFJS object exposes the API // In production, it will be declared outside a global wrapper @@ -16,7 +16,6 @@ var verbosity = TODOS; if (!globalScope.PDFJS) { globalScope.PDFJS = {}; } -PDFJS.disableWorker = true; // getPdf() // Convenience function to perform binary Ajax GET // Usage: getPdf('http://...', callback) From 176452c988e3b83624ae613a3d0cd78e89fc448e Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Mon, 12 Dec 2011 09:26:24 -0800 Subject: [PATCH 08/13] Style nits. --- src/core.js | 2 +- src/evaluator.js | 2 +- src/image.js | 2 +- src/util.js | 4 ++-- src/worker.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core.js b/src/core.js index 4dd7a90b8..79b7a12ca 100644 --- a/src/core.js +++ b/src/core.js @@ -647,7 +647,7 @@ var PDFDoc = (function PDFDocClosure() { buf[j + 1] = data[i + 1]; buf[j + 2] = data[i + 2]; } - } else if(components == 1) { + } else if (components == 1) { for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) { buf[j] = data[i]; } diff --git a/src/evaluator.js b/src/evaluator.js index 1a70b35c8..edef57f91 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -179,7 +179,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { return loadedName; } - function buildPaintImageXObject(image, inline) { + function buildPaintImageXObject(image, inline) { var dict = image.dict; var w = dict.get('Width', 'W'); var h = dict.get('Height', 'H'); diff --git a/src/image.js b/src/image.js index 030c20952..38377b4f7 100644 --- a/src/image.js +++ b/src/image.js @@ -13,7 +13,7 @@ var PDFImage = (function PDFImageClosure() { // For natively supported jpegs send them to the main thread for decoding. var dict = image.dict; var colorSpace = dict.get('ColorSpace', 'CS'); - colorSpace = ColorSpace.parse(colorSpace, xref, res); + colorSpace = ColorSpace.parse(colorSpace, xref, res); var numComps = colorSpace.numComps; handler.send('jpeg_decode', [image.getIR(), numComps], function(message) { var data = message.data; diff --git a/src/util.js b/src/util.js index 8a775fd94..57dbca4bb 100644 --- a/src/util.js +++ b/src/util.js @@ -220,8 +220,8 @@ var Promise = (function PromiseClosure() { /** * Builds a promise that is resolved when all the passed in promises are * resolved. - * @param Array promises - * @return Promise + * @param {Promise[]} promises Array of promises to wait for. + * @return {Promise} New dependant promise. */ Promise.all = function(promises) { var deferred = new Promise(); diff --git a/src/worker.js b/src/worker.js index d1c8026d4..dfd6d9969 100644 --- a/src/worker.js +++ b/src/worker.js @@ -25,7 +25,7 @@ Message.prototype = { this.messager = messager; this.id = id; } -} +}; function MessageHandler(name, comObj) { this.name = name; From 948de2bdbe57a80433c87a2fe4fe27aeabf0ccae Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Mon, 12 Dec 2011 09:53:31 -0800 Subject: [PATCH 09/13] Remove some unintended changes. --- src/canvas.js | 1 + src/core.js | 2 +- src/image.js | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/canvas.js b/src/canvas.js index 086b43d97..475dbac08 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -1053,6 +1053,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.restore(); }, + paintImageMaskXObject: function canvasGraphicsPaintImageMaskXObject( imgArray, inverseDecode, width, height) { function applyStencilMask(buffer, inverseDecode) { diff --git a/src/core.js b/src/core.js index 79b7a12ca..e97465599 100644 --- a/src/core.js +++ b/src/core.js @@ -16,6 +16,7 @@ var verbosity = WARNINGS; if (!globalScope.PDFJS) { globalScope.PDFJS = {}; } + // getPdf() // Convenience function to perform binary Ajax GET // Usage: getPdf('http://...', callback) @@ -446,7 +447,6 @@ var PDFDocModel = (function PDFDocModelClosure() { this.startXRef, this.mainXRefEntriesOffset); this.catalog = new Catalog(this.xref); - this.objs = new PDFObjects(); }, get numPages() { var linearization = this.linearization; diff --git a/src/image.js b/src/image.js index 38377b4f7..cc7242ad5 100644 --- a/src/image.js +++ b/src/image.js @@ -26,7 +26,6 @@ var PDFImage = (function PDFImageClosure() { } function PDFImage(xref, res, image, inline, smask) { this.image = image; - if (image.getParams) { // JPX/JPEG2000 streams directly contain bits per component // and color space mode information. From fcd612e4867d58ab660c10e4d855caed7f8a16b0 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Mon, 12 Dec 2011 11:38:16 -0800 Subject: [PATCH 10/13] Add comments to some of the code I touched/created. --- src/worker.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/worker.js b/src/worker.js index dfd6d9969..56bcb9abd 100644 --- a/src/worker.js +++ b/src/worker.js @@ -3,6 +3,9 @@ 'use strict'; +/** + * A wrapper for data to facilitate adding functionality to messages. + */ function Message(data) { this.data = data; this.allowsReply = false; @@ -10,6 +13,9 @@ function Message(data) { this.id; } Message.prototype = { + /** + * Reply to the action handler that sent the message. + */ reply: function messageReply(data) { if (!this.allowsReply) error('This message does not accept replies.'); @@ -20,6 +26,11 @@ Message.prototype = { data: data }); }, + /** + * Setup the message to allow a reply. + * @param {function} messager A function that takes a JSON reply. + * @param {String} id The id to identify this message. + */ setupReply: function setupReply(messager, id) { this.allowsReply = true; this.messager = messager; @@ -73,7 +84,12 @@ MessageHandler.prototype = { } ah[actionName] = [handler, scope]; }, - + /** + * Sends a message to the comObj to invoke the action with the supplied data. + * @param {String} actionName Action to call. + * @param {JSON} data JSON data to send. + * @param {function} [callback] Optional callback that will handle a reply. + */ send: function messageHandlerSend(actionName, data, callback) { var message = { action: actionName, From 94a48cab82b3f98619e2777d02001a109c61baec Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Mon, 12 Dec 2011 15:09:05 -0800 Subject: [PATCH 11/13] Address Julian's comments. --- src/core.js | 8 ++++---- src/image.js | 10 +++++----- test/pdfs/aboutstacks.pdf.link | 1 + test/test_manifest.json | 8 ++++++++ 4 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 test/pdfs/aboutstacks.pdf.link diff --git a/src/core.js b/src/core.js index e97465599..dc44edb76 100644 --- a/src/core.js +++ b/src/core.js @@ -636,10 +636,10 @@ var PDFDoc = (function PDFDocClosure() { var size = width * height; var rgbaLength = size * 4; var buf = new Uint8Array(size * components); - var tempCanvas = new ScratchCanvas(width, height); - var tempCtx = tempCanvas.getContext('2d'); - tempCtx.drawImage(img, 0, 0); - var data = tempCtx.getImageData(0, 0, width, height).data; + var tmpCanvas = new ScratchCanvas(width, height); + var tmpCtx = tmpCanvas.getContext('2d'); + tmpCtx.drawImage(img, 0, 0); + var data = tmpCtx.getImageData(0, 0, width, height).data; if (components == 3) { for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) { diff --git a/src/image.js b/src/image.js index cc7242ad5..987542c58 100644 --- a/src/image.js +++ b/src/image.js @@ -84,17 +84,17 @@ var PDFImage = (function PDFImageClosure() { */ PDFImage.buildImage = function buildImage(callback, handler, xref, res, image, inline) { - var promise = new Promise(); + var imageDataPromise = new Promise(); var smaskPromise = new Promise(); - var promises = [promise, smaskPromise]; // The image data and smask data may not be ready yet, wait till both are // resolved. - Promise.all(promises).then(function(results) { - var image = new PDFImage(xref, res, results[0], inline, results[1]); + Promise.all([imageDataPromise, smaskPromise]).then(function(results) { + var imageData = results[0], smaskData = results[1]; + var image = new PDFImage(xref, res, imageData, inline, smaskData); callback(image); }); - handleImageData(handler, xref, res, image, promise); + handleImageData(handler, xref, res, image, imageDataPromise); var smask = xref.fetchIfRef(image.dict.get('SMask')); if (smask) diff --git a/test/pdfs/aboutstacks.pdf.link b/test/pdfs/aboutstacks.pdf.link new file mode 100644 index 000000000..8b04ec042 --- /dev/null +++ b/test/pdfs/aboutstacks.pdf.link @@ -0,0 +1 @@ +http://greenhousechallenge.org/media/item/313/38/About-Stacks.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index 4d55ac0f7..813fc272b 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -340,5 +340,13 @@ "link": true, "pageLimit": 3, "type": "eq" + }, + { + "id": "aboutstacks", + "file": "pdfs/aboutstacks.pdf", + "md5": "6e7c8416a293ba2d83bc8dd20c6ccf51", + "rounds": 1, + "link": true, + "type": "eq" } ] From 2f10d2450297c962e62521965918570c53da43c7 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Tue, 13 Dec 2011 09:05:00 -0800 Subject: [PATCH 12/13] Fix for chrome. --- src/worker.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/worker.js b/src/worker.js index 56bcb9abd..c0496015a 100644 --- a/src/worker.js +++ b/src/worker.js @@ -9,7 +9,7 @@ function Message(data) { this.data = data; this.allowsReply = false; - this.messager; + this.combObj; this.id; } Message.prototype = { @@ -20,7 +20,7 @@ Message.prototype = { if (!this.allowsReply) error('This message does not accept replies.'); - this.messager({ + this.combObj.postMessage({ isReply: true, callbackId: this.id, data: data @@ -28,12 +28,12 @@ Message.prototype = { }, /** * Setup the message to allow a reply. - * @param {function} messager A function that takes a JSON reply. + * @param {Object} combObj The handler that has a postMessage function. * @param {String} id The id to identify this message. */ - setupReply: function setupReply(messager, id) { + setupReply: function setupReply(combObj, id) { this.allowsReply = true; - this.messager = messager; + this.combObj = combObj; this.id = id; } }; @@ -67,7 +67,7 @@ function MessageHandler(name, comObj) { var action = ah[data.action]; var message = new Message(data.data); if (data.callbackId) - message.setupReply(this.postMessage, data.callbackId); + message.setupReply(comObj, data.callbackId); action[0].call(action[1], message); } else { From d1f4e7c7d53308c53695ba12c048a0f9649d3512 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Wed, 14 Dec 2011 14:02:00 -0800 Subject: [PATCH 13/13] Change to passing promises. --- src/core.js | 23 +++++++----------- src/worker.js | 65 ++++++++++++++------------------------------------- 2 files changed, 26 insertions(+), 62 deletions(-) diff --git a/src/core.js b/src/core.js index dc44edb76..a6effd2bd 100644 --- a/src/core.js +++ b/src/core.js @@ -515,8 +515,7 @@ var PDFDoc = (function PDFDocClosure() { // Tell the worker the file it was created from. messageHandler.send('workerSrc', workerSrc); - messageHandler.on('test', function pdfDocTest(message) { - var supportTypedArray = message.data; + messageHandler.on('test', function pdfDocTest(supportTypedArray) { if (supportTypedArray) { this.worker = worker; this.setupMessageHandler(messageHandler); @@ -554,8 +553,7 @@ var PDFDoc = (function PDFDocClosure() { setupMessageHandler: function(messageHandler) { this.messageHandler = messageHandler; - messageHandler.on('page', function pdfDocPage(message) { - var data = message.data; + messageHandler.on('page', function pdfDocPage(data) { var pageNum = data.pageNum; var page = this.pageCache[pageNum]; var depFonts = data.depFonts; @@ -563,8 +561,7 @@ var PDFDoc = (function PDFDocClosure() { page.startRenderingFromIRQueue(data.IRQueue, depFonts); }, this); - messageHandler.on('obj', function pdfDocObj(message) { - var data = message.data; + messageHandler.on('obj', function pdfDocObj(data) { var id = data[0]; var type = data[1]; @@ -601,8 +598,7 @@ var PDFDoc = (function PDFDocClosure() { } }, this); - messageHandler.on('font_ready', function pdfDocFontReady(message) { - var data = message.data; + messageHandler.on('font_ready', function pdfDocFontReady(data) { var id = data[0]; var font = new FontShape(data[1]); @@ -614,8 +610,7 @@ var PDFDoc = (function PDFDocClosure() { } }.bind(this)); - messageHandler.on('page_error', function pdfDocError(message) { - var data = message.data; + messageHandler.on('page_error', function pdfDocError(data) { var page = this.pageCache[data.pageNum]; if (page.callback) page.callback(data.error); @@ -623,9 +618,9 @@ var PDFDoc = (function PDFDocClosure() { throw data.error; }, this); - messageHandler.on('jpeg_decode', function(message) { - var imageData = message.data[0]; - var components = message.data[1]; + messageHandler.on('jpeg_decode', function(data, promise) { + var imageData = data[0]; + var components = data[1]; if (components != 3 && components != 1) error('Only 3 component or 1 component can be returned'); @@ -652,7 +647,7 @@ var PDFDoc = (function PDFDocClosure() { buf[j] = data[i]; } } - message.reply({ data: buf, width: width, height: height}); + promise.resolve({ data: buf, width: width, height: height}); }).bind(this); var src = 'data:image/jpeg;base64,' + window.btoa(imageData); img.src = src; diff --git a/src/worker.js b/src/worker.js index c0496015a..c18de65ad 100644 --- a/src/worker.js +++ b/src/worker.js @@ -3,41 +3,6 @@ 'use strict'; -/** - * A wrapper for data to facilitate adding functionality to messages. - */ -function Message(data) { - this.data = data; - this.allowsReply = false; - this.combObj; - this.id; -} -Message.prototype = { - /** - * Reply to the action handler that sent the message. - */ - reply: function messageReply(data) { - if (!this.allowsReply) - error('This message does not accept replies.'); - - this.combObj.postMessage({ - isReply: true, - callbackId: this.id, - data: data - }); - }, - /** - * Setup the message to allow a reply. - * @param {Object} combObj The handler that has a postMessage function. - * @param {String} id The id to identify this message. - */ - setupReply: function setupReply(combObj, id) { - this.allowsReply = true; - this.combObj = combObj; - this.id = id; - } -}; - function MessageHandler(name, comObj) { this.name = name; this.comObj = comObj; @@ -65,11 +30,19 @@ function MessageHandler(name, comObj) { } } else if (data.action in ah) { var action = ah[data.action]; - var message = new Message(data.data); - if (data.callbackId) - message.setupReply(comObj, data.callbackId); - - action[0].call(action[1], message); + if (data.callbackId) { + var promise = new Promise(); + promise.then(function(resolvedData) { + comObj.postMessage({ + isReply: true, + callbackId: data.callbackId, + data: resolvedData + }); + }); + action[0].call(action[1], data.data, promise); + } else { + action[0].call(action[1], data.data); + } } else { throw 'Unkown action from worker: ' + data.action; } @@ -108,8 +81,7 @@ var WorkerMessageHandler = { setup: function wphSetup(handler) { var pdfDoc = null; - handler.on('test', function wphSetupTest(message) { - var data = message.data; + handler.on('test', function wphSetupTest(data) { handler.send('test', data instanceof Uint8Array); }); @@ -120,15 +92,13 @@ var WorkerMessageHandler = { // undefined action `workerSrc`. }); - handler.on('doc', function wphSetupDoc(message) { - var data = message.data; + handler.on('doc', function wphSetupDoc(data) { // Create only the model of the PDFDoc, which is enough for // processing the content of the pdf. pdfDoc = new PDFDocModel(new Stream(data)); }); - handler.on('page_request', function wphSetupPageRequest(message) { - var pageNum = message.data; + handler.on('page_request', function wphSetupPageRequest(pageNum) { pageNum = parseInt(pageNum); @@ -177,8 +147,7 @@ var WorkerMessageHandler = { }); }, this); - handler.on('font', function wphSetupFont(message) { - var data = message.data; + handler.on('font', function wphSetupFont(data) { var objId = data[0]; var name = data[1]; var file = data[2];