diff --git a/Makefile b/Makefile index caeca9b41..0ad2eb09c 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,8 @@ bundle: | $(BUILD_DIR) @cd src; \ cat $(PDF_JS_FILES) > all_files.tmp; \ sed '/PDFJSSCRIPT_INCLUDE_ALL/ r all_files.tmp' pdf.js > ../$(BUILD_TARGET); \ - sed -i '' "s/PDFJSSCRIPT_BUNDLE_VER/`git log --format="%H" -n 1`/" ../$(BUILD_TARGET); \ + sed -i.bak "s/PDFJSSCRIPT_BUNDLE_VER/`git log --format="%h" -n 1`/" ../$(BUILD_TARGET); \ + rm -f ../$(BUILD_TARGET).bak rm -f *.tmp; \ cd .. diff --git a/src/canvas.js b/src/canvas.js index 0913b582a..286fd0e04 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -6,6 +6,17 @@ // contexts store most of the state we need natively. // However, PDF needs a bit more state, which we store here. +var TextRenderingMode = { + FILL: 0, + STROKE: 1, + FILL_STROKE: 2, + INVISIBLE: 3, + FILL_ADD_TO_PATH: 4, + STROKE_ADD_TO_PATH: 5, + FILL_STROKE_ADD_TO_PATH: 6, + ADD_TO_PATH: 7 +}; + var CanvasExtraState = (function canvasExtraState() { function constructor(old) { // Are soft masks and alpha values shapes or opacities? @@ -23,6 +34,7 @@ var CanvasExtraState = (function canvasExtraState() { this.charSpacing = 0; this.wordSpacing = 0; this.textHScale = 1; + this.textRenderingMode = TextRenderingMode.FILL; // Color spaces this.fillColorSpace = new DeviceGrayCS(); this.fillColorSpaceObj = null; @@ -195,6 +207,35 @@ var CanvasGraphics = (function canvasGraphics() { var EO_CLIP = {}; constructor.prototype = { + slowCommands: { + 'stroke': true, + 'closeStroke': true, + 'fill': true, + 'eoFill': true, + 'fillStroke': true, + 'eoFillStroke': true, + 'closeFillStroke': true, + 'closeEOFillStroke': true, + 'showText': true, + 'showSpacedText': true, + 'setStrokeColorSpace': true, + 'setFillColorSpace': true, + 'setStrokeColor': true, + 'setStrokeColorN': true, + 'setFillColor': true, + 'setFillColorN_IR': true, + 'setStrokeGray': true, + 'setFillGray': true, + 'setStrokeRGBColor': true, + 'setFillRGBColor': true, + 'setStrokeCMYKColor': true, + 'setFillCMYKColor': true, + 'paintJpegXObject': true, + 'paintImageXObject': true, + 'paintImageMaskXObject': true, + 'shadingFill': true + }, + beginDrawing: function canvasGraphicsBeginDrawing(mediaBox) { var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height; this.ctx.save(); @@ -234,13 +275,17 @@ var CanvasGraphics = (function canvasGraphics() { } var executionEndIdx; - var startTime = Date.now(); + var endTime = Date.now() + kExecutionTime; var objs = this.objs; + var fnName; + var slowCommands = this.slowCommands; while (true) { - if (fnArray[i] !== 'dependency') { - this[fnArray[i]].apply(this, argsArray[i]); + fnName = fnArray[i]; + + if (fnName !== 'dependency') { + this[fnName].apply(this, argsArray[i]); } else { var deps = argsArray[i]; for (var n = 0, nn = deps.length; n < nn; n++) { @@ -254,6 +299,7 @@ var CanvasGraphics = (function canvasGraphics() { } } } + i++; // If the entire IRQueue was executed, stop as were done. @@ -264,7 +310,7 @@ var CanvasGraphics = (function canvasGraphics() { // If the execution took longer then a certain amount of time, shedule // to continue exeution after a short delay. // However, this is only possible if a 'continueCallback' is passed in. - if (continueCallback && (Date.now() - startTime) > kExecutionTime) { + if (continueCallback && slowCommands[fnName] && Date.now() > endTime) { setTimeout(continueCallback, 0); return i; } @@ -543,7 +589,9 @@ var CanvasGraphics = (function canvasGraphics() { this.ctx.font = rule; }, setTextRenderingMode: function canvasGraphicsSetTextRenderingMode(mode) { - TODO('text rendering mode: ' + mode); + if (mode >= TextRenderingMode.FILL_ADD_TO_PATH) + TODO('unsupported text rendering mode: ' + mode); + this.current.textRenderingMode = mode; }, setTextRise: function canvasGraphicsSetTextRise(rise) { TODO('text rise: ' + rise); @@ -637,6 +685,7 @@ var CanvasGraphics = (function canvasGraphics() { var textLayer = this.textLayer; var text = {str: '', length: 0, canvasWidth: 0, geom: {}}; var textSelection = textLayer && !skipTextSelection ? true : false; + var textRenderingMode = current.textRenderingMode; if (textSelection) { ctx.save(); @@ -693,7 +742,26 @@ var CanvasGraphics = (function canvasGraphics() { var char = glyph.fontChar; var charWidth = glyph.width * fontSize * 0.001 + charSpacing; - ctx.fillText(char, width, 0); + + switch (textRenderingMode) { + default: // other unsupported rendering modes + case TextRenderingMode.FILL: + case TextRenderingMode.FILL_ADD_TO_PATH: + ctx.fillText(char, width, 0); + break; + case TextRenderingMode.STROKE: + case TextRenderingMode.STROKE_ADD_TO_PATH: + ctx.strokeText(char, width, 0); + break; + case TextRenderingMode.FILL_STROKE: + case TextRenderingMode.FILL_STROKE_ADD_TO_PATH: + ctx.fillText(char, width, 0); + ctx.strokeText(char, width, 0); + break; + case TextRenderingMode.INVISIBLE: + break; + } + width += charWidth; text.str += glyph.unicode === ' ' ? ' ' : glyph.unicode; @@ -917,9 +985,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]); @@ -969,8 +1037,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'); } @@ -980,7 +1048,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); @@ -1046,26 +1113,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) { @@ -1126,3 +1183,38 @@ var CanvasGraphics = (function canvasGraphics() { return constructor; })(); +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/core.js b/src/core.js index 32c13d8cf..1bad175da 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; @@ -39,11 +41,15 @@ function getPdf(arg, callback) { if ('error' in params) xhr.onerror = params.error || undefined; - xhr.onreadystatechange = function getPdfOnreadystatechange() { - if (xhr.readyState === 4 && xhr.status === xhr.expected) { - var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse || - xhr.responseArrayBuffer || xhr.response); - callback(data); + xhr.onreadystatechange = function getPdfOnreadystatechange(e) { + if (xhr.readyState === 4) { + if (xhr.status === xhr.expected) { + var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse || + xhr.responseArrayBuffer || xhr.response); + callback(data); + } else if (params.error) { + params.error(e); + } } }; xhr.send(null); @@ -63,6 +69,9 @@ var Page = (function pagePage() { }; this.xref = xref; this.ref = ref; + + this.ctx = null; + this.callback = null; } constructor.prototype = { @@ -165,8 +174,10 @@ var Page = (function pagePage() { try { self.display(gfx, self.callback); } catch (e) { - if (self.callback) self.callback(e.toString()); - throw e; + if (self.callback) + self.callback(e); + else + throw e; } }); }; @@ -556,8 +567,8 @@ 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 'Font': var name = data[2]; @@ -595,6 +606,14 @@ var PDFDoc = (function pdfDoc() { } }.bind(this)); + messageHandler.on('page_error', function pdfDocError(data) { + var page = this.pageCache[data.pageNum]; + if (page.callback) + page.callback(data.error); + else + throw data.error; + }, this); + setTimeout(function pdfDocFontReadySetTimeout() { messageHandler.send('doc', this.data); this.workerReadyPromise.resolve(true); diff --git a/src/fonts.js b/src/fonts.js index 672739ea4..1a4366c19 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. */ @@ -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) + @@ -1758,7 +1765,7 @@ var Font = (function Font() { var hasShortCmap = !!cmapTable.hasShortCmap; var toUnicode = this.toUnicode; - if (hasShortCmap && toUnicode) { + if (toUnicode) { // checking if cmap is just identity map var isIdentity = true; for (var i = 0, ii = glyphs.length; i < ii; i++) { @@ -1903,9 +1910,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 +1924,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- @@ -2110,6 +2117,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 +2149,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 +2164,7 @@ var Font = (function Font() { }; }, - charsToGlyphs: function fonts_chars2Glyphs(chars) { + charsToGlyphs: function fonts_charsToGlyphs(chars) { var charsCache = this.charsCache; var glyphs; diff --git a/src/image.js b/src/image.js index 17ef7b06d..217e081c2 100644 --- a/src/image.js +++ b/src/image.js @@ -130,11 +130,11 @@ var PDFImage = (function pdfImage() { var buf = new Uint8Array(width * height); if (smask) { - if (smask.image.getImage) { + if (smask.image.src) { // smask is a DOM image var tempCanvas = new ScratchCanvas(width, height); var tempCtx = tempCanvas.getContext('2d'); - var domImage = smask.image.getImage(); + var domImage = smask.image; tempCtx.drawImage(domImage, 0, 0, domImage.width, domImage.height, 0, 0, width, height); var data = tempCtx.getImageData(0, 0, width, height).data; @@ -229,29 +229,10 @@ 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; -})(); - +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 7aebb732a..9d32956b7 100644 --- a/src/obj.js +++ b/src/obj.js @@ -518,20 +518,29 @@ var XRef = (function xRefXRef() { readXRef: function readXref(startXRef) { var stream = this.stream; stream.pos = startXRef; - var parser = new Parser(new Lexer(stream), true); - var obj = parser.getObj(); - // parse an old-style xref table - if (isCmd(obj, 'xref')) - return this.readXRefTable(parser); - // parse an xref stream - if (isInt(obj)) { - if (!isInt(parser.getObj()) || - !isCmd(parser.getObj(), 'obj') || - !isStream(obj = parser.getObj())) { - error('Invalid XRef stream'); + + try { + var parser = new Parser(new Lexer(stream), true); + var obj = parser.getObj(); + + // parse an old-style xref table + if (isCmd(obj, 'xref')) + return this.readXRefTable(parser); + + // parse an xref stream + if (isInt(obj)) { + if (!isInt(parser.getObj()) || + !isCmd(parser.getObj(), 'obj') || + !isStream(obj = parser.getObj())) { + error('Invalid XRef stream'); + } + return this.readXRefStream(obj); } - return this.readXRefStream(obj); + } catch (e) { + log('Reading of the xref table/stream failed: ' + e); } + + warn('Indexing all PDF objects'); return this.indexObjects(); }, getEntry: function xRefGetEntry(i) { @@ -589,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; } diff --git a/src/parser.js b/src/parser.js index 93a3f21b5..036191677 100644 --- a/src/parser.js +++ b/src/parser.js @@ -225,7 +225,8 @@ var Parser = (function parserParser() { return new PredictorStream(new FlateStream(stream), params); } return new FlateStream(stream); - } else if (name == 'LZWDecode' || name == 'LZW') { + } + if (name == 'LZWDecode' || name == 'LZW') { var earlyChange = 1; if (params) { if (params.has('EarlyChange')) @@ -234,18 +235,21 @@ var Parser = (function parserParser() { new LZWStream(stream, earlyChange), params); } return new LZWStream(stream, earlyChange); - } else if (name == 'DCTDecode' || name == 'DCT') { + } + if (name == 'DCTDecode' || name == 'DCT') { var bytes = stream.getBytes(length); return new JpegStream(bytes, stream.dict, this.xref); - } else if (name == 'ASCII85Decode' || name == 'A85') { - return new Ascii85Stream(stream); - } else if (name == 'ASCIIHexDecode' || name == 'AHx') { - return new AsciiHexStream(stream); - } else if (name == 'CCITTFaxDecode' || name == 'CCF') { - return new CCITTFaxStream(stream, params); - } else { - TODO('filter "' + name + '" not supported yet'); } + if (name == 'ASCII85Decode' || name == 'A85') { + return new Ascii85Stream(stream); + } + if (name == 'ASCIIHexDecode' || name == 'AHx') { + return new AsciiHexStream(stream); + } + if (name == 'CCITTFaxDecode' || name == 'CCF') { + return new CCITTFaxStream(stream, params); + } + TODO('filter "' + name + '" not supported yet'); return stream; } }; diff --git a/src/pattern.js b/src/pattern.js index 72d13d896..aa4667e07 100644 --- a/src/pattern.js +++ b/src/pattern.js @@ -3,6 +3,11 @@ 'use strict'; +var PatternType = { + AXIAL: 2, + RADIAL: 3 +}; + var Pattern = (function patternPattern() { // Constructor should define this.getPattern function constructor() { @@ -28,9 +33,9 @@ var Pattern = (function patternPattern() { var type = dict.get('ShadingType'); switch (type) { - case 2: - case 3: - // both radial and axial shadings are handled by RadialAxial shading + case PatternType.AXIAL: + case PatternType.RADIAL: + // Both radial and axial shadings are handled by RadialAxial shading. return new Shadings.RadialAxial(dict, matrix, xref, res, ctx); default: return new Shadings.Dummy(); @@ -117,9 +122,9 @@ Shadings.RadialAxial = (function radialAxialShading() { } var grad; - if (type == 2) + if (type == PatternType.AXIAL) grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]); - else if (type == 3) + else if (type == PatternType.RADIAL) grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1); for (var i = 0, ii = colorStops.length; i < ii; ++i) { @@ -133,12 +138,12 @@ Shadings.RadialAxial = (function radialAxialShading() { getIR: function radialAxialShadingGetIR() { var coordsArr = this.coordsArr; var type = this.shadingType; - if (type == 2) { + if (type == PatternType.AXIAL) { var p0 = [coordsArr[0], coordsArr[1]]; var p1 = [coordsArr[2], coordsArr[3]]; var r0 = null; var r1 = null; - } else if (type == 3) { + } else if (type == PatternType.RADIAL) { var p0 = [coordsArr[0], coordsArr[1]]; var p1 = [coordsArr[3], coordsArr[4]]; var r0 = coordsArr[2]; @@ -178,7 +183,11 @@ Shadings.Dummy = (function dummyShading() { })(); var TilingPattern = (function tilingPattern() { - var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2; + var PaintType = { + COLORED: 1, + UNCOLORED: 2 + }; + var MAX_PATTERN_SIZE = 512; function TilingPattern(IR, color, ctx, objs) { var IRQueue = IR[2]; @@ -204,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); @@ -220,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/worker.js b/src/worker.js index 67f1bf658..8e4c14fbc 100644 --- a/src/worker.js +++ b/src/worker.js @@ -14,7 +14,6 @@ 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) { @@ -67,7 +66,6 @@ var WorkerMessageHandler = { handler.on('page_request', function wphSetupPageRequest(pageNum) { pageNum = parseInt(pageNum); - var page = pdfDoc.getPage(pageNum); // The following code does quite the same as // Page.prototype.startRendering, but stops at one point and sends the @@ -77,9 +75,23 @@ var WorkerMessageHandler = { var start = Date.now(); var dependency = []; - - // Pre compile the pdf page and fetch the fonts/images. - var IRQueue = page.getIRQueue(handler, dependency); + var IRQueue = null; + try { + var page = pdfDoc.getPage(pageNum); + // Pre compile the pdf page and fetch the fonts/images. + IRQueue = page.getIRQueue(handler, dependency); + } catch (e) { + // Turn the error into an obj that can be serialized + e = { + message: e.message, + stack: e.stack + }; + handler.send('page_error', { + pageNum: pageNum, + error: e + }); + return; + } console.log('page=%d - getIRQueue: time=%dms, len=%d', pageNum, Date.now() - start, IRQueue.fnArray.length); diff --git a/test/driver.js b/test/driver.js index c11cecf56..2467b57a3 100644 --- a/test/driver.js +++ b/test/driver.js @@ -160,12 +160,19 @@ 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(e) { - snapshotCurrentPage(task, (!failure && e) ? - ('render : ' + e) : failure); - } + function nextPageStartRendering(error) { + var failureMessage = false; + if (error) + failureMessage = 'render : ' + error.message; + snapshotCurrentPage(task, failureMessage); + }, + textLayer ); } catch (e) { failure = 'page setup : ' + e.toString(); 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/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_manifest.json b/test/test_manifest.json index 082a5ef7f..ca09d14bc 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" @@ -302,5 +301,21 @@ "md5": "20d88011dd7e3c4fb5274979094dab93", "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" } ] diff --git a/web/viewer.css b/web/viewer.css index 3313ce3cb..a1ef92810 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -273,6 +273,38 @@ canvas { display: none; } +#errorWrapper { + background: none repeat scroll 0 0 #FF5555; + color: white; + left: 0; + position: fixed; + right: 0; + top: 30px; + z-index: 1000; + padding: 3px; + font-size: 0.8em; +} + +#errorMessageLeft { + float: left; +} + +#errorMessageRight { + float: right; +} + +#errorMoreInfo { + background-color: #FFFFFF; + color: black; + padding: 3px; + margin: 3px; + white-space: pre; +} + +.clearBoth { + clear: both; +} + /* === Printed media overrides === */ @media print { #sidebar { diff --git a/web/viewer.html b/web/viewer.html index e441a9847..ffd4327a7 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -97,6 +97,24 @@ -- +