diff --git a/src/core/core.js b/src/core/core.js index 6ec8264ff..69a76f6d8 100644 --- a/src/core/core.js +++ b/src/core/core.js @@ -184,7 +184,6 @@ var Page = (function PageClosure() { var annotations = datas[1]; if (annotations.length === 0) { - PartialEvaluator.optimizeQueue(pageOpList); pageOpList.flush(true); promise.resolve(pageOpList); return; @@ -193,7 +192,6 @@ var Page = (function PageClosure() { var annotationsReadyPromise = Annotation.appendToOperatorList( annotations, pageOpList, pdfManager, partialEvaluator); annotationsReadyPromise.then(function () { - PartialEvaluator.optimizeQueue(pageOpList); pageOpList.flush(true); promise.resolve(pageOpList); }, reject); diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 286cb3ef4..80099f5dd 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -20,7 +20,7 @@ isStream, isString, JpegStream, Lexer, Metrics, Name, Parser, Pattern, PDFImage, PDFJS, serifFonts, stdFontMap, symbolsFonts, TilingPattern, TODO, warn, Util, Promise, - RefSetCache, isRef, TextRenderingMode, CMapFactory */ + RefSetCache, isRef, TextRenderingMode, CMapFactory, OPS */ 'use strict'; @@ -45,98 +45,99 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // If variableArgs === false: exactly `numArgs` expected var OP_MAP = { // Graphic state - w: { fnName: 'setLineWidth', numArgs: 1, variableArgs: false }, - J: { fnName: 'setLineCap', numArgs: 1, variableArgs: false }, - j: { fnName: 'setLineJoin', numArgs: 1, variableArgs: false }, - M: { fnName: 'setMiterLimit', numArgs: 1, variableArgs: false }, - d: { fnName: 'setDash', numArgs: 2, variableArgs: false }, - ri: { fnName: 'setRenderingIntent', numArgs: 1, variableArgs: false }, - i: { fnName: 'setFlatness', numArgs: 1, variableArgs: false }, - gs: { fnName: 'setGState', numArgs: 1, variableArgs: false }, - q: { fnName: 'save', numArgs: 0, variableArgs: false }, - Q: { fnName: 'restore', numArgs: 0, variableArgs: false }, - cm: { fnName: 'transform', numArgs: 6, variableArgs: false }, + w: { id: OPS.setLineWidth, numArgs: 1, variableArgs: false }, + J: { id: OPS.setLineCap, numArgs: 1, variableArgs: false }, + j: { id: OPS.setLineJoin, numArgs: 1, variableArgs: false }, + M: { id: OPS.setMiterLimit, numArgs: 1, variableArgs: false }, + d: { id: OPS.setDash, numArgs: 2, variableArgs: false }, + ri: { id: OPS.setRenderingIntent, numArgs: 1, variableArgs: false }, + i: { id: OPS.setFlatness, numArgs: 1, variableArgs: false }, + gs: { id: OPS.setGState, numArgs: 1, variableArgs: false }, + q: { id: OPS.save, numArgs: 0, variableArgs: false }, + Q: { id: OPS.restore, numArgs: 0, variableArgs: false }, + cm: { id: OPS.transform, numArgs: 6, variableArgs: false }, // Path - m: { fnName: 'moveTo', numArgs: 2, variableArgs: false }, - l: { fnName: 'lineTo', numArgs: 2, variableArgs: false }, - c: { fnName: 'curveTo', numArgs: 6, variableArgs: false }, - v: { fnName: 'curveTo2', numArgs: 4, variableArgs: false }, - y: { fnName: 'curveTo3', numArgs: 4, variableArgs: false }, - h: { fnName: 'closePath', numArgs: 0, variableArgs: false }, - re: { fnName: 'rectangle', numArgs: 4, variableArgs: false }, - S: { fnName: 'stroke', numArgs: 0, variableArgs: false }, - s: { fnName: 'closeStroke', numArgs: 0, variableArgs: false }, - f: { fnName: 'fill', numArgs: 0, variableArgs: false }, - F: { fnName: 'fill', numArgs: 0, variableArgs: false }, - 'f*': { fnName: 'eoFill', numArgs: 0, variableArgs: false }, - B: { fnName: 'fillStroke', numArgs: 0, variableArgs: false }, - 'B*': { fnName: 'eoFillStroke', numArgs: 0, variableArgs: false }, - b: { fnName: 'closeFillStroke', numArgs: 0, variableArgs: false }, - 'b*': { fnName: 'closeEOFillStroke', numArgs: 0, variableArgs: false }, - n: { fnName: 'endPath', numArgs: 0, variableArgs: false }, + m: { id: OPS.moveTo, numArgs: 2, variableArgs: false }, + l: { id: OPS.lineTo, numArgs: 2, variableArgs: false }, + c: { id: OPS.curveTo, numArgs: 6, variableArgs: false }, + v: { id: OPS.curveTo2, numArgs: 4, variableArgs: false }, + y: { id: OPS.curveTo3, numArgs: 4, variableArgs: false }, + h: { id: OPS.closePath, numArgs: 0, variableArgs: false }, + re: { id: OPS.rectangle, numArgs: 4, variableArgs: false }, + S: { id: OPS.stroke, numArgs: 0, variableArgs: false }, + s: { id: OPS.closeStroke, numArgs: 0, variableArgs: false }, + f: { id: OPS.fill, numArgs: 0, variableArgs: false }, + F: { id: OPS.fill, numArgs: 0, variableArgs: false }, + 'f*': { id: OPS.eoFill, numArgs: 0, variableArgs: false }, + B: { id: OPS.fillStroke, numArgs: 0, variableArgs: false }, + 'B*': { id: OPS.eoFillStroke, numArgs: 0, variableArgs: false }, + b: { id: OPS.closeFillStroke, numArgs: 0, variableArgs: false }, + 'b*': { id: OPS.closeEOFillStroke, numArgs: 0, variableArgs: false }, + n: { id: OPS.endPath, numArgs: 0, variableArgs: false }, // Clipping - W: { fnName: 'clip', numArgs: 0, variableArgs: false }, - 'W*': { fnName: 'eoClip', numArgs: 0, variableArgs: false }, + W: { id: OPS.clip, numArgs: 0, variableArgs: false }, + 'W*': { id: OPS.eoClip, numArgs: 0, variableArgs: false }, // Text - BT: { fnName: 'beginText', numArgs: 0, variableArgs: false }, - ET: { fnName: 'endText', numArgs: 0, variableArgs: false }, - Tc: { fnName: 'setCharSpacing', numArgs: 1, variableArgs: false }, - Tw: { fnName: 'setWordSpacing', numArgs: 1, variableArgs: false }, - Tz: { fnName: 'setHScale', numArgs: 1, variableArgs: false }, - TL: { fnName: 'setLeading', numArgs: 1, variableArgs: false }, - Tf: { fnName: 'setFont', numArgs: 2, variableArgs: false }, - Tr: { fnName: 'setTextRenderingMode', numArgs: 1, variableArgs: false }, - Ts: { fnName: 'setTextRise', numArgs: 1, variableArgs: false }, - Td: { fnName: 'moveText', numArgs: 2, variableArgs: false }, - TD: { fnName: 'setLeadingMoveText', numArgs: 2, variableArgs: false }, - Tm: { fnName: 'setTextMatrix', numArgs: 6, variableArgs: false }, - 'T*': { fnName: 'nextLine', numArgs: 0, variableArgs: false }, - Tj: { fnName: 'showText', numArgs: 1, variableArgs: false }, - TJ: { fnName: 'showSpacedText', numArgs: 1, variableArgs: false }, - '\'': { fnName: 'nextLineShowText', numArgs: 1, variableArgs: false }, - '"': { fnName: 'nextLineSetSpacingShowText', numArgs: 3, + BT: { id: OPS.beginText, numArgs: 0, variableArgs: false }, + ET: { id: OPS.endText, numArgs: 0, variableArgs: false }, + Tc: { id: OPS.setCharSpacing, numArgs: 1, variableArgs: false }, + Tw: { id: OPS.setWordSpacing, numArgs: 1, variableArgs: false }, + Tz: { id: OPS.setHScale, numArgs: 1, variableArgs: false }, + TL: { id: OPS.setLeading, numArgs: 1, variableArgs: false }, + Tf: { id: OPS.setFont, numArgs: 2, variableArgs: false }, + Tr: { id: OPS.setTextRenderingMode, numArgs: 1, variableArgs: false }, + Ts: { id: OPS.setTextRise, numArgs: 1, variableArgs: false }, + Td: { id: OPS.moveText, numArgs: 2, variableArgs: false }, + TD: { id: OPS.setLeadingMoveText, numArgs: 2, variableArgs: false }, + Tm: { id: OPS.setTextMatrix, numArgs: 6, variableArgs: false }, + 'T*': { id: OPS.nextLine, numArgs: 0, variableArgs: false }, + Tj: { id: OPS.showText, numArgs: 1, variableArgs: false }, + TJ: { id: OPS.showSpacedText, numArgs: 1, variableArgs: false }, + '\'': { id: OPS.nextLineShowText, numArgs: 1, variableArgs: false }, + '"': { id: OPS.nextLineSetSpacingShowText, numArgs: 3, variableArgs: false }, // Type3 fonts - d0: { fnName: 'setCharWidth', numArgs: 2, variableArgs: false }, - d1: { fnName: 'setCharWidthAndBounds', numArgs: 6, variableArgs: false }, + d0: { id: OPS.setCharWidth, numArgs: 2, variableArgs: false }, + d1: { id: OPS.setCharWidthAndBounds, numArgs: 6, variableArgs: false }, // Color - CS: { fnName: 'setStrokeColorSpace', numArgs: 1, variableArgs: false }, - cs: { fnName: 'setFillColorSpace', numArgs: 1, variableArgs: false }, - SC: { fnName: 'setStrokeColor', numArgs: 4, variableArgs: true }, - SCN: { fnName: 'setStrokeColorN', numArgs: 33, variableArgs: true }, - sc: { fnName: 'setFillColor', numArgs: 4, variableArgs: true }, - scn: { fnName: 'setFillColorN', numArgs: 33, variableArgs: true }, - G: { fnName: 'setStrokeGray', numArgs: 1, variableArgs: false }, - g: { fnName: 'setFillGray', numArgs: 1, variableArgs: false }, - RG: { fnName: 'setStrokeRGBColor', numArgs: 3, variableArgs: false }, - rg: { fnName: 'setFillRGBColor', numArgs: 3, variableArgs: false }, - K: { fnName: 'setStrokeCMYKColor', numArgs: 4, variableArgs: false }, - k: { fnName: 'setFillCMYKColor', numArgs: 4, variableArgs: false }, + CS: { id: OPS.setStrokeColorSpace, numArgs: 1, variableArgs: false }, + cs: { id: OPS.setFillColorSpace, numArgs: 1, variableArgs: false }, + SC: { id: OPS.setStrokeColor, numArgs: 4, variableArgs: true }, + SCN: { id: OPS.setStrokeColorN, numArgs: 33, variableArgs: true }, + sc: { id: OPS.setFillColor, numArgs: 4, variableArgs: true }, + scn: { id: OPS.setFillColorN, numArgs: 33, variableArgs: true }, + G: { id: OPS.setStrokeGray, numArgs: 1, variableArgs: false }, + g: { id: OPS.setFillGray, numArgs: 1, variableArgs: false }, + RG: { id: OPS.setStrokeRGBColor, numArgs: 3, variableArgs: false }, + rg: { id: OPS.setFillRGBColor, numArgs: 3, variableArgs: false }, + K: { id: OPS.setStrokeCMYKColor, numArgs: 4, variableArgs: false }, + k: { id: OPS.setFillCMYKColor, numArgs: 4, variableArgs: false }, // Shading - sh: { fnName: 'shadingFill', numArgs: 1, variableArgs: false }, + sh: { id: OPS.shadingFill, numArgs: 1, variableArgs: false }, // Images - BI: { fnName: 'beginInlineImage', numArgs: 0, variableArgs: false }, - ID: { fnName: 'beginImageData', numArgs: 0, variableArgs: false }, - EI: { fnName: 'endInlineImage', numArgs: 1, variableArgs: false }, + BI: { id: OPS.beginInlineImage, numArgs: 0, variableArgs: false }, + ID: { id: OPS.beginImageData, numArgs: 0, variableArgs: false }, + EI: { id: OPS.endInlineImage, numArgs: 1, variableArgs: false }, // XObjects - Do: { fnName: 'paintXObject', numArgs: 1, variableArgs: false }, - MP: { fnName: 'markPoint', numArgs: 1, variableArgs: false }, - DP: { fnName: 'markPointProps', numArgs: 2, variableArgs: false }, - BMC: { fnName: 'beginMarkedContent', numArgs: 1, variableArgs: false }, - BDC: { fnName: 'beginMarkedContentProps', numArgs: 2, variableArgs: false }, - EMC: { fnName: 'endMarkedContent', numArgs: 0, variableArgs: false }, + Do: { id: OPS.paintXObject, numArgs: 1, variableArgs: false }, + MP: { id: OPS.markPoint, numArgs: 1, variableArgs: false }, + DP: { id: OPS.markPointProps, numArgs: 2, variableArgs: false }, + BMC: { id: OPS.beginMarkedContent, numArgs: 1, variableArgs: false }, + BDC: { id: OPS.beginMarkedContentProps, numArgs: 2, + variableArgs: false }, + EMC: { id: OPS.endMarkedContent, numArgs: 0, variableArgs: false }, // Compatibility - BX: { fnName: 'beginCompat', numArgs: 0, variableArgs: false }, - EX: { fnName: 'endCompat', numArgs: 0, variableArgs: false }, + BX: { id: OPS.beginCompat, numArgs: 0, variableArgs: false }, + EX: { id: OPS.endCompat, numArgs: 0, variableArgs: false }, // (reserved partial commands for the lexer) BM: null, @@ -218,17 +219,17 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // There is also a group colorspace, but since we put everything in // RGB I'm not sure we need it. } - operatorList.addOp('beginGroup', [groupOptions]); + operatorList.addOp(OPS.beginGroup, [groupOptions]); } - operatorList.addOp('paintFormXObjectBegin', [matrix, bbox]); + operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]); this.getOperatorList(xobj, xobj.dict.get('Resources') || resources, operatorList); - operatorList.addOp('paintFormXObjectEnd', []); + operatorList.addOp(OPS.paintFormXObjectEnd, []); if (group) { - operatorList.addOp('endGroup', [groupOptions]); + operatorList.addOp(OPS.endGroup, [groupOptions]); } }, @@ -259,7 +260,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var decode = dict.get('Decode', 'D'); var inverseDecode = !!decode && decode[0] > 0; - operatorList.addOp('paintImageMaskXObject', + operatorList.addOp(OPS.paintImageMaskXObject, [PDFImage.createMask(imgArray, width, height, inverseDecode)] ); @@ -277,7 +278,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var imageObj = new PDFImage(this.xref, resources, image, inline, null, null); var imgData = imageObj.getImageData(); - operatorList.addOp('paintInlineImageXObject', [imgData]); + operatorList.addOp(OPS.paintInlineImageXObject, [imgData]); return; } @@ -291,7 +292,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (!softMask && !mask && image instanceof JpegStream && image.isNativelySupported(this.xref, resources)) { // These JPEGs don't need any more processing so we can just send it. - operatorList.addOp('paintJpegXObject', args); + operatorList.addOp(OPS.paintJpegXObject, args); this.handler.send( 'obj', [objId, this.pageIndex, 'JpegStream', image.getIR()]); return; @@ -303,7 +304,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData]); }, self.handler, self.xref, resources, image, inline); - operatorList.addOp('paintImageXObject', args); + operatorList.addOp(OPS.paintImageXObject, args); }, handleTilingType: function PartialEvaluator_handleTilingType( @@ -442,7 +443,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { setGStateForKey(gStateObj, key, value); } - operatorList.addOp('setGState', [gStateObj]); + operatorList.addOp(OPS.setGState, [gStateObj]); }, loadFont: function PartialEvaluator_loadFont(fontName, font, xref, @@ -557,7 +558,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { continue; } - var fn = opSpec.fnName; + var fn = opSpec.id; // Validate the number of arguments for the command if (opSpec.variableArgs) { @@ -640,7 +641,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var loadedName = self.handleSetFont(resources, args, null, operatorList); operatorList.addDependency(loadedName); - fn = 'setFont'; + fn = OPS.setFont; args[0] = loadedName; } else if (cmd == 'EI') { self.buildPaintImageXObject(resources, args[0], true, operatorList); @@ -675,11 +676,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { switch (fn) { // Parse the ColorSpace data to a raw format. - case 'setFillColorSpace': - case 'setStrokeColorSpace': + case OPS.setFillColorSpace: + case OPS.setStrokeColorSpace: args = [ColorSpace.parseToIR(args[0], xref, resources)]; break; - case 'shadingFill': + case OPS.shadingFill: var shadingRes = resources.get('Shading'); if (!shadingRes) error('No shading resource found'); @@ -692,9 +693,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { shading, null, xref, resources); var patternIR = shadingFill.getIR(); args = [patternIR]; - fn = 'shadingFill'; + fn = OPS.shadingFill; break; - case 'setGState': + case OPS.setGState: var dictName = args[0]; var extGState = resources.get('ExtGState'); @@ -1330,6 +1331,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { PartialEvaluator.optimizeQueue = function PartialEvaluator_optimizeQueue(queue) { + function squash(array, index, howMany, element) { + if (isArray(array)) { + array.splice(index, howMany, element); + } else { + // Replace the element. + array[index] = element; + // Shift everything after the element up. + var sub = array.subarray(index + howMany); + array.set(sub, index + 1); + } + } + var fnArray = queue.fnArray, argsArray = queue.argsArray; // grouping paintInlineImageXObject's into paintInlineImageXObjectGroup // searching for (save, transform, paintInlineImageXObject, restore)+ @@ -1337,10 +1350,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200; var MAX_WIDTH = 1000; var IMAGE_PADDING = 1; - for (var i = 0, ii = fnArray.length; i < ii; i++) { - if (fnArray[i] === 'paintInlineImageXObject' && - fnArray[i - 2] === 'save' && fnArray[i - 1] === 'transform' && - fnArray[i + 1] === 'restore') { + for (var i = 0, ii = argsArray.length; i < ii; i++) { + if (fnArray[i] === OPS.paintInlineImageXObject && + fnArray[i - 2] === OPS.save && fnArray[i - 1] === OPS.transform && + fnArray[i + 1] === OPS.restore) { var j = i - 2; for (i += 2; i < ii && fnArray[i - 4] === fnArray[i]; i++) { } @@ -1405,21 +1418,21 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } } // replacing queue items - fnArray.splice(j, count * 4, ['paintInlineImageXObjectGroup']); + squash(fnArray, j, count * 4, OPS.paintInlineImageXObjectGroup); argsArray.splice(j, count * 4, [{width: imgWidth, height: imgHeight, data: imgData}, map]); i = j; - ii = fnArray.length; + ii = argsArray.length; } } // grouping paintImageMaskXObject's into paintImageMaskXObjectGroup // searching for (save, transform, paintImageMaskXObject, restore)+ var MIN_IMAGES_IN_MASKS_BLOCK = 10; var MAX_IMAGES_IN_MASKS_BLOCK = 100; - for (var i = 0, ii = fnArray.length; i < ii; i++) { - if (fnArray[i] === 'paintImageMaskXObject' && - fnArray[i - 2] === 'save' && fnArray[i - 1] === 'transform' && - fnArray[i + 1] === 'restore') { + for (var i = 0, ii = argsArray.length; i < ii; i++) { + if (fnArray[i] === OPS.paintImageMaskXObject && + fnArray[i - 2] === OPS.save && fnArray[i - 1] === OPS.transform && + fnArray[i + 1] === OPS.restore) { var j = i - 2; for (i += 2; i < ii && fnArray[i - 4] === fnArray[i]; i++) { } @@ -1436,10 +1449,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { height: maskParams.height, transform: transform}); } // replacing queue items - fnArray.splice(j, count * 4, ['paintImageMaskXObjectGroup']); + squash(fnArray, j, count * 4, OPS.paintImageMaskXObjectGroup); argsArray.splice(j, count * 4, [images]); i = j; - ii = fnArray.length; + ii = argsArray.length; } } }; @@ -1448,25 +1461,40 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { return PartialEvaluator; })(); - var OperatorList = (function OperatorListClosure() { var CHUNK_SIZE = 100; function OperatorList(messageHandler, pageIndex) { this.messageHandler = messageHandler; - this.fnArray = []; + // When there isn't a message handler the fn array needs to be able to grow + // since we can't flush the operators. + if (messageHandler) { + this.fnArray = new Uint8Array(CHUNK_SIZE); + } else { + this.fnArray = []; + } this.argsArray = []; this.dependencies = {}, this.pageIndex = pageIndex; + this.fnIndex = 0; } OperatorList.prototype = { + get length() { + return this.argsArray.length; + }, + addOp: function(fn, args) { - this.fnArray.push(fn); - this.argsArray.push(args); - if (this.messageHandler && this.fnArray.length >= CHUNK_SIZE) { - this.flush(); + if (this.messageHandler) { + this.fnArray[this.fnIndex++] = fn; + this.argsArray.push(args); + if (this.fnIndex >= CHUNK_SIZE) { + this.flush(); + } + } else { + this.fnArray.push(fn); + this.argsArray.push(args); } }, @@ -1475,7 +1503,7 @@ var OperatorList = (function OperatorListClosure() { return; } this.dependencies[dependency] = true; - this.addOp('dependency', [dependency]); + this.addOp(OPS.dependency, [dependency]); }, addDependencies: function(dependencies) { @@ -1485,15 +1513,17 @@ var OperatorList = (function OperatorListClosure() { }, addOpList: function(opList) { - Util.concatenateToArray(this.fnArray, opList.fnArray); - Util.concatenateToArray(this.argsArray, opList.argsArray); Util.extendObj(this.dependencies, opList.dependencies); + for (var i = 0, ii = opList.length; i < ii; i++) { + this.addOp(opList.fnArray[i], opList.argsArray[i]); + } }, getIR: function() { return { fnArray: this.fnArray, - argsArray: this.argsArray + argsArray: this.argsArray, + length: this.length }; }, @@ -1503,12 +1533,13 @@ var OperatorList = (function OperatorListClosure() { operatorList: { fnArray: this.fnArray, argsArray: this.argsArray, - lastChunk: lastChunk + lastChunk: lastChunk, + length: this.length }, pageIndex: this.pageIndex }); this.dependencies = []; - this.fnArray = []; + this.fnIndex = 0; this.argsArray = []; } }; diff --git a/src/display/api.js b/src/display/api.js index a170b13f5..2ad762dd1 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -481,10 +481,10 @@ var PDFPageProxy = (function PDFPageProxyClosure() { */ _renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk) { // Add the new chunk to the current operator list. - Util.concatenateToArray(this.operatorList.fnArray, - operatorListChunk.fnArray); - Util.concatenateToArray(this.operatorList.argsArray, - operatorListChunk.argsArray); + for (var i = 0, ii = operatorListChunk.length; i < ii; i++) { + this.operatorList.fnArray.push(operatorListChunk.fnArray[i]); + this.operatorList.argsArray.push(operatorListChunk.argsArray[i]); + } this.operatorList.lastChunk = operatorListChunk.lastChunk; // Notify all the rendering tasks there are more operators to be consumed. @@ -1094,7 +1094,7 @@ var InternalRenderTask = (function InternalRenderTaskClosure() { this.operatorListIdx, this._continue.bind(this), this.stepper); - if (this.operatorListIdx === this.operatorList.fnArray.length) { + if (this.operatorListIdx === this.operatorList.argsArray.length) { this.running = false; if (this.operatorList.lastChunk) { this.gfx.endDrawing(); diff --git a/src/display/canvas.js b/src/display/canvas.js index 0c5ca513f..d8884955b 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -17,7 +17,7 @@ /* globals ColorSpace, DeviceCmykCS, DeviceGrayCS, DeviceRgbCS, error, FONT_IDENTITY_MATRIX, IDENTITY_MATRIX, ImageData, isArray, isNum, Pattern, TilingPattern, TODO, Util, warn, assert, info, - TextRenderingMode */ + TextRenderingMode, OPS */ 'use strict'; @@ -538,7 +538,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var commonObjs = this.commonObjs; var objs = this.objs; - var fnName; + var fnId; var slowCommands = this.slowCommands; while (true) { @@ -547,10 +547,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { return i; } - fnName = fnArray[i]; + fnId = fnArray[i]; - if (fnName !== 'dependency') { - this[fnName].apply(this, argsArray[i]); + if (fnId !== OPS.dependency) { + this[fnId].apply(this, argsArray[i]); } else { var deps = argsArray[i]; for (var n = 0, nn = deps.length; n < nn; n++) { @@ -580,7 +580,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { // 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 && slowCommands[fnName] && Date.now() > endTime) { + if (continueCallback && slowCommands[fnId] && Date.now() > endTime) { setTimeout(continueCallback, 0); return i; } @@ -1867,6 +1867,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } }; + for (var op in OPS) { + CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op]; + } + return CanvasGraphics; })(); diff --git a/src/shared/annotation.js b/src/shared/annotation.js index 8d0630219..67365f22f 100644 --- a/src/shared/annotation.js +++ b/src/shared/annotation.js @@ -16,7 +16,7 @@ */ /* globals Util, isDict, isName, stringToPDFString, TODO, Dict, Stream, stringToBytes, PDFJS, isWorker, assert, NotImplementedException, - Promise, isArray, ObjectLoader, isValidUrl, OperatorList */ + Promise, isArray, ObjectLoader, isValidUrl, OperatorList, OPS */ 'use strict'; @@ -188,9 +188,9 @@ var Annotation = (function AnnotationClosure() { resourcesPromise.then(function(resources) { var opList = new OperatorList(); - opList.addOp('beginAnnotation', [data.rect, transform, matrix]); + opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]); evaluator.getOperatorList(this.appearance, resources, opList); - opList.addOp('endAnnotation', []); + opList.addOp(OPS.endAnnotation, []); promise.resolve(opList); }.bind(this)); @@ -284,12 +284,12 @@ var Annotation = (function AnnotationClosure() { annotationPromises.push(annotations[i].getOperatorList(partialEvaluator)); } Promise.all(annotationPromises).then(function(datas) { - opList.addOp('beginAnnotations', []); + opList.addOp(OPS.beginAnnotations, []); for (var i = 0, n = datas.length; i < n; ++i) { var annotOpList = datas[i]; opList.addOpList(annotOpList); } - opList.addOp('endAnnotations', []); + opList.addOp(OPS.endAnnotations, []); annotationsReadyPromise.resolve(); }, reject); @@ -465,10 +465,10 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() { data.rgb = [0, 0, 0]; // TODO THIS DOESN'T MAKE ANY SENSE SINCE THE fnArray IS EMPTY! for (var i = 0, n = fnArray.length; i < n; ++i) { - var fnName = appearanceFnArray[i]; + var fnId = appearanceFnArray[i]; var args = appearanceArgsArray[i]; - if (fnName === 'setFont') { + if (fnId === OPS.setFont) { data.fontRefName = args[0]; var size = args[1]; if (size < 0) { @@ -478,9 +478,9 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() { data.fontDirection = 1; data.fontSize = size; } - } else if (fnName === 'setFillRGBColor') { + } else if (fnId === OPS.setFillRGBColor) { data.rgb = args; - } else if (fnName === 'setFillGray') { + } else if (fnId === OPS.setFillGray) { var rgbValue = args[0] * 255; data.rgb = [rgbValue, rgbValue, rgbValue]; } diff --git a/src/shared/util.js b/src/shared/util.js index ad1694167..cccd284d2 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -49,6 +49,98 @@ if (!globalScope.PDFJS) { globalScope.PDFJS.pdfBug = false; +// All the possible operations for an operator list. +var OPS = PDFJS.OPS = { + // Intentially start from 1 so it is easy to spot bad operators that will be + // 0's. + dependency: 1, + setLineWidth: 2, + setLineCap: 3, + setLineJoin: 4, + setMiterLimit: 5, + setDash: 6, + setRenderingIntent: 7, + setFlatness: 8, + setGState: 9, + save: 10, + restore: 11, + transform: 12, + moveTo: 13, + lineTo: 14, + curveTo: 15, + curveTo2: 16, + curveTo3: 17, + closePath: 18, + rectangle: 19, + stroke: 20, + closeStroke: 21, + fill: 22, + eoFill: 23, + fillStroke: 24, + eoFillStroke: 25, + closeFillStroke: 26, + closeEOFillStroke: 27, + endPath: 28, + clip: 29, + eoClip: 30, + beginText: 31, + endText: 32, + setCharSpacing: 33, + setWordSpacing: 34, + setHScale: 35, + setLeading: 36, + setFont: 37, + setTextRenderingMode: 38, + setTextRise: 39, + moveText: 40, + setLeadingMoveText: 41, + setTextMatrix: 42, + nextLine: 43, + showText: 44, + showSpacedText: 45, + nextLineShowText: 46, + nextLineSetSpacingShowText: 47, + setCharWidth: 48, + setCharWidthAndBounds: 49, + setStrokeColorSpace: 50, + setFillColorSpace: 51, + setStrokeColor: 52, + setStrokeColorN: 53, + setFillColor: 54, + setFillColorN: 55, + setStrokeGray: 56, + setFillGray: 57, + setStrokeRGBColor: 58, + setFillRGBColor: 59, + setStrokeCMYKColor: 60, + setFillCMYKColor: 61, + shadingFill: 62, + beginInlineImage: 63, + beginImageData: 64, + endInlineImage: 65, + paintXObject: 66, + markPoint: 67, + markPointProps: 68, + beginMarkedContent: 69, + beginMarkedContentProps: 70, + endMarkedContent: 71, + beginCompat: 72, + endCompat: 73, + paintFormXObjectBegin: 74, + paintFormXObjectEnd: 75, + beginGroup: 76, + endGroup: 77, + beginAnnotations: 78, + endAnnotations: 79, + beginAnnotation: 80, + endAnnotation: 81, + paintJpegXObject: 82, + paintImageMaskXObject: 83, + paintImageMaskXObjectGroup: 84, + paintImageXObject: 85, + paintInlineImageXObject: 86, + paintInlineImageXObjectGroup: 87 +}; // Use only for debugging purposes. This should not be used in any code that is // in mozilla master. diff --git a/test/unit/evaluator_spec.js b/test/unit/evaluator_spec.js index fd023fc4d..48006f839 100644 --- a/test/unit/evaluator_spec.js +++ b/test/unit/evaluator_spec.js @@ -1,6 +1,6 @@ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ -/* globals expect, it, describe, PartialEvaluator, StringStream */ +/* globals expect, it, describe, PartialEvaluator, StringStream, OPS */ 'use strict'; @@ -39,7 +39,7 @@ describe('evaluator', function() { var result = evaluator.getOperatorList(stream, new ResourcesMock()); expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray.length).toEqual(1); - expect(result.fnArray[0]).toEqual('save'); + expect(result.fnArray[0]).toEqual(OPS.save); expect(result.argsArray[0].length).toEqual(0); }); @@ -51,7 +51,7 @@ describe('evaluator', function() { var result = evaluator.getOperatorList(stream, new ResourcesMock()); expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray.length).toEqual(1); - expect(result.fnArray[0]).toEqual('restore'); + expect(result.fnArray[0]).toEqual(OPS.restore); }); it('should handle two glued operations', function() { @@ -64,8 +64,8 @@ describe('evaluator', function() { var result = evaluator.getOperatorList(stream, resources); expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray.length).toEqual(2); - expect(result.fnArray[0]).toEqual('paintXObject'); - expect(result.fnArray[1]).toEqual('restore'); + expect(result.fnArray[0]).toEqual(OPS.paintXObject); + expect(result.fnArray[1]).toEqual(OPS.restore); }); it('should handle tree glued operations', function() { @@ -76,9 +76,9 @@ describe('evaluator', function() { var result = evaluator.getOperatorList(stream, new ResourcesMock()); expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray.length).toEqual(3); - expect(result.fnArray[0]).toEqual('save'); - expect(result.fnArray[1]).toEqual('save'); - expect(result.fnArray[2]).toEqual('save'); + expect(result.fnArray[0]).toEqual(OPS.save); + expect(result.fnArray[1]).toEqual(OPS.save); + expect(result.fnArray[2]).toEqual(OPS.save); }); it('should handle three glued operations #2', function() { @@ -91,9 +91,9 @@ describe('evaluator', function() { var result = evaluator.getOperatorList(stream, resources); expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray.length).toEqual(3); - expect(result.fnArray[0]).toEqual('eoFillStroke'); - expect(result.fnArray[1]).toEqual('fillStroke'); - expect(result.fnArray[2]).toEqual('eoFill'); + expect(result.fnArray[0]).toEqual(OPS.eoFillStroke); + expect(result.fnArray[1]).toEqual(OPS.fillStroke); + expect(result.fnArray[2]).toEqual(OPS.eoFill); }); it('should handle glued operations and operands', function() { @@ -104,8 +104,8 @@ describe('evaluator', function() { var result = evaluator.getOperatorList(stream, new ResourcesMock()); expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray.length).toEqual(2); - expect(result.fnArray[0]).toEqual('save'); - expect(result.fnArray[1]).toEqual('setTextRise'); + expect(result.fnArray[0]).toEqual(OPS.save); + expect(result.fnArray[1]).toEqual(OPS.setTextRise); expect(result.argsArray.length).toEqual(2); expect(result.argsArray[1].length).toEqual(1); expect(result.argsArray[1][0]).toEqual(5); @@ -119,9 +119,9 @@ describe('evaluator', function() { var result = evaluator.getOperatorList(stream, new ResourcesMock()); expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray.length).toEqual(3); - expect(result.fnArray[0]).toEqual('setFlatness'); - expect(result.fnArray[1]).toEqual('setRenderingIntent'); - expect(result.fnArray[2]).toEqual('save'); + expect(result.fnArray[0]).toEqual(OPS.setFlatness); + expect(result.fnArray[1]).toEqual(OPS.setRenderingIntent); + expect(result.fnArray[2]).toEqual(OPS.save); expect(result.argsArray.length).toEqual(3); expect(result.argsArray[0].length).toEqual(1); expect(result.argsArray[0][0]).toEqual(true); @@ -141,7 +141,7 @@ describe('evaluator', function() { var result = evaluator.getOperatorList(stream, new ResourcesMock()); expect(result.argsArray[0][0]).toEqual(5); expect(result.argsArray[0][1]).toEqual(1); - expect(result.fnArray[0]).toEqual('setCharWidth'); + expect(result.fnArray[0]).toEqual(OPS.setCharWidth); }); it('should execute if too many arguments', function() { var evaluator = new PartialEvaluator(new PdfManagerMock(), @@ -152,7 +152,7 @@ describe('evaluator', function() { expect(result.argsArray[0][0]).toEqual(5); expect(result.argsArray[0][1]).toEqual(1); expect(result.argsArray[0][2]).toEqual(4); - expect(result.fnArray[0]).toEqual('setCharWidth'); + expect(result.fnArray[0]).toEqual(OPS.setCharWidth); }); it('should skip if too few arguments', function() { var evaluator = new PartialEvaluator(new PdfManagerMock(), diff --git a/web/debugger.js b/web/debugger.js index da57fc3dd..a70ad0cd9 100644 --- a/web/debugger.js +++ b/web/debugger.js @@ -240,6 +240,8 @@ var Stepper = (function StepperClosure() { return out; } + var opMap = null; + var glyphCommands = { 'showText': 0, 'showSpacedText': 0, @@ -271,6 +273,12 @@ var Stepper = (function StepperClosure() { headerRow.appendChild(c('th', 'args')); panel.appendChild(content); this.table = table; + if (!opMap) { + opMap = Object.create(null); + for (var key in PDFJS.OPS) { + opMap[PDFJS.OPS[key]] = key; + } + } }, updateOperatorList: function updateOperatorList(operatorList) { var self = this; @@ -300,7 +308,7 @@ var Stepper = (function StepperClosure() { breakCell.appendChild(cbox); line.appendChild(breakCell); line.appendChild(c('td', i.toString())); - var fn = operatorList.fnArray[i]; + var fn = opMap[operatorList.fnArray[i]]; var decArgs = args; if (fn in glyphCommands) { var glyphIndex = glyphCommands[fn];