diff --git a/README.md b/README.md index f3500ae4d..7e5d2eeb3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # pdf.js - + ## Overview @@ -205,3 +205,4 @@ a "PDF Reference" from Adobe: Recommended chapters to read: "2. Overview", "3.4 File Structure", "4.1 Graphics Objects" that lists the PDF commands. + diff --git a/src/canvas.js b/src/canvas.js index 6007d0031..e056fe0f2 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -17,8 +17,8 @@ var TextRenderingMode = { ADD_TO_PATH: 7 }; -var CanvasExtraState = (function canvasExtraState() { - function constructor(old) { +var CanvasExtraState = (function CanvasExtraStateClosure() { + function CanvasExtraState(old) { // Are soft masks and alpha values shapes or opacities? this.alphaIsShape = false; this.fontSize = 0; @@ -52,7 +52,7 @@ var CanvasExtraState = (function canvasExtraState() { this.old = old; } - constructor.prototype = { + CanvasExtraState.prototype = { clone: function canvasextra_clone() { return Object.create(this); }, @@ -61,7 +61,7 @@ var CanvasExtraState = (function canvasExtraState() { this.y = y; } }; - return constructor; + return CanvasExtraState; })(); function ScratchCanvas(width, height) { @@ -181,12 +181,12 @@ function addContextCurrentTransform(ctx) { } } -var CanvasGraphics = (function canvasGraphics() { +var CanvasGraphics = (function CanvasGraphicsClosure() { // Defines the time the executeIRQueue is going to be executing // before it stops and shedules a continue of execution. var kExecutionTime = 50; - function constructor(canvasCtx, objs, textLayer) { + function CanvasGraphics(canvasCtx, objs, textLayer) { this.ctx = canvasCtx; this.current = new CanvasExtraState(); this.stateStack = []; @@ -206,7 +206,7 @@ var CanvasGraphics = (function canvasGraphics() { var NORMAL_CLIP = {}; var EO_CLIP = {}; - constructor.prototype = { + CanvasGraphics.prototype = { slowCommands: { 'stroke': true, 'closeStroke': true, @@ -638,17 +638,7 @@ var CanvasGraphics = (function canvasGraphics() { geometry.hScale = tr[0] - bl[0]; geometry.vScale = tr[1] - bl[1]; } - var spaceGlyph = font.charsToGlyphs(' '); - - // Hack (sometimes space is not encoded) - if (spaceGlyph.length === 0 || spaceGlyph[0].width === 0) - spaceGlyph = font.charsToGlyphs('i'); - - // Fallback - if (spaceGlyph.length === 0 || spaceGlyph[0].width === 0) - spaceGlyph = [{width: 0}]; - - geometry.spaceWidth = spaceGlyph[0].width; + geometry.spaceWidth = font.spaceWidth; return geometry; }, @@ -687,13 +677,6 @@ var CanvasGraphics = (function canvasGraphics() { var textSelection = textLayer && !skipTextSelection ? true : false; var textRenderingMode = current.textRenderingMode; - if (textSelection) { - ctx.save(); - this.applyTextTransforms(); - text.geom = this.getTextGeometry(); - ctx.restore(); - } - // Type3 fonts - each glyph is a "mini-PDF" if (font.coded) { ctx.save(); @@ -701,6 +684,13 @@ var CanvasGraphics = (function canvasGraphics() { ctx.translate(current.x, current.y); ctx.scale(textHScale, 1); + + if (textSelection) { + this.save(); + ctx.scale(1, -1); + text.geom = this.getTextGeometry(); + this.restore(); + } for (var i = 0; i < glyphsLength; ++i) { var glyph = glyphs[i]; @@ -720,7 +710,7 @@ var CanvasGraphics = (function canvasGraphics() { var width = transformed[0] * fontSize + charSpacing; ctx.translate(width, 0); - current.x += width * textHScale2; + current.x += width * textHScale; text.str += glyph.unicode; text.length++; @@ -730,6 +720,8 @@ var CanvasGraphics = (function canvasGraphics() { } else { ctx.save(); this.applyTextTransforms(); + if (textSelection) + text.geom = this.getTextGeometry(); var width = 0; for (var i = 0; i < glyphsLength; ++i) { @@ -780,18 +772,26 @@ var CanvasGraphics = (function canvasGraphics() { showSpacedText: function canvasGraphicsShowSpacedText(arr) { var ctx = this.ctx; var current = this.current; + var font = current.font; var fontSize = current.fontSize; - var textHScale2 = current.textHScale * - (current.font.fontMatrix || IDENTITY_MATRIX)[0]; + var textHScale = current.textHScale; + if (!font.coded) + textHScale *= (font.fontMatrix || IDENTITY_MATRIX)[0]; var arrLength = arr.length; var textLayer = this.textLayer; - var font = current.font; var text = {str: '', length: 0, canvasWidth: 0, geom: {}}; var textSelection = textLayer ? true : false; if (textSelection) { ctx.save(); - this.applyTextTransforms(); + // Type3 fonts - each glyph is a "mini-PDF" (see also showText) + if (font.coded) { + ctx.transform.apply(ctx, current.textMatrix); + ctx.scale(1, -1); + ctx.translate(current.x, -1 * current.y); + ctx.scale(textHScale, 1); + } else + this.applyTextTransforms(); text.geom = this.getTextGeometry(); ctx.restore(); } @@ -799,7 +799,7 @@ var CanvasGraphics = (function canvasGraphics() { for (var i = 0; i < arrLength; ++i) { var e = arr[i]; if (isNum(e)) { - var spacingLength = -e * 0.001 * fontSize * textHScale2; + var spacingLength = -e * 0.001 * fontSize * textHScale; current.x += spacingLength; if (textSelection) { @@ -807,9 +807,10 @@ var CanvasGraphics = (function canvasGraphics() { text.canvasWidth += spacingLength; if (e < 0 && text.geom.spaceWidth > 0) { // avoid div by zero var numFakeSpaces = Math.round(-e / text.geom.spaceWidth); - for (var j = 0; j < numFakeSpaces; ++j) + if (numFakeSpaces > 0) { text.str += ' '; - text.length += numFakeSpaces > 0 ? 1 : 0; + text.length++; + } } } } else if (isString(e)) { @@ -985,9 +986,9 @@ var CanvasGraphics = (function canvasGraphics() { var height = canvas.height; var bl = Util.applyTransform([0, 0], inv); - var br = Util.applyTransform([0, width], inv); - var ul = Util.applyTransform([height, 0], inv); - var ur = Util.applyTransform([height, width], inv); + var br = Util.applyTransform([0, height], inv); + var ul = Util.applyTransform([width, 0], inv); + var ur = Util.applyTransform([width, height], inv); var x0 = Math.min(bl[0], br[0], ul[0], ur[0]); var y0 = Math.min(bl[1], br[1], ul[1], ur[1]); @@ -1037,8 +1038,8 @@ var CanvasGraphics = (function canvasGraphics() { }, paintJpegXObject: function canvasGraphicsPaintJpegXObject(objId, w, h) { - var image = this.objs.get(objId); - if (!image) { + var domImage = this.objs.get(objId); + if (!domImage) { error('Dependent image isn\'t ready yet'); } @@ -1048,7 +1049,6 @@ var CanvasGraphics = (function canvasGraphics() { // 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, 0, -h, w, h); @@ -1104,7 +1104,11 @@ var CanvasGraphics = (function canvasGraphics() { this.restore(); }, - paintImageXObject: 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; @@ -1114,26 +1118,16 @@ var CanvasGraphics = (function canvasGraphics() { var tmpCanvas = new this.ScratchCanvas(w, h); var tmpCtx = tmpCanvas.getContext('2d'); - var tmpImgData; + this.putBinaryImageData(tmpCtx, imgData, w, h); - // Some browsers can set an UInt8Array directly as imageData, some - // can't. As long as we don't have proper feature detection, just - // copy over each pixel and set the imageData that way. - tmpImgData = tmpCtx.getImageData(0, 0, w, h); - - // Copy over the imageData. - var tmpImgDataPixels = tmpImgData.data; - var len = tmpImgDataPixels.length; - - while (len--) { - tmpImgDataPixels[len] = imgData.data[len]; - } - - tmpCtx.putImageData(tmpImgData, 0, 0); ctx.drawImage(tmpCanvas, 0, -h); this.restore(); }, + putBinaryImageData: function canvasPutBinaryImageData() { + // + }, + // Marked content markPoint: function canvasGraphicsMarkPoint(tag) { @@ -1191,6 +1185,41 @@ var CanvasGraphics = (function canvasGraphics() { } }; - return constructor; + return CanvasGraphics; })(); +if (!isWorker) { + // Feature detection if the browser can use an Uint8Array directly as imgData. + var canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + var ctx = canvas.getContext('2d'); + + try { + ctx.putImageData({ + width: 1, + height: 1, + data: new Uint8Array(4) + }, 0, 0); + + CanvasGraphics.prototype.putBinaryImageData = + function CanvasGraphicsPutBinaryImageDataNative(ctx, imgData) { + ctx.putImageData(imgData, 0, 0); + }; + } catch (e) { + CanvasGraphics.prototype.putBinaryImageData = + function CanvasGraphicsPutBinaryImageDataShim(ctx, imgData, w, h) { + var tmpImgData = ctx.getImageData(0, 0, w, h); + + // Copy over the imageData pixel by pixel. + var tmpImgDataPixels = tmpImgData.data; + var len = tmpImgDataPixels.length; + + while (len--) { + tmpImgDataPixels[len] = imgData.data[len]; + } + + ctx.putImageData(tmpImgData, 0, 0); + }; + } +} diff --git a/src/colorspace.js b/src/colorspace.js index b369d0f88..231ff6923 100644 --- a/src/colorspace.js +++ b/src/colorspace.js @@ -3,13 +3,13 @@ 'use strict'; -var ColorSpace = (function colorSpaceColorSpace() { +var ColorSpace = (function ColorSpaceClosure() { // Constructor should define this.numComps, this.defaultColor, this.name - function constructor() { + function ColorSpace() { error('should not call ColorSpace constructor'); } - constructor.prototype = { + ColorSpace.prototype = { // Input: array of size numComps representing color component values // Output: array of rgb values, each value ranging from [0.1] getRgb: function colorSpaceGetRgb(color) { @@ -22,15 +22,15 @@ var ColorSpace = (function colorSpaceColorSpace() { } }; - constructor.parse = function colorSpaceParse(cs, xref, res) { - var IR = constructor.parseToIR(cs, xref, res); + ColorSpace.parse = function colorSpaceParse(cs, xref, res) { + var IR = ColorSpace.parseToIR(cs, xref, res); if (IR instanceof AlternateCS) return IR; - return constructor.fromIR(IR); + return ColorSpace.fromIR(IR); }; - constructor.fromIR = function colorSpaceFromIR(IR) { + ColorSpace.fromIR = function colorSpaceFromIR(IR) { var name = isArray(IR) ? IR[0] : IR; switch (name) { @@ -63,7 +63,7 @@ var ColorSpace = (function colorSpaceColorSpace() { return null; }; - constructor.parseToIR = function colorSpaceParseToIR(cs, xref, res) { + ColorSpace.parseToIR = function colorSpaceParseToIR(cs, xref, res) { if (isName(cs)) { var colorSpaces = xref.fetchIfRef(res.get('ColorSpace')); if (isDict(colorSpaces)) { @@ -155,7 +155,7 @@ var ColorSpace = (function colorSpaceColorSpace() { return null; }; - return constructor; + return ColorSpace; })(); /** @@ -164,8 +164,8 @@ var ColorSpace = (function colorSpaceColorSpace() { * Both color spaces use a tinting function to convert colors to a base color * space. */ -var AlternateCS = (function alternateCS() { - function constructor(numComps, base, tintFn) { +var AlternateCS = (function AlternateCSClosure() { + function AlternateCS(numComps, base, tintFn) { this.name = 'Alternate'; this.numComps = numComps; this.defaultColor = []; @@ -175,7 +175,7 @@ var AlternateCS = (function alternateCS() { this.tintFn = tintFn; } - constructor.prototype = { + AlternateCS.prototype = { getRgb: function altcs_getRgb(color) { var tinted = this.tintFn(color); return this.base.getRgb(tinted); @@ -203,21 +203,21 @@ var AlternateCS = (function alternateCS() { } }; - return constructor; + return AlternateCS; })(); -var PatternCS = (function patternCS() { - function constructor(baseCS) { +var PatternCS = (function PatternCSClosure() { + function PatternCS(baseCS) { this.name = 'Pattern'; this.base = baseCS; } - constructor.prototype = {}; + PatternCS.prototype = {}; - return constructor; + return PatternCS; })(); -var IndexedCS = (function indexedCS() { - function constructor(base, highVal, lookup) { +var IndexedCS = (function IndexedCSClosure() { + function IndexedCS(base, highVal, lookup) { this.name = 'Indexed'; this.numComps = 1; this.defaultColor = [0]; @@ -240,7 +240,7 @@ var IndexedCS = (function indexedCS() { this.lookup = lookupArray; } - constructor.prototype = { + IndexedCS.prototype = { getRgb: function indexcs_getRgb(color) { var numComps = this.base.numComps; var start = color[0] * numComps; @@ -269,17 +269,17 @@ var IndexedCS = (function indexedCS() { return base.getRgbBuffer(baseBuf, 8); } }; - return constructor; + return IndexedCS; })(); -var DeviceGrayCS = (function deviceGrayCS() { - function constructor() { +var DeviceGrayCS = (function DeviceGrayCSClosure() { + function DeviceGrayCS() { this.name = 'DeviceGray'; this.numComps = 1; this.defaultColor = [0]; } - constructor.prototype = { + DeviceGrayCS.prototype = { getRgb: function graycs_getRgb(color) { var c = color[0]; return [c, c, c]; @@ -297,16 +297,16 @@ var DeviceGrayCS = (function deviceGrayCS() { return rgbBuf; } }; - return constructor; + return DeviceGrayCS; })(); -var DeviceRgbCS = (function deviceRgbCS() { - function constructor() { +var DeviceRgbCS = (function DeviceRgbCSClosure() { + function DeviceRgbCS() { this.name = 'DeviceRGB'; this.numComps = 3; this.defaultColor = [0, 0, 0]; } - constructor.prototype = { + DeviceRgbCS.prototype = { getRgb: function rgbcs_getRgb(color) { return color; }, @@ -321,16 +321,16 @@ var DeviceRgbCS = (function deviceRgbCS() { return rgbBuf; } }; - return constructor; + return DeviceRgbCS; })(); -var DeviceCmykCS = (function deviceCmykCS() { - function constructor() { +var DeviceCmykCS = (function DeviceCmykCSClosure() { + function DeviceCmykCS() { this.name = 'DeviceCMYK'; this.numComps = 4; this.defaultColor = [0, 0, 0, 1]; } - constructor.prototype = { + DeviceCmykCS.prototype = { getRgb: function cmykcs_getRgb(color) { var c = color[0], m = color[1], y = color[2], k = color[3]; var c1 = 1 - c, m1 = 1 - m, y1 = 1 - y, k1 = 1 - k; @@ -406,6 +406,6 @@ var DeviceCmykCS = (function deviceCmykCS() { } }; - return constructor; + return DeviceCmykCS; })(); diff --git a/src/core.js b/src/core.js index d6d70a2ed..71c18f178 100644 --- a/src/core.js +++ b/src/core.js @@ -5,6 +5,8 @@ var globalScope = (typeof window === 'undefined') ? this : window; +var isWorker = (typeof window == 'undefined'); + var ERRORS = 0, WARNINGS = 1, TODOS = 5; var verbosity = WARNINGS; @@ -31,7 +33,7 @@ function getPdf(arg, callback) { var xhr = new XMLHttpRequest(); xhr.open('GET', params.url); xhr.mozResponseType = xhr.responseType = 'arraybuffer'; - xhr.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200; + xhr.expected = (params.url.indexOf('file:') === 0) ? 0 : 200; if ('progress' in params) xhr.onprogress = params.progress || undefined; @@ -54,8 +56,8 @@ function getPdf(arg, callback) { } globalScope.PDFJS.getPdf = getPdf; -var Page = (function pagePage() { - function constructor(xref, pageNumber, pageDict, ref) { +var Page = (function PageClosure() { + function Page(xref, pageNumber, pageDict, ref) { this.pageNumber = pageNumber; this.pageDict = pageDict; this.stats = { @@ -72,7 +74,7 @@ var Page = (function pagePage() { this.callback = null; } - constructor.prototype = { + Page.prototype = { getPageProp: function pageGetPageProp(key) { return this.xref.fetchIfRef(this.pageDict.get(key)); }, @@ -392,7 +394,7 @@ var Page = (function pagePage() { } }; - return constructor; + return Page; })(); /** @@ -405,8 +407,8 @@ var Page = (function pagePage() { * need for the `PDFDocModel` anymore and there is only one object on the * main thread and not one entire copy on each worker instance. */ -var PDFDocModel = (function pdfDoc() { - function constructor(arg, callback) { +var PDFDocModel = (function PDFDocModelClosure() { + function PDFDocModel(arg, callback) { if (isStream(arg)) init.call(this, arg); else if (isArrayBuffer(arg)) @@ -438,7 +440,7 @@ var PDFDocModel = (function pdfDoc() { return true; /* found */ } - constructor.prototype = { + PDFDocModel.prototype = { get linearization() { var length = this.stream.length; var linearization = false; @@ -460,12 +462,17 @@ var PDFDocModel = (function pdfDoc() { if (find(stream, 'endobj', 1024)) startXRef = stream.pos + 6; } else { - // Find startxref at the end of the file. - var start = stream.end - 1024; - if (start < 0) - start = 0; - stream.pos = start; - if (find(stream, 'startxref', 1024, true)) { + // Find startxref by jumping backward from the end of the file. + var step = 1024; + var found = false, pos = stream.end; + while (!found && pos > 0) { + pos -= step - 'startxref'.length; + if (pos < 0) + pos = 0; + stream.pos = pos; + found = find(stream, 'startxref', step, true); + } + if (found) { stream.skip(9); var ch; do { @@ -522,11 +529,11 @@ var PDFDocModel = (function pdfDoc() { } }; - return constructor; + return PDFDocModel; })(); -var PDFDoc = (function pdfDoc() { - function constructor(arg, callback) { +var PDFDoc = (function PDFDocClosure() { + function PDFDoc(arg, callback) { var stream = null; var data = null; @@ -594,7 +601,7 @@ var PDFDoc = (function pdfDoc() { } } - constructor.prototype = { + PDFDoc.prototype = { setupFakeWorker: function() { // If we don't use a worker, just post/sendMessage to the main thread. var fakeWorker = { @@ -630,8 +637,12 @@ var PDFDoc = (function pdfDoc() { switch (type) { case 'JpegStream': - var IR = data[2]; - new JpegImageLoader(id, IR, this.objs); + var imageData = data[2]; + loadJpegStream(id, imageData, this.objs); + break; + case 'Image': + var imageData = data[2]; + this.objs.resolve(id, imageData); break; case 'Font': var name = data[2]; @@ -677,6 +688,41 @@ var PDFDoc = (function pdfDoc() { throw data.error; }, this); + 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'); + + var img = new Image(); + img.onload = (function jpegImageLoaderOnload() { + var width = img.width; + var height = img.height; + var size = width * height; + var rgbaLength = size * 4; + var buf = new Uint8Array(size * components); + 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) { + 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]; + } + } + promise.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); @@ -723,7 +769,7 @@ var PDFDoc = (function pdfDoc() { } }; - return constructor; + return PDFDoc; })(); globalScope.PDFJS.PDFDoc = PDFDoc; diff --git a/src/crypto.js b/src/crypto.js index 955598644..7c34a8506 100644 --- a/src/crypto.js +++ b/src/crypto.js @@ -3,8 +3,8 @@ 'use strict'; -var ARCFourCipher = (function arcFourCipher() { - function constructor(key) { +var ARCFourCipher = (function ARCFourCipherClosure() { + function ARCFourCipher(key) { this.a = 0; this.b = 0; var s = new Uint8Array(256); @@ -20,7 +20,7 @@ var ARCFourCipher = (function arcFourCipher() { this.s = s; } - constructor.prototype = { + ARCFourCipher.prototype = { encryptBlock: function arcFourCipherEncryptBlock(data) { var i, n = data.length, tmp, tmp2; var a = this.a, b = this.b, s = this.s; @@ -39,12 +39,12 @@ var ARCFourCipher = (function arcFourCipher() { return output; } }; - constructor.prototype.decryptBlock = constructor.prototype.encryptBlock; + ARCFourCipher.prototype.decryptBlock = ARCFourCipher.prototype.encryptBlock; - return constructor; + return ARCFourCipher; })(); -var calculateMD5 = (function calculateMD5() { +var calculateMD5 = (function calculateMD5Closure() { var r = new Uint8Array([ 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, @@ -128,20 +128,20 @@ var calculateMD5 = (function calculateMD5() { return hash; })(); -var NullCipher = (function nullCipher() { - function constructor() { +var NullCipher = (function NullCipherClosure() { + function NullCipher() { } - constructor.prototype = { + NullCipher.prototype = { decryptBlock: function nullCipherDecryptBlock(data) { return data; } }; - return constructor; + return NullCipher; })(); -var AES128Cipher = (function aes128Cipher() { +var AES128Cipher = (function AES128CipherClosure() { var rcon = new Uint8Array([ 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, @@ -330,7 +330,7 @@ var AES128Cipher = (function aes128Cipher() { return state; } - function constructor(key) { + function AES128Cipher(key) { this.key = expandKey128(key); this.buffer = new Uint8Array(16); this.bufferPosition = 0; @@ -370,7 +370,7 @@ var AES128Cipher = (function aes128Cipher() { return output; } - constructor.prototype = { + AES128Cipher.prototype = { decryptBlock: function aes128CipherDecryptBlock(data) { var i, sourceLength = data.length; var buffer = this.buffer, bufferLength = this.bufferPosition; @@ -391,15 +391,15 @@ var AES128Cipher = (function aes128Cipher() { } }; - return constructor; + return AES128Cipher; })(); -var CipherTransform = (function cipherTransform() { - function constructor(stringCipherConstructor, streamCipherConstructor) { +var CipherTransform = (function CipherTransformClosure() { + function CipherTransform(stringCipherConstructor, streamCipherConstructor) { this.stringCipherConstructor = stringCipherConstructor; this.streamCipherConstructor = streamCipherConstructor; } - constructor.prototype = { + CipherTransform.prototype = { createStream: function cipherTransformCreateStream(stream) { var cipher = new this.streamCipherConstructor(); return new DecryptStream(stream, @@ -415,10 +415,10 @@ var CipherTransform = (function cipherTransform() { return bytesToString(data); } }; - return constructor; + return CipherTransform; })(); -var CipherTransformFactory = (function cipherTransformFactory() { +var CipherTransformFactory = (function CipherTransformFactoryClosure() { function prepareKeyData(fileId, password, ownerPassword, userPassword, flags, revision, keyLength, encryptMetadata) { var defaultPasswordBytes = new Uint8Array([ @@ -490,7 +490,7 @@ var CipherTransformFactory = (function cipherTransformFactory() { var identityName = new Name('Identity'); - function constructor(dict, fileId, password) { + function CipherTransformFactory(dict, fileId, password) { var filter = dict.get('Filter'); if (!isName(filter) || filter.name != 'Standard') error('unknown encryption method'); @@ -573,7 +573,7 @@ var CipherTransformFactory = (function cipherTransformFactory() { return null; } - constructor.prototype = { + CipherTransformFactory.prototype = { createCipherTransform: function buildCipherCreateCipherTransform(num, gen) { if (this.algorithm == 4) { @@ -592,6 +592,6 @@ var CipherTransformFactory = (function cipherTransformFactory() { } }; - return constructor; + return CipherTransformFactory; })(); diff --git a/src/evaluator.js b/src/evaluator.js index 954c3bec3..edef57f91 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -3,8 +3,8 @@ 'use strict'; -var PartialEvaluator = (function partialEvaluator() { - function constructor(xref, handler, uniquePrefix) { +var PartialEvaluator = (function PartialEvaluatorClosure() { + function PartialEvaluator(xref, handler, uniquePrefix) { this.state = new EvalState(); this.stateStack = []; @@ -111,7 +111,7 @@ var PartialEvaluator = (function partialEvaluator() { EX: 'endCompat' }; - constructor.prototype = { + PartialEvaluator.prototype = { getIRQueue: function partialEvaluatorGetIRQueue(stream, resources, queue, dependency) { @@ -184,62 +184,52 @@ var PartialEvaluator = (function partialEvaluator() { var w = dict.get('Width', 'W'); var h = dict.get('Height', 'H'); - if (image instanceof JpegStream && image.isNative) { - var objId = 'img_' + uniquePrefix + (++self.objIdCounter); - handler.send('obj', [objId, 'JpegStream', image.getIR()]); + 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. - fn = 'paintJpegXObject'; - args = [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]); + args = [objId, w, h]; - if (imageObj.imageMask) { - throw 'Can\'t handle this in the web worker :/'; - } - - var imgData = { - width: w, - height: h, - data: new Uint8Array(w * h * 4) - }; - var pixels = imgData.data; - imageObj.fillRgbaBuffer(pixels, imageObj.decode); - - fn = 'paintImageXObject'; - args = [imgData]; + 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; } - // 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. - fn = 'paintImageMaskXObject'; + fn = 'paintImageXObject'; - 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 = [imgArray, inverseDecode, width, height]; + PDFImage.buildImage(function(imageObj) { + 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]); + }, handler, xref, resources, image, inline); } uniquePrefix = uniquePrefix || ''; @@ -858,11 +848,11 @@ var PartialEvaluator = (function partialEvaluator() { } }; - return constructor; + return PartialEvaluator; })(); -var EvalState = (function evalState() { - function constructor() { +var EvalState = (function EvalStateClosure() { + function EvalState() { // Are soft masks and alpha values shapes or opacities? this.alphaIsShape = false; this.fontSize = 0; @@ -879,8 +869,8 @@ var EvalState = (function evalState() { this.fillColorSpace = null; this.strokeColorSpace = null; } - constructor.prototype = { + EvalState.prototype = { }; - return constructor; + return EvalState; })(); diff --git a/src/fonts.js b/src/fonts.js index 672739ea4..83ce4abaa 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -3,8 +3,6 @@ 'use strict'; -var isWorker = (typeof window == 'undefined'); - /** * Maximum time to wait for a font to be loaded by font-face rules. */ @@ -733,8 +731,8 @@ function isSpecialUnicode(unicode) { * var type1Font = new Font("MyFontName", binaryFile, propertiesObject); * type1Font.bind(); */ -var Font = (function Font() { - var constructor = function font_constructor(name, file, properties) { +var Font = (function FontClosure() { + function Font(name, file, properties) { this.name = name; this.coded = properties.coded; this.charProcIRQueues = properties.charProcIRQueues; @@ -765,8 +763,10 @@ var Font = (function Font() { this.fontMatrix = properties.fontMatrix; this.widthMultiplier = 1.0; - if (properties.type == 'Type3') + if (properties.type == 'Type3') { + this.encoding = properties.baseEncoding; return; + } // Trying to fix encoding using glyph CIDSystemInfo. this.loadCidToUnicode(properties); @@ -884,6 +884,13 @@ var Font = (function Font() { String.fromCharCode(value & 0xff); }; + function safeString16(value) { + // clamp value to the 16-bit int range + value = value > 0x7FFF ? 0x7FFF : value < -0x8000 ? -0x8000 : value; + return String.fromCharCode((value >> 8) & 0xff) + + String.fromCharCode(value & 0xff); + }; + function string32(value) { return String.fromCharCode((value >> 24) & 0xff) + String.fromCharCode((value >> 16) & 0xff) + @@ -1222,7 +1229,7 @@ var Font = (function Font() { return nameTable; } - constructor.prototype = { + Font.prototype = { name: null, font: null, mimetype: null, @@ -1758,7 +1765,7 @@ var Font = (function Font() { var hasShortCmap = !!cmapTable.hasShortCmap; var toUnicode = this.toUnicode; - if (hasShortCmap && toUnicode) { + if (toUnicode && toUnicode.length > 0) { // checking if cmap is just identity map var isIdentity = true; for (var i = 0, ii = glyphs.length; i < ii; i++) { @@ -1769,15 +1776,38 @@ var Font = (function Font() { } // if it is, replacing with meaningful toUnicode values if (isIdentity) { + var usedUnicodes = [], unassignedUnicodeItems = []; for (var i = 0, ii = glyphs.length; i < ii; i++) { - var unicode = toUnicode[i + 1] || i + 1; + var unicode = toUnicode[i + 1]; + if (!unicode || unicode in usedUnicodes) { + unassignedUnicodeItems.push(i); + continue; + } glyphs[i].unicode = unicode; + usedUnicodes[unicode] = true; + } + var unusedUnicode = kCmapGlyphOffset; + for (var j = 0, jj = unassignedUnicodeItems.length; j < jj; j++) { + var i = unassignedUnicodeItems[j]; + while (unusedUnicode in usedUnicodes) + unusedUnicode++; + var cid = i + 1; + // override only if unicode mapping is not specified + if (!(cid in toUnicode)) + toUnicode[cid] = unusedUnicode; + glyphs[i].unicode = unusedUnicode++; } this.useToUnicode = true; } } properties.hasShortCmap = hasShortCmap; + // remove glyph references outside range of avaialable glyphs + for (var i = 0, ii = ids.length; i < ii; i++) { + if (ids[i] >= numGlyphs) + ids[i] = 0; + } + createGlyphNameMap(glyphs, ids, properties); this.glyphNameMap = properties.glyphNameMap; @@ -1903,9 +1933,9 @@ var Font = (function Font() { '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // creation date '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // modifification date '\x00\x00' + // xMin - string16(properties.descent) + // yMin + safeString16(properties.descent) + // yMin '\x0F\xFF' + // xMax - string16(properties.ascent) + // yMax + safeString16(properties.ascent) + // yMax string16(properties.italicAngle ? 2 : 0) + // macStyle '\x00\x11' + // lowestRecPPEM '\x00\x00' + // fontDirectionHint @@ -1917,15 +1947,15 @@ var Font = (function Font() { 'hhea': (function fontFieldsHhea() { return stringToArray( '\x00\x01\x00\x00' + // Version number - string16(properties.ascent) + // Typographic Ascent - string16(properties.descent) + // Typographic Descent + safeString16(properties.ascent) + // Typographic Ascent + safeString16(properties.descent) + // Typographic Descent '\x00\x00' + // Line Gap '\xFF\xFF' + // advanceWidthMax '\x00\x00' + // minLeftSidebearing '\x00\x00' + // minRightSidebearing '\x00\x00' + // xMaxExtent - string16(properties.capHeight) + // caretSlopeRise - string16(Math.tan(properties.italicAngle) * + safeString16(properties.capHeight) + // caretSlopeRise + safeString16(Math.tan(properties.italicAngle) * properties.xHeight) + // caretSlopeRun '\x00\x00' + // caretOffset '\x00\x00' + // -reserved- @@ -2071,6 +2101,37 @@ var Font = (function Font() { return rule; }, + get spaceWidth() { + // trying to estimate space character width + var possibleSpaceReplacements = ['space', 'minus', 'one', 'i']; + var width; + for (var i = 0, ii = possibleSpaceReplacements.length; i < ii; i++) { + var glyphName = possibleSpaceReplacements[i]; + // if possible, getting width by glyph name + if (glyphName in this.widths) { + width = this.widths[glyphName]; + break; + } + var glyphUnicode = GlyphsUnicode[glyphName]; + // finding the charcode via unicodeToCID map + var charcode = 0; + if (this.composite) + charcode = this.unicodeToCID[glyphUnicode]; + // ... via toUnicode map + if (!charcode && 'toUnicode' in this) + charcode = this.toUnicode.indexOf(glyphUnicode); + // setting it to unicode if negative or undefined + if (!(charcode > 0)) + charcode = glyphUnicode; + // trying to get width via charcode + width = this.widths[charcode]; + if (width) + break; // the non-zero width found + } + width = (width || this.defaultWidth) * this.widthMultiplier; + return shadow(this, 'spaceWidth', width); + }, + charToGlyph: function fonts_charToGlyph(charcode) { var unicode, width, codeIRQueue; @@ -2095,9 +2156,9 @@ var Font = (function Font() { break; case 'Type1': var glyphName = this.differences[charcode] || this.encoding[charcode]; + if (!isNum(width)) + width = this.widths[glyphName]; if (this.noUnicodeAdaptation) { - if (!isNum(width)) - width = this.widths[glyphName]; unicode = GlyphsUnicode[glyphName] || charcode; break; } @@ -2110,6 +2171,10 @@ var Font = (function Font() { unicode = charcode; break; case 'TrueType': + if (this.useToUnicode) { + unicode = this.toUnicode[charcode] || charcode; + break; + } var glyphName = this.differences[charcode] || this.encoding[charcode]; if (!glyphName) glyphName = Encodings.StandardEncoding[charcode]; @@ -2138,7 +2203,8 @@ var Font = (function Font() { break; } - var unicodeChars = this.toUnicode ? this.toUnicode[charcode] : charcode; + var unicodeChars = !('toUnicode' in this) ? charcode : + this.toUnicode[charcode] || charcode; if (typeof unicodeChars === 'number') unicodeChars = String.fromCharCode(unicodeChars); @@ -2152,7 +2218,7 @@ var Font = (function Font() { }; }, - charsToGlyphs: function fonts_chars2Glyphs(chars) { + charsToGlyphs: function fonts_charsToGlyphs(chars) { var charsCache = this.charsCache; var glyphs; @@ -2200,7 +2266,7 @@ var Font = (function Font() { } }; - return constructor; + return Font; })(); /* @@ -3110,9 +3176,9 @@ CFF.prototype = { } }; -var Type2CFF = (function type2CFF() { +var Type2CFF = (function Type2CFFClosure() { // TODO: replace parsing code with the Type2Parser in font_utils.js - function constructor(file, properties) { + function Type2CFF(file, properties) { var bytes = file.getBytes(); this.bytes = bytes; this.properties = properties; @@ -3120,7 +3186,7 @@ var Type2CFF = (function type2CFF() { this.data = this.parse(); } - constructor.prototype = { + Type2CFF.prototype = { parse: function cff_parse() { var header = this.parseHeader(); var properties = this.properties; @@ -3664,6 +3730,6 @@ var Type2CFF = (function type2CFF() { } }; - return constructor; + return Type2CFF; })(); diff --git a/src/function.js b/src/function.js index ef24736c1..6b0063218 100644 --- a/src/function.js +++ b/src/function.js @@ -3,7 +3,7 @@ 'use strict'; -var PDFFunction = (function pdfFunction() { +var PDFFunction = (function PDFFunctionClosure() { var CONSTRUCT_SAMPLED = 0; var CONSTRUCT_INTERPOLATED = 2; var CONSTRUCT_STICHED = 3; diff --git a/src/image.js b/src/image.js index 17ef7b06d..987542c58 100644 --- a/src/image.js +++ b/src/image.js @@ -3,8 +3,28 @@ 'use strict'; -var PDFImage = (function pdfImage() { - function constructor(xref, res, image, inline) { +var PDFImage = (function PDFImageClosure() { + /** + * 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 PDFImage(xref, res, image, inline, smask) { this.image = image; if (image.getParams) { // JPX/JPEG2000 streams directly contain bits per component @@ -51,16 +71,39 @@ 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.smask = new PDFImage(xref, res, smask); + 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. + */ + PDFImage.buildImage = function buildImage(callback, handler, xref, res, + image, inline) { + var imageDataPromise = new Promise(); + var smaskPromise = new Promise(); + // The image data and smask data may not be ready yet, wait till both are + // resolved. + 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); + }); - constructor.prototype = { + handleImageData(handler, xref, res, image, imageDataPromise); + + var smask = xref.fetchIfRef(image.dict.get('SMask')); + if (smask) + handleImageData(handler, xref, res, smask, smaskPromise); + else + smaskPromise.resolve(null); + }; + + PDFImage.prototype = { getComponents: function getComponents(buffer, decodeMap) { var bpc = this.bpc; if (bpc == 8) @@ -130,18 +173,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) @@ -159,8 +190,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 @@ -188,8 +218,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); @@ -216,42 +245,27 @@ 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; for (var i = 0; i < length; ++i) buffer[i] = comps[i]; + }, + getImageBytes: function getImageBytes(length) { + this.image.reset(); + return this.image.getBytes(length); } }; - return constructor; + return PDFImage; })(); -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; -})(); +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/obj.js b/src/obj.js index 474b54336..2f7488a76 100644 --- a/src/obj.js +++ b/src/obj.js @@ -3,34 +3,34 @@ 'use strict'; -var Name = (function nameName() { - function constructor(name) { +var Name = (function NameClosure() { + function Name(name) { this.name = name; } - constructor.prototype = { + Name.prototype = { }; - return constructor; + return Name; })(); -var Cmd = (function cmdCmd() { - function constructor(cmd) { +var Cmd = (function CmdClosure() { + function Cmd(cmd) { this.cmd = cmd; } - constructor.prototype = { + Cmd.prototype = { }; - return constructor; + return Cmd; })(); -var Dict = (function dictDict() { - function constructor() { +var Dict = (function DictClosure() { + function Dict() { this.map = Object.create(null); } - constructor.prototype = { + Dict.prototype = { get: function dictGet(key1, key2, key3) { var value; if (typeof (value = this.map[key1]) != 'undefined' || key1 in this.map || @@ -60,29 +60,29 @@ var Dict = (function dictDict() { } }; - return constructor; + return Dict; })(); -var Ref = (function refRef() { - function constructor(num, gen) { +var Ref = (function RefClosure() { + function Ref(num, gen) { this.num = num; this.gen = gen; } - constructor.prototype = { + Ref.prototype = { }; - return constructor; + return Ref; })(); // The reference is identified by number and generation, // this structure stores only one instance of the reference. -var RefSet = (function refSet() { - function constructor() { +var RefSet = (function RefSetClosure() { + function RefSet() { this.dict = {}; } - constructor.prototype = { + RefSet.prototype = { has: function refSetHas(ref) { return !!this.dict['R' + ref.num + '.' + ref.gen]; }, @@ -92,18 +92,18 @@ var RefSet = (function refSet() { } }; - return constructor; + return RefSet; })(); -var Catalog = (function catalogCatalog() { - function constructor(xref) { +var Catalog = (function CatalogClosure() { + function Catalog(xref) { this.xref = xref; var obj = xref.getCatalogObj(); assertWellFormed(isDict(obj), 'catalog object is not a dictionary'); this.catDict = obj; } - constructor.prototype = { + Catalog.prototype = { get toplevelPagesDict() { var pagesObj = this.catDict.get('Pages'); assertWellFormed(isRef(pagesObj), 'invalid top-level pages reference'); @@ -253,11 +253,11 @@ var Catalog = (function catalogCatalog() { } }; - return constructor; + return Catalog; })(); -var XRef = (function xRefXRef() { - function constructor(stream, startXRef, mainXRefEntriesOffset) { +var XRef = (function XRefClosure() { + function XRef(stream, startXRef, mainXRefEntriesOffset) { this.stream = stream; this.entries = []; this.xrefstms = {}; @@ -278,7 +278,7 @@ var XRef = (function xRefXRef() { error('Invalid root reference'); } - constructor.prototype = { + XRef.prototype = { readXRefTable: function readXRefTable(parser) { var obj; while (true) { @@ -598,7 +598,7 @@ var XRef = (function xRefXRef() { e = parser.getObj(); } // Don't cache streams since they are mutable (except images). - if (!isStream(e) || e.getImage) + if (!isStream(e) || e instanceof JpegStream) this.cache[num] = e; return e; } @@ -642,7 +642,7 @@ var XRef = (function xRefXRef() { } }; - return constructor; + return XRef; })(); /** @@ -651,7 +651,7 @@ var XRef = (function xRefXRef() { * inside of a worker. The `PDFObjects` implements some basic functions to * manage these objects. */ -var PDFObjects = (function pdfObjects() { +var PDFObjects = (function PDFObjectsClosure() { function PDFObjects() { this.objs = {}; } diff --git a/src/parser.js b/src/parser.js index 036191677..695438379 100644 --- a/src/parser.js +++ b/src/parser.js @@ -9,8 +9,8 @@ function isEOF(v) { return v == EOF; } -var Parser = (function parserParser() { - function constructor(lexer, allowStreams, xref) { +var Parser = (function ParserClosure() { + function Parser(lexer, allowStreams, xref) { this.lexer = lexer; this.allowStreams = allowStreams; this.xref = xref; @@ -18,7 +18,7 @@ var Parser = (function parserParser() { this.refill(); } - constructor.prototype = { + Parser.prototype = { refill: function parserRefill() { this.buf1 = this.lexer.getObj(); this.buf2 = this.lexer.getObj(); @@ -249,20 +249,20 @@ var Parser = (function parserParser() { if (name == 'CCITTFaxDecode' || name == 'CCF') { return new CCITTFaxStream(stream, params); } - TODO('filter "' + name + '" not supported yet'); + warn('filter "' + name + '" not supported yet'); return stream; } }; - return constructor; + return Parser; })(); -var Lexer = (function lexer() { - function constructor(stream) { +var Lexer = (function LexerClosure() { + function Lexer(stream) { this.stream = stream; } - constructor.isSpace = function lexerIsSpace(ch) { + Lexer.isSpace = function lexerIsSpace(ch) { return ch == ' ' || ch == '\t' || ch == '\x0d' || ch == '\x0a'; }; @@ -296,7 +296,7 @@ var Lexer = (function lexer() { return -1; } - constructor.prototype = { + Lexer.prototype = { getNumber: function lexerGetNumber(ch) { var floating = false; var str = ch; @@ -558,11 +558,11 @@ var Lexer = (function lexer() { } }; - return constructor; + return Lexer; })(); -var Linearization = (function linearizationLinearization() { - function constructor(stream) { +var Linearization = (function LinearizationClosure() { + function Linearization(stream) { this.parser = new Parser(new Lexer(stream), false); var obj1 = this.parser.getObj(); var obj2 = this.parser.getObj(); @@ -576,7 +576,7 @@ var Linearization = (function linearizationLinearization() { } } - constructor.prototype = { + Linearization.prototype = { getInt: function linearizationGetInt(name) { var linDict = this.linDict; var obj; @@ -635,6 +635,6 @@ var Linearization = (function linearizationLinearization() { } }; - return constructor; + return Linearization; })(); diff --git a/src/pattern.js b/src/pattern.js index 9e2dca17a..dbe2e5c23 100644 --- a/src/pattern.js +++ b/src/pattern.js @@ -8,13 +8,13 @@ var PatternType = { RADIAL: 3 }; -var Pattern = (function patternPattern() { +var Pattern = (function PatternClosure() { // Constructor should define this.getPattern - function constructor() { + function Pattern() { error('should not call Pattern constructor'); } - constructor.prototype = { + Pattern.prototype = { // Input: current Canvas context // Output: the appropriate fillStyle or strokeStyle getPattern: function pattern_getStyle(ctx) { @@ -22,11 +22,11 @@ var Pattern = (function patternPattern() { } }; - constructor.shadingFromIR = function pattern_shadingFromIR(ctx, raw) { + Pattern.shadingFromIR = function pattern_shadingFromIR(ctx, raw) { return Shadings[raw[0]].fromIR(ctx, raw); }; - constructor.parseShading = function pattern_shading(shading, matrix, xref, + Pattern.parseShading = function pattern_shading(shading, matrix, xref, res, ctx) { var dict = isStream(shading) ? shading.dict : shading; @@ -41,15 +41,15 @@ var Pattern = (function patternPattern() { return new Shadings.Dummy(); } }; - return constructor; + return Pattern; })(); var Shadings = {}; // Radial and axial shading have very similar implementations // If needed, the implementations can be broken into two classes -Shadings.RadialAxial = (function radialAxialShading() { - function constructor(dict, matrix, xref, res, ctx) { +Shadings.RadialAxial = (function RadialAxialClosure() { + function RadialAxial(dict, matrix, xref, res, ctx) { this.matrix = matrix; this.coordsArr = dict.get('Coords'); this.shadingType = dict.get('ShadingType'); @@ -102,7 +102,7 @@ Shadings.RadialAxial = (function radialAxialShading() { this.colorStops = colorStops; } - constructor.fromIR = function radialAxialShadingGetIR(ctx, raw) { + RadialAxial.fromIR = function radialAxialShadingGetIR(ctx, raw) { var type = raw[1]; var colorStops = raw[2]; var p0 = raw[3]; @@ -134,7 +134,7 @@ Shadings.RadialAxial = (function radialAxialShading() { return grad; }; - constructor.prototype = { + RadialAxial.prototype = { getIR: function radialAxialShadingGetIR() { var coordsArr = this.coordsArr; var type = this.shadingType; @@ -162,28 +162,32 @@ Shadings.RadialAxial = (function radialAxialShading() { } }; - return constructor; + return RadialAxial; })(); -Shadings.Dummy = (function dummyShading() { - function constructor() { +Shadings.Dummy = (function DummyClosure() { + function Dummy() { this.type = 'Pattern'; } - constructor.fromIR = function dummyShadingFromIR() { + Dummy.fromIR = function dummyShadingFromIR() { return 'hotpink'; }; - constructor.prototype = { + Dummy.prototype = { getIR: function dummyShadingGetIR() { return ['Dummy']; } }; - return constructor; + return Dummy; })(); -var TilingPattern = (function tilingPattern() { - var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2; +var TilingPattern = (function TilingPatternClosure() { + var PaintType = { + COLORED: 1, + UNCOLORED: 2 + }; + var MAX_PATTERN_SIZE = 512; function TilingPattern(IR, color, ctx, objs) { var IRQueue = IR[2]; @@ -209,13 +213,13 @@ var TilingPattern = (function tilingPattern() { var width = botRight[0] - topLeft[0]; var height = botRight[1] - topLeft[1]; - // TODO: hack to avoid OOM, we would idealy compute the tiling + // TODO: hack to avoid OOM, we would ideally compute the tiling // pattern to be only as large as the acual size in device space // This could be computed with .mozCurrentTransform, but still // needs to be implemented - while (Math.abs(width) > 512 || Math.abs(height) > 512) { - width = 512; - height = 512; + while (Math.abs(width) > MAX_PATTERN_SIZE || + Math.abs(height) > MAX_PATTERN_SIZE) { + width = height = MAX_PATTERN_SIZE; } var tmpCanvas = new ScratchCanvas(width, height); @@ -225,11 +229,11 @@ var TilingPattern = (function tilingPattern() { var graphics = new CanvasGraphics(tmpCtx, objs); switch (paintType) { - case PAINT_TYPE_COLORED: + case PaintType.COLORED: tmpCtx.fillStyle = ctx.fillStyle; tmpCtx.strokeStyle = ctx.strokeStyle; break; - case PAINT_TYPE_UNCOLORED: + case PaintType.UNCOLORED: color = Util.makeCssRgb.apply(this, color); tmpCtx.fillStyle = color; tmpCtx.strokeStyle = color; diff --git a/src/stream.js b/src/stream.js index 559fb2ca2..d996f5c91 100644 --- a/src/stream.js +++ b/src/stream.js @@ -3,8 +3,8 @@ 'use strict'; -var Stream = (function streamStream() { - function constructor(arrayBuffer, start, length, dict) { +var Stream = (function StreamClosure() { + function Stream(arrayBuffer, start, length, dict) { this.bytes = new Uint8Array(arrayBuffer); this.start = start || 0; this.pos = this.start; @@ -14,7 +14,7 @@ var Stream = (function streamStream() { // required methods for a stream. if a particular stream does not // implement these, an error should be thrown - constructor.prototype = { + Stream.prototype = { get length() { return this.end - this.start; }, @@ -67,11 +67,11 @@ var Stream = (function streamStream() { isStream: true }; - return constructor; + return Stream; })(); -var StringStream = (function stringStream() { - function constructor(str) { +var StringStream = (function StringStreamClosure() { + function StringStream(str) { var length = str.length; var bytes = new Uint8Array(length); for (var n = 0; n < length; ++n) @@ -79,21 +79,21 @@ var StringStream = (function stringStream() { Stream.call(this, bytes); } - constructor.prototype = Stream.prototype; + StringStream.prototype = Stream.prototype; - return constructor; + return StringStream; })(); // super class for the decoding streams -var DecodeStream = (function decodeStream() { - function constructor() { +var DecodeStream = (function DecodeStreamClosure() { + function DecodeStream() { this.pos = 0; this.bufferLength = 0; this.eof = false; this.buffer = null; } - constructor.prototype = { + DecodeStream.prototype = { ensureBuffer: function decodestream_ensureBuffer(requested) { var buffer = this.buffer; var current = buffer ? buffer.byteLength : 0; @@ -178,24 +178,24 @@ var DecodeStream = (function decodeStream() { } }; - return constructor; + return DecodeStream; })(); -var FakeStream = (function fakeStream() { - function constructor(stream) { +var FakeStream = (function FakeStreamClosure() { + function FakeStream(stream) { this.dict = stream.dict; DecodeStream.call(this); } - constructor.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBlock = function fakeStreamReadBlock() { + FakeStream.prototype = Object.create(DecodeStream.prototype); + FakeStream.prototype.readBlock = function fakeStreamReadBlock() { var bufferLength = this.bufferLength; bufferLength += 1024; var buffer = this.ensureBuffer(bufferLength); this.bufferLength = bufferLength; }; - constructor.prototype.getBytes = function fakeStreamGetBytes(length) { + FakeStream.prototype.getBytes = function fakeStreamGetBytes(length) { var end, pos = this.pos; if (length) { @@ -217,18 +217,20 @@ var FakeStream = (function fakeStream() { return this.buffer.subarray(pos, end); }; - return constructor; + return FakeStream; })(); -var StreamsSequenceStream = (function streamSequenceStream() { - function constructor(streams) { +var StreamsSequenceStream = (function StreamsSequenceStreamClosure() { + function StreamsSequenceStream(streams) { this.streams = streams; DecodeStream.call(this); } - constructor.prototype = Object.create(DecodeStream.prototype); + StreamsSequenceStream.prototype = Object.create(DecodeStream.prototype); + + StreamsSequenceStream.prototype.readBlock = + function streamSequenceStreamReadBlock() { - constructor.prototype.readBlock = function streamSequenceStreamReadBlock() { var streams = this.streams; if (streams.length == 0) { this.eof = true; @@ -243,10 +245,10 @@ var StreamsSequenceStream = (function streamSequenceStream() { this.bufferLength = newLength; }; - return constructor; + return StreamsSequenceStream; })(); -var FlateStream = (function flateStream() { +var FlateStream = (function FlateStreamClosure() { var codeLenCodeMap = new Uint32Array([ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]); @@ -339,7 +341,7 @@ var FlateStream = (function flateStream() { 0x50003, 0x50013, 0x5000b, 0x5001b, 0x50007, 0x50017, 0x5000f, 0x00000 ]), 5]; - function constructor(stream) { + function FlateStream(stream) { var bytes = stream.getBytes(); var bytesPos = 0; @@ -364,9 +366,9 @@ var FlateStream = (function flateStream() { DecodeStream.call(this); } - constructor.prototype = Object.create(DecodeStream.prototype); + FlateStream.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.getBits = function flateStreamGetBits(bits) { + FlateStream.prototype.getBits = function flateStreamGetBits(bits) { var codeSize = this.codeSize; var codeBuf = this.codeBuf; var bytes = this.bytes; @@ -386,7 +388,7 @@ var FlateStream = (function flateStream() { return b; }; - constructor.prototype.getCode = function flateStreamGetCode(table) { + FlateStream.prototype.getCode = function flateStreamGetCode(table) { var codes = table[0]; var maxLen = table[1]; var codeSize = this.codeSize; @@ -412,7 +414,7 @@ var FlateStream = (function flateStream() { return codeVal; }; - constructor.prototype.generateHuffmanTable = + FlateStream.prototype.generateHuffmanTable = function flateStreamGenerateHuffmanTable(lengths) { var n = lengths.length; @@ -451,7 +453,7 @@ var FlateStream = (function flateStream() { return [codes, maxLen]; }; - constructor.prototype.readBlock = function flateStreamReadBlock() { + FlateStream.prototype.readBlock = function flateStreamReadBlock() { // read block header var hdr = this.getBits(3); if (hdr & 1) @@ -582,11 +584,11 @@ var FlateStream = (function flateStream() { } }; - return constructor; + return FlateStream; })(); -var PredictorStream = (function predictorStream() { - function constructor(stream, params) { +var PredictorStream = (function PredictorStreamClosure() { + function PredictorStream(stream, params) { var predictor = this.predictor = params.get('Predictor') || 1; if (predictor <= 1) @@ -613,9 +615,9 @@ var PredictorStream = (function predictorStream() { return this; } - constructor.prototype = Object.create(DecodeStream.prototype); + PredictorStream.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBlockTiff = + PredictorStream.prototype.readBlockTiff = function predictorStreamReadBlockTiff() { var rowBytes = this.rowBytes; @@ -676,7 +678,9 @@ var PredictorStream = (function predictorStream() { this.bufferLength += rowBytes; }; - constructor.prototype.readBlockPng = function predictorStreamReadBlockPng() { + PredictorStream.prototype.readBlockPng = + function predictorStreamReadBlockPng() { + var rowBytes = this.rowBytes; var pixBytes = this.pixBytes; @@ -753,7 +757,7 @@ var PredictorStream = (function predictorStream() { this.bufferLength += rowBytes; }; - return constructor; + return PredictorStream; })(); /** @@ -763,7 +767,7 @@ var PredictorStream = (function predictorStream() { * a library to decode these images and the stream behaves like all the other * DecodeStreams. */ -var JpegStream = (function jpegStream() { +var JpegStream = (function JpegStreamClosure() { function isAdobeImage(bytes) { var maxBytesScanned = Math.max(bytes.length - 16, 1024); // Looking for APP14, 'Adobe' @@ -794,7 +798,7 @@ var JpegStream = (function jpegStream() { return newBytes; } - function constructor(bytes, dict, xref) { + function JpegStream(bytes, dict, xref) { // TODO: per poppler, some images may have 'junk' before that // need to be removed this.dict = dict; @@ -825,9 +829,9 @@ var JpegStream = (function jpegStream() { DecodeStream.call(this); } - constructor.prototype = Object.create(DecodeStream.prototype); + JpegStream.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.ensureBuffer = function jpegStreamEnsureBuffer(req) { + JpegStream.prototype.ensureBuffer = function jpegStreamEnsureBuffer(req) { if (this.bufferLength) return; var jpegImage = new JpegImage(); @@ -839,18 +843,18 @@ var JpegStream = (function jpegStream() { this.buffer = data; this.bufferLength = data.length; }; - constructor.prototype.getIR = function jpegStreamGetIR() { + JpegStream.prototype.getIR = function jpegStreamGetIR() { return this.src; }; - constructor.prototype.getChar = function jpegStreamGetChar() { + JpegStream.prototype.getChar = function jpegStreamGetChar() { error('internal error: getChar is not valid on JpegStream'); }; - return constructor; + return JpegStream; })(); -var DecryptStream = (function decryptStream() { - function constructor(str, decrypt) { +var DecryptStream = (function DecryptStreamClosure() { + function DecryptStream(str, decrypt) { this.str = str; this.dict = str.dict; this.decrypt = decrypt; @@ -860,9 +864,9 @@ var DecryptStream = (function decryptStream() { var chunkSize = 512; - constructor.prototype = Object.create(DecodeStream.prototype); + DecryptStream.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBlock = function decryptStreamReadBlock() { + DecryptStream.prototype.readBlock = function decryptStreamReadBlock() { var chunk = this.str.getBytes(chunkSize); if (!chunk || chunk.length == 0) { this.eof = true; @@ -879,11 +883,11 @@ var DecryptStream = (function decryptStream() { this.bufferLength = bufferLength; }; - return constructor; + return DecryptStream; })(); -var Ascii85Stream = (function ascii85Stream() { - function constructor(str) { +var Ascii85Stream = (function Ascii85StreamClosure() { + function Ascii85Stream(str) { this.str = str; this.dict = str.dict; this.input = new Uint8Array(5); @@ -891,9 +895,9 @@ var Ascii85Stream = (function ascii85Stream() { DecodeStream.call(this); } - constructor.prototype = Object.create(DecodeStream.prototype); + Ascii85Stream.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBlock = function ascii85StreamReadBlock() { + Ascii85Stream.prototype.readBlock = function ascii85StreamReadBlock() { var tildaCode = '~'.charCodeAt(0); var zCode = 'z'.charCodeAt(0); var str = this.str; @@ -948,11 +952,11 @@ var Ascii85Stream = (function ascii85Stream() { } }; - return constructor; + return Ascii85Stream; })(); -var AsciiHexStream = (function asciiHexStream() { - function constructor(str) { +var AsciiHexStream = (function AsciiHexStreamClosure() { + function AsciiHexStream(str) { this.str = str; this.dict = str.dict; @@ -986,9 +990,9 @@ var AsciiHexStream = (function asciiHexStream() { 102: 15 }; - constructor.prototype = Object.create(DecodeStream.prototype); + AsciiHexStream.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBlock = function asciiHexStreamReadBlock() { + AsciiHexStream.prototype.readBlock = function asciiHexStreamReadBlock() { var gtCode = '>'.charCodeAt(0), bytes = this.str.getBytes(), c, n, decodeLength, buffer, bufferLength, i, length; @@ -1018,10 +1022,10 @@ var AsciiHexStream = (function asciiHexStream() { this.eof = true; }; - return constructor; + return AsciiHexStream; })(); -var CCITTFaxStream = (function ccittFaxStream() { +var CCITTFaxStream = (function CCITTFaxStreamClosure() { var ccittEOL = -2; var twoDimPass = 0; @@ -1449,7 +1453,7 @@ var CCITTFaxStream = (function ccittFaxStream() { [2, 2], [2, 2], [2, 2], [2, 2] ]; - function constructor(str, params) { + function CCITTFaxStream(str, params) { this.str = str; this.dict = str.dict; @@ -1494,9 +1498,9 @@ var CCITTFaxStream = (function ccittFaxStream() { DecodeStream.call(this); } - constructor.prototype = Object.create(DecodeStream.prototype); + CCITTFaxStream.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBlock = function ccittFaxStreamReadBlock() { + CCITTFaxStream.prototype.readBlock = function ccittFaxStreamReadBlock() { while (!this.eof) { var c = this.lookChar(); this.buf = EOF; @@ -1505,7 +1509,7 @@ var CCITTFaxStream = (function ccittFaxStream() { } }; - constructor.prototype.addPixels = + CCITTFaxStream.prototype.addPixels = function ccittFaxStreamAddPixels(a1, blackPixels) { var codingLine = this.codingLine; var codingPos = this.codingPos; @@ -1525,7 +1529,7 @@ var CCITTFaxStream = (function ccittFaxStream() { this.codingPos = codingPos; }; - constructor.prototype.addPixelsNeg = + CCITTFaxStream.prototype.addPixelsNeg = function ccittFaxStreamAddPixelsNeg(a1, blackPixels) { var codingLine = this.codingLine; var codingPos = this.codingPos; @@ -1554,7 +1558,7 @@ var CCITTFaxStream = (function ccittFaxStream() { this.codingPos = codingPos; }; - constructor.prototype.lookChar = function ccittFaxStreamLookChar() { + CCITTFaxStream.prototype.lookChar = function ccittFaxStreamLookChar() { if (this.buf != EOF) return this.buf; @@ -1873,7 +1877,9 @@ var CCITTFaxStream = (function ccittFaxStream() { return [false, 0, false]; }; - constructor.prototype.getTwoDimCode = function ccittFaxStreamGetTwoDimCode() { + CCITTFaxStream.prototype.getTwoDimCode = + function ccittFaxStreamGetTwoDimCode() { + var code = 0; var p; if (this.eoblock) { @@ -1892,7 +1898,9 @@ var CCITTFaxStream = (function ccittFaxStream() { return EOF; }; - constructor.prototype.getWhiteCode = function ccittFaxStreamGetWhiteCode() { + CCITTFaxStream.prototype.getWhiteCode = + function ccittFaxStreamGetWhiteCode() { + var code = 0; var p; var n; @@ -1924,7 +1932,9 @@ var CCITTFaxStream = (function ccittFaxStream() { return 1; }; - constructor.prototype.getBlackCode = function ccittFaxStreamGetBlackCode() { + CCITTFaxStream.prototype.getBlackCode = + function ccittFaxStreamGetBlackCode() { + var code, p; if (this.eoblock) { code = this.lookBits(13); @@ -1959,7 +1969,7 @@ var CCITTFaxStream = (function ccittFaxStream() { return 1; }; - constructor.prototype.lookBits = function ccittFaxStreamLookBits(n) { + CCITTFaxStream.prototype.lookBits = function ccittFaxStreamLookBits(n) { var c; while (this.inputBits < n) { if ((c = this.str.getByte()) == null) { @@ -1974,16 +1984,16 @@ var CCITTFaxStream = (function ccittFaxStream() { return (this.inputBuf >> (this.inputBits - n)) & (0xFFFF >> (16 - n)); }; - constructor.prototype.eatBits = function ccittFaxStreamEatBits(n) { + CCITTFaxStream.prototype.eatBits = function ccittFaxStreamEatBits(n) { if ((this.inputBits -= n) < 0) this.inputBits = 0; }; - return constructor; + return CCITTFaxStream; })(); -var LZWStream = (function lzwStream() { - function constructor(str, earlyChange) { +var LZWStream = (function LZWStreamClosure() { + function LZWStream(str, earlyChange) { this.str = str; this.dict = str.dict; this.cachedData = 0; @@ -2009,9 +2019,9 @@ var LZWStream = (function lzwStream() { DecodeStream.call(this); } - constructor.prototype = Object.create(DecodeStream.prototype); + LZWStream.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBits = function lzwStreamReadBits(n) { + LZWStream.prototype.readBits = function lzwStreamReadBits(n) { var bitsCached = this.bitsCached; var cachedData = this.cachedData; while (bitsCached < n) { @@ -2029,7 +2039,7 @@ var LZWStream = (function lzwStream() { return (cachedData >>> bitsCached) & ((1 << n) - 1); }; - constructor.prototype.readBlock = function lzwStreamReadBlock() { + LZWStream.prototype.readBlock = function lzwStreamReadBlock() { var blockSize = 512; var estimatedDecodedSize = blockSize * 2, decodedSizeDelta = blockSize; var i, j, q; @@ -2108,6 +2118,6 @@ var LZWStream = (function lzwStream() { this.bufferLength = currentBufferLength; }; - return constructor; + return LZWStream; })(); diff --git a/src/util.js b/src/util.js index 4fb96f062..57dbca4bb 100644 --- a/src/util.js +++ b/src/util.js @@ -76,24 +76,24 @@ function stringToBytes(str) { var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; -var Util = (function utilUtil() { - function constructor() {} - constructor.makeCssRgb = function makergb(r, g, b) { +var Util = (function UtilClosure() { + function Util() {} + Util.makeCssRgb = function makergb(r, g, b) { var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0; return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; }; - constructor.makeCssCmyk = function makecmyk(c, m, y, k) { + Util.makeCssCmyk = function makecmyk(c, m, y, k) { c = (new DeviceCmykCS()).getRgb([c, m, y, k]); var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0; return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; }; - constructor.applyTransform = function apply(p, m) { + Util.applyTransform = function apply(p, m) { var xt = p[0] * m[0] + p[1] * m[2] + m[4]; var yt = p[0] * m[1] + p[1] * m[3] + m[5]; return [xt, yt]; }; - return constructor; + return Util; })(); var PDFStringTranslateTable = [ @@ -197,7 +197,7 @@ function isPDFFunction(v) { * can be set. If any of these happens twice or the data is required before * it was set, an exception is throw. */ -var Promise = (function promise() { +var Promise = (function PromiseClosure() { var EMPTY_PROMISE = {}; /** @@ -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 {Promise[]} promises Array of promises to wait for. + * @return {Promise} New dependant 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, diff --git a/src/worker.js b/src/worker.js index 8e4c14fbc..c18de65ad 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; + var callbacks = this.callbacks = {}; var ah = this.actionHandler = {}; ah['console_log'] = [function ahConsoleLog(data) { @@ -14,11 +16,33 @@ function MessageHandler(name, comObj) { ah['console_error'] = [function ahConsoleError(data) { console.error.apply(console, data); }]; + comObj.onmessage = function messageHandlerComObjOnMessage(event) { var data = event.data; - if (data.action in ah) { + if (data.isReply) { + var callbackId = data.callbackId; + if (data.callbackId in callbacks) { + var callback = callbacks[callbackId]; + delete callbacks[callbackId]; + callback(data.data); + } else { + throw 'Cannot resolve callback ' + callbackId; + } + } else if (data.action in ah) { var action = ah[data.action]; - action[0].call(action[1], data.data); + 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; } @@ -33,12 +57,23 @@ MessageHandler.prototype = { } ah[actionName] = [handler, scope]; }, - - send: function messageHandlerSend(actionName, data) { - this.comObj.postMessage({ + /** + * 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, data: data - }); + }; + if (callback) { + var callbackId = this.callbackIndex++; + this.callbacks[callbackId] = callback; + message.callbackId = callbackId; + } + this.comObj.postMessage(message); } }; @@ -83,8 +118,8 @@ var WorkerMessageHandler = { } catch (e) { // Turn the error into an obj that can be serialized e = { - message: e.message, - stack: e.stack + message: typeof e === 'object' ? e.message : e, + stack: typeof e === 'object' ? e.stack : null }; handler.send('page_error', { pageNum: pageNum, diff --git a/test/driver.js b/test/driver.js index ffaf0b53a..64fceee90 100644 --- a/test/driver.js +++ b/test/driver.js @@ -139,6 +139,11 @@ function nextPage(task, loadError) { if (task.skipPages && task.skipPages.indexOf(task.pageNum) >= 0) { log(' skipping page ' + task.pageNum + '/' + task.pdfDoc.numPages + '... '); + // empty the canvas + canvas.width = 1; + canvas.height = 1; + clear(canvas.getContext('2d')); + snapshotCurrentPage(task, ''); return; } @@ -160,6 +165,10 @@ function nextPage(task, loadError) { canvas.height = pageHeight * pdfToCssUnitsCoef; clear(ctx); + // using non-attached to the document div to test + // text layer creation operations + var textLayer = document.createElement('div'); + page.startRendering( ctx, function nextPageStartRendering(error) { @@ -167,7 +176,8 @@ function nextPage(task, loadError) { if (error) failureMessage = 'render : ' + error.message; snapshotCurrentPage(task, failureMessage); - } + }, + textLayer ); } catch (e) { failure = 'page setup : ' + e.toString(); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 325987de6..23ba6340e 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -17,3 +17,6 @@ !devicen.pdf !cmykjpeg.pdf !issue840.pdf +!scan-bad.pdf +!freeculture.pdf +!issue918.pdf 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/pdfs/bpl13210.pdf.link b/test/pdfs/bpl13210.pdf.link new file mode 100644 index 000000000..7cde56a22 --- /dev/null +++ b/test/pdfs/bpl13210.pdf.link @@ -0,0 +1 @@ +http://h20000.www2.hp.com/bc/docs/support/SupportManual/bpl13210/bpl13210.pdf diff --git a/test/pdfs/freeculture.pdf b/test/pdfs/freeculture.pdf new file mode 100644 index 000000000..8b27e9355 Binary files /dev/null and b/test/pdfs/freeculture.pdf differ diff --git a/test/pdfs/geothermal.pdf.link b/test/pdfs/geothermal.pdf.link new file mode 100644 index 000000000..6a255647f --- /dev/null +++ b/test/pdfs/geothermal.pdf.link @@ -0,0 +1 @@ +http://geothermal.inel.gov/publications/future_of_geothermal_energy.pdf diff --git a/test/pdfs/issue918.pdf b/test/pdfs/issue918.pdf new file mode 100644 index 000000000..ac1a9c37f Binary files /dev/null and b/test/pdfs/issue918.pdf differ diff --git a/test/pdfs/issue919.pdf.link b/test/pdfs/issue919.pdf.link new file mode 100644 index 000000000..683001139 --- /dev/null +++ b/test/pdfs/issue919.pdf.link @@ -0,0 +1 @@ +http://agb.traviangames.com/Travian_AR_Terms.pdf diff --git a/test/pdfs/lista_preliminar.pdf.link b/test/pdfs/lista_preliminar.pdf.link new file mode 100644 index 000000000..54102b3b1 --- /dev/null +++ b/test/pdfs/lista_preliminar.pdf.link @@ -0,0 +1 @@ +http://www.lfg.com.br/concursodebolsas/lista_preliminar_classificao.pdf diff --git a/test/pdfs/scan-bad.pdf b/test/pdfs/scan-bad.pdf new file mode 100755 index 000000000..ca09315f9 Binary files /dev/null and b/test/pdfs/scan-bad.pdf differ diff --git a/test/pdfs/tutorial.pdf.link b/test/pdfs/tutorial.pdf.link new file mode 100644 index 000000000..ec8141ce7 --- /dev/null +++ b/test/pdfs/tutorial.pdf.link @@ -0,0 +1 @@ +http://cplusplus.com/files/tutorial.pdf diff --git a/test/test.py b/test/test.py index 256200587..888bd9ce8 100644 --- a/test/test.py +++ b/test/test.py @@ -12,6 +12,7 @@ DOC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__),"..")) ANAL = True DEFAULT_MANIFEST_FILE = 'test_manifest.json' EQLOG_FILE = 'eq.log' +BROWSERLOG_FILE = 'browser.log' REFDIR = 'ref' TMPDIR = 'tmp' VERBOSE = False @@ -229,6 +230,7 @@ class BaseBrowserCommand(object): def setup(self): self.tempDir = tempfile.mkdtemp() self.profileDir = os.path.join(self.tempDir, "profile") + self.browserLog = open(BROWSERLOG_FILE, "w") def teardown(self): # If the browser is still running, wait up to ten seconds for it to quit @@ -245,6 +247,8 @@ class BaseBrowserCommand(object): if self.tempDir is not None and os.path.exists(self.tempDir): shutil.rmtree(self.tempDir) + self.browserLog.close() + def start(self, url): raise Exception("Can't start BaseBrowserCommand") @@ -262,7 +266,7 @@ class FirefoxBrowserCommand(BaseBrowserCommand): if platform.system() == "Darwin": cmds.append("-foreground") cmds.extend(["-no-remote", "-profile", self.profileDir, url]) - self.process = subprocess.Popen(cmds) + self.process = subprocess.Popen(cmds, stdout = self.browserLog, stderr = self.browserLog) class ChromeBrowserCommand(BaseBrowserCommand): def _fixupMacPath(self): @@ -272,7 +276,7 @@ class ChromeBrowserCommand(BaseBrowserCommand): cmds = [self.path] cmds.extend(["--user-data-dir=%s" % self.profileDir, "--no-first-run", "--disable-sync", url]) - self.process = subprocess.Popen(cmds) + self.process = subprocess.Popen(cmds, stdout = self.browserLog, stderr = self.browserLog) def makeBrowserCommand(browser): path = browser["path"].lower() diff --git a/test/test_manifest.json b/test/test_manifest.json index bfa131d9a..7469db678 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -20,7 +20,6 @@ { "id": "intelisa-load", "file": "pdfs/intelisa.pdf", "md5": "f5712097d29287a97f1278839814f682", - "md5": "f3ed5487d1afa34d8b77c0c734a95c79", "link": true, "rounds": 1, "type": "load" @@ -88,6 +87,13 @@ "rounds": 1, "type": "eq" }, + { "id": "freeculture", + "file": "pdfs/freeculture.pdf", + "md5": "dcdf3a8268e6a18938a42d5149efcfca", + "rounds": 1, + "pageLimit": 5, + "type": "eq" + }, { "id": "wnv_chinese-pdf", "file": "pdfs/wnv_chinese.pdf", "md5": "db682638e68391125e8982d3c984841e", @@ -221,6 +227,12 @@ "rounds": 1, "type": "load" }, + { "id": "scan-bad", + "file": "pdfs/scan-bad.pdf", + "md5": "4cf988f01ab83f61aca57f406dfd6584", + "rounds": 1, + "type": "load" + }, { "id": "ibwa-bad", "file": "pdfs/ibwa-bad.pdf", "md5": "6ca059d32b74ac2688ae06f727fee755", @@ -296,5 +308,58 @@ "md5": "20d88011dd7e3c4fb5274979094dab93", "rounds": 1, "type": "eq" + }, + { "id": "bpl13210", + "file": "pdfs/bpl13210.pdf", + "md5": "8a08512baa9fa95378d9ad4b995947c7", + "link": true, + "pageLimit": 5, + "rounds": 1, + "type": "eq" + }, + { "id": "tutorial", + "file": "pdfs/tutorial.pdf", + "md5": "6e122f618c27f3aa9a689423e3be6b8d", + "link": true, + "rounds": 1, + "type": "eq" + }, + { "id": "geothermal.pdf", + "file": "pdfs/geothermal.pdf", + "md5": "ecffc0ce38ffdf1e90dc952f186e9a91", + "rounds": 1, + "link": true, + "pageLimit": 5, + "skipPages": [1], + "type": "eq" + }, + { "id": "lista_preliminar", + "file": "pdfs/lista_preliminar.pdf", + "md5": "4eff251319eeb660ba8a7a5cfac7787d", + "rounds": 1, + "link": true, + "pageLimit": 3, + "type": "eq" + }, + { "id": "issue919", + "file": "pdfs/issue919.pdf", + "md5": "3a1716a512aca4d7a8d6106bd4885d14", + "rounds": 1, + "link": true, + "pageLimit": 3, + "type": "eq" + }, + { "id": "issue918", + "file": "pdfs/issue918.pdf", + "md5": "d582cc0f2592ae82936589ced2a47e55", + "rounds": 1, + "type": "eq" + }, + { "id": "aboutstacks", + "file": "pdfs/aboutstacks.pdf", + "md5": "6e7c8416a293ba2d83bc8dd20c6ccf51", + "rounds": 1, + "link": true, + "type": "eq" } ] diff --git a/web/viewer.html b/web/viewer.html index ffd4327a7..53ca2a247 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -27,9 +27,9 @@ - - + +