]> git.parisson.com Git - pdf.js.git/commitdiff
#123, part 1: create PartialEvaluator
authorChris Jones <jones.chris.g@gmail.com>
Sun, 10 Jul 2011 04:29:35 +0000 (21:29 -0700)
committerChris Jones <jones.chris.g@gmail.com>
Sun, 10 Jul 2011 04:29:35 +0000 (21:29 -0700)
pdf.js

diff --git a/pdf.js b/pdf.js
index 674341f7d4a7c6e1ce2c89bb9cf6e2900be39527..b2554cd74469c41cda1e7bd32834318c3427c4b3 100644 (file)
--- a/pdf.js
+++ b/pdf.js
@@ -3385,18 +3385,13 @@ var Encodings = {
 
 var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
 
-// <canvas> contexts store most of the state we need natively.
-// However, PDF needs a bit more state, which we store here.
-var CanvasExtraState = (function() {
+var EvalState = (function() {
   function constructor() {
     // Are soft masks and alpha values shapes or opacities?
     this.alphaIsShape = false;
     this.fontSize = 0;
     this.textMatrix = IDENTITY_MATRIX;
     this.leading = 0;
-    // Current point (in user coordinates)
-    this.x = 0;
-    this.y = 0;
     // Start of text line (in text coordinates)
     this.lineX = 0;
     this.lineY = 0;
@@ -3413,126 +3408,187 @@ var CanvasExtraState = (function() {
   return constructor;
 })();
 
-function ScratchCanvas(width, height) {
-  var canvas = document.createElement('canvas');
-  canvas.width = width;
-  canvas.height = height;
-  return canvas;
-}
-
-var CanvasGraphics = (function() {
-  function constructor(canvasCtx, imageCanvas) {
-    this.ctx = canvasCtx;
-    this.current = new CanvasExtraState();
-    this.stateStack = [];
-    this.pendingClip = null;
-    this.res = null;
-    this.xobjs = null;
-    this.ScratchCanvas = imageCanvas || ScratchCanvas;
+var PartialEvaluator = (function() {
+  function constructor() {
+    this.state = new EvalState();
+    this.stateStack = [ ];
   }
 
-  var LINE_CAP_STYLES = ['butt', 'round', 'square'];
-  var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
-  var NORMAL_CLIP = {};
-  var EO_CLIP = {};
+  var OP_MAP = {
+    // Graphics state
+    w: 'setLineWidth',
+    J: 'setLineCap',
+    j: 'setLineJoin',
+    M: 'setMiterLimit',
+    d: 'setDash',
+    ri: 'setRenderingIntent',
+    i: 'setFlatness',
+    gs: 'setGState',
+    q: 'save',
+    Q: 'restore',
+    cm: 'transform',
 
-  // Used for tiling patterns
-  var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2;
+    // Path
+    m: 'moveTo',
+    l: 'lineTo',
+    c: 'curveTo',
+    v: 'curveTo2',
+    y: 'curveTo3',
+    h: 'closePath',
+    re: 'rectangle',
+    S: 'stroke',
+    s: 'closeStroke',
+    f: 'fill',
+    F: 'fill',
+    'f*': 'eoFill',
+    B: 'fillStroke',
+    'B*': 'eoFillStroke',
+    b: 'closeFillStroke',
+    'b*': 'closeEOFillStroke',
+    n: 'endPath',
+
+    // Clipping
+    W: 'clip',
+    'W*': 'eoClip',
+
+    // Text
+    BT: 'beginText',
+    ET: 'endText',
+    Tc: 'setCharSpacing',
+    Tw: 'setWordSpacing',
+    Tz: 'setHScale',
+    TL: 'setLeading',
+    Tf: 'setFont',
+    Tr: 'setTextRenderingMode',
+    Ts: 'setTextRise',
+    Td: 'moveText',
+    TD: 'setLeadingMoveText',
+    Tm: 'setTextMatrix',
+    'T*': 'nextLine',
+    Tj: 'showText',
+    TJ: 'showSpacedText',
+    "'": 'nextLineShowText',
+    '"': 'nextLineSetSpacingShowText',
+
+    // Type3 fonts
+    d0: 'setCharWidth',
+    d1: 'setCharWidthAndBounds',
+
+    // Color
+    CS: 'setStrokeColorSpace',
+    cs: 'setFillColorSpace',
+    SC: 'setStrokeColor',
+    SCN: 'setStrokeColorN',
+    sc: 'setFillColor',
+    scn: 'setFillColorN',
+    G: 'setStrokeGray',
+    g: 'setFillGray',
+    RG: 'setStrokeRGBColor',
+    rg: 'setFillRGBColor',
+    K: 'setStrokeCMYKColor',
+    k: 'setFillCMYKColor',
+
+    // Shading
+    sh: 'shadingFill',
+
+    // Images
+    BI: 'beginInlineImage',
+
+    // XObjects
+    Do: 'paintXObject',
+
+    // Marked content
+    MP: 'markPoint',
+    DP: 'markPointProps',
+    BMC: 'beginMarkedContent',
+    BDC: 'beginMarkedContentProps',
+    EMC: 'endMarkedContent',
+
+    // Compatibility
+    BX: 'beginCompat',
+    EX: 'endCompat'
+  };
 
   constructor.prototype = {
-    map: {
-      // Graphics state
-      w: 'setLineWidth',
-      J: 'setLineCap',
-      j: 'setLineJoin',
-      M: 'setMiterLimit',
-      d: 'setDash',
-      ri: 'setRenderingIntent',
-      i: 'setFlatness',
-      gs: 'setGState',
-      q: 'save',
-      Q: 'restore',
-      cm: 'transform',
-
-      // Path
-      m: 'moveTo',
-      l: 'lineTo',
-      c: 'curveTo',
-      v: 'curveTo2',
-      y: 'curveTo3',
-      h: 'closePath',
-      re: 'rectangle',
-      S: 'stroke',
-      s: 'closeStroke',
-      f: 'fill',
-      F: 'fill',
-      'f*': 'eoFill',
-      B: 'fillStroke',
-      'B*': 'eoFillStroke',
-      b: 'closeFillStroke',
-      'b*': 'closeEOFillStroke',
-      n: 'endPath',
-
-      // Clipping
-      W: 'clip',
-      'W*': 'eoClip',
-
-      // Text
-      BT: 'beginText',
-      ET: 'endText',
-      Tc: 'setCharSpacing',
-      Tw: 'setWordSpacing',
-      Tz: 'setHScale',
-      TL: 'setLeading',
-      Tf: 'setFont',
-      Tr: 'setTextRenderingMode',
-      Ts: 'setTextRise',
-      Td: 'moveText',
-      TD: 'setLeadingMoveText',
-      Tm: 'setTextMatrix',
-      'T*': 'nextLine',
-      Tj: 'showText',
-      TJ: 'showSpacedText',
-      "'": 'nextLineShowText',
-      '"': 'nextLineSetSpacingShowText',
-
-      // Type3 fonts
-      d0: 'setCharWidth',
-      d1: 'setCharWidthAndBounds',
-
-      // Color
-      CS: 'setStrokeColorSpace',
-      cs: 'setFillColorSpace',
-      SC: 'setStrokeColor',
-      SCN: 'setStrokeColorN',
-      sc: 'setFillColor',
-      scn: 'setFillColorN',
-      G: 'setStrokeGray',
-      g: 'setFillGray',
-      RG: 'setStrokeRGBColor',
-      rg: 'setFillRGBColor',
-      K: 'setStrokeCMYKColor',
-      k: 'setFillCMYKColor',
-
-      // Shading
-      sh: 'shadingFill',
-
-      // Images
-      BI: 'beginInlineImage',
-
-      // XObjects
-      Do: 'paintXObject',
-
-      // Marked content
-      MP: 'markPoint',
-      DP: 'markPointProps',
-      BMC: 'beginMarkedContent',
-      BDC: 'beginMarkedContentProps',
-      EMC: 'endMarkedContent',
-
-      // Compatibility
-      BX: 'beginCompat',
-      EX: 'endCompat'
+    eval: function(stream, xref, resources, fonts) {
+      resources = xref.fetchIfRef(resources) || new Dict();
+      var xobjs = xref.fetchIfRef(resources.get('XObject')) || new Dict();
+
+      var parser = new Parser(new Lexer(stream), false);
+      var objpool = [];
+
+      function emitArg(arg) {
+        if (typeof arg == 'object' || typeof arg == 'string') {
+          var index = objpool.length;
+          objpool[index] = arg;
+          return 'objpool[' + index + ']';
+        }
+        return arg;
+      }
+
+      var src = '';
+
+      var args = [];
+      var obj;
+      while (!IsEOF(obj = parser.getObj())) {
+        if (IsCmd(obj)) {
+          var cmd = obj.cmd;
+          var fn = OP_MAP[cmd];
+          assertWellFormed(fn, "Unknown command '" + cmd + "'");
+          // TODO figure out how to type-check vararg functions
+
+          if (cmd == 'Do' && !args[0].code) { // eagerly compile XForm objects
+            var name = args[0].name;
+            var xobj = xobjs.get(name);
+            if (xobj) {
+              xobj = xref.fetchIfRef(xobj);
+              assertWellFormed(IsStream(xobj), 'XObject should be a stream');
+
+              var type = xobj.dict.get('Subtype');
+              assertWellFormed(
+                IsName(type),
+                'XObject should have a Name subtype'
+              );
+
+              if ('Form' == type.name) {
+                args[0].code = this.eval(xobj,
+                                         xref,
+                                         xobj.dict.get('Resources'),
+                                         fonts);
+              }
+            }
+          } else if (cmd == 'Tf') { // eagerly collect all fonts
+            var fontRes = resources.get('Font');
+            if (fontRes) {
+              fontRes = xref.fetchIfRef(fontRes);
+              var font = xref.fetchIfRef(fontRes.get(args[0].name));
+              assertWellFormed(IsDict(font));
+              if (!font.translated) {
+                font.translated = this.translateFont(font, xref, resources);
+                if (fonts && font.translated) {
+                  // keep track of each font we translated so the caller can
+                  // load them asynchronously before calling display on a page
+                  fonts.push(font.translated);
+                }
+              }
+            }
+          }
+
+          src += 'this.';
+          src += fn;
+          src += '(';
+          src += args.map(emitArg).join(',');
+          src += ');\n';
+
+          args.length = 0;
+        } else {
+          assertWellFormed(args.length <= 33, 'Too many arguments');
+          args.push(obj);
+        }
+      }
+
+      var fn = Function('objpool', src);
+      return function(gfx) { fn.call(gfx, objpool); };
     },
 
     translateFont: function(fontDict, xref, resources) {
@@ -3697,7 +3753,66 @@ var CanvasGraphics = (function() {
         properties: properties
       };
     },
+  };
+
+  return constructor;
+})();
 
+// <canvas> contexts store most of the state we need natively.
+// However, PDF needs a bit more state, which we store here.
+var CanvasExtraState = (function() {
+  function constructor() {
+    // Are soft masks and alpha values shapes or opacities?
+    this.alphaIsShape = false;
+    this.fontSize = 0;
+    this.textMatrix = IDENTITY_MATRIX;
+    this.leading = 0;
+    // Current point (in user coordinates)
+    this.x = 0;
+    this.y = 0;
+    // Start of text line (in text coordinates)
+    this.lineX = 0;
+    this.lineY = 0;
+    // Character and word spacing
+    this.charSpace = 0;
+    this.wordSpace = 0;
+    this.textHScale = 100;
+    // Color spaces
+    this.fillColorSpace = null;
+    this.strokeColorSpace = null;
+  }
+  constructor.prototype = {
+  };
+  return constructor;
+})();
+
+function ScratchCanvas(width, height) {
+  var canvas = document.createElement('canvas');
+  canvas.width = width;
+  canvas.height = height;
+  return canvas;
+}
+
+var CanvasGraphics = (function() {
+  function constructor(canvasCtx, imageCanvas) {
+    this.ctx = canvasCtx;
+    this.current = new CanvasExtraState();
+    this.stateStack = [];
+    this.pendingClip = null;
+    this.res = null;
+    this.xobjs = null;
+    this.ScratchCanvas = imageCanvas || ScratchCanvas;
+  }
+
+  var LINE_CAP_STYLES = ['butt', 'round', 'square'];
+  var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
+  var NORMAL_CLIP = {};
+  var EO_CLIP = {};
+
+  // Used for tiling patterns
+  var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2;
+
+  constructor.prototype = {
     beginDrawing: function(mediaBox) {
       var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height;
       this.ctx.save();
@@ -3705,6 +3820,11 @@ var CanvasGraphics = (function() {
       this.ctx.translate(0, -mediaBox.height);
     },
 
+    compile: function(stream, xref, resources, fonts) {
+      var pe = new PartialEvaluator();
+      return pe.eval(stream, xref, resources, fonts);
+    },
+
     execute: function(code, xref, resources) {
       resources = xref.fetchIfRef(resources) || new Dict();
       var savedXref = this.xref, savedRes = this.res, savedXobjs = this.xobjs;
@@ -3719,88 +3839,6 @@ var CanvasGraphics = (function() {
       this.xref = savedXref;
     },
 
-    compile: function(stream, xref, resources, fonts) {
-      resources = xref.fetchIfRef(resources) || new Dict();
-      var xobjs = xref.fetchIfRef(resources.get('XObject')) || new Dict();
-
-      var parser = new Parser(new Lexer(stream), false);
-      var objpool = [];
-
-      function emitArg(arg) {
-        if (typeof arg == 'object' || typeof arg == 'string') {
-          var index = objpool.length;
-          objpool[index] = arg;
-          return 'objpool[' + index + ']';
-        }
-        return arg;
-      }
-
-      var src = '';
-
-      var args = [];
-      var map = this.map;
-      var obj;
-      while (!IsEOF(obj = parser.getObj())) {
-        if (IsCmd(obj)) {
-          var cmd = obj.cmd;
-          var fn = map[cmd];
-          assertWellFormed(fn, "Unknown command '" + cmd + "'");
-          // TODO figure out how to type-check vararg functions
-
-          if (cmd == 'Do' && !args[0].code) { // eagerly compile XForm objects
-            var name = args[0].name;
-            var xobj = xobjs.get(name);
-            if (xobj) {
-              xobj = xref.fetchIfRef(xobj);
-              assertWellFormed(IsStream(xobj), 'XObject should be a stream');
-
-              var type = xobj.dict.get('Subtype');
-              assertWellFormed(
-                IsName(type),
-                'XObject should have a Name subtype'
-              );
-
-              if ('Form' == type.name) {
-                args[0].code = this.compile(xobj,
-                                            xref,
-                                            xobj.dict.get('Resources'),
-                                            fonts);
-              }
-            }
-          } else if (cmd == 'Tf') { // eagerly collect all fonts
-            var fontRes = resources.get('Font');
-            if (fontRes) {
-              fontRes = xref.fetchIfRef(fontRes);
-              var font = xref.fetchIfRef(fontRes.get(args[0].name));
-              assertWellFormed(IsDict(font));
-              if (!font.translated) {
-                font.translated = this.translateFont(font, xref, resources);
-                if (fonts && font.translated) {
-                  // keep track of each font we translated so the caller can
-                  // load them asynchronously before calling display on a page
-                  fonts.push(font.translated);
-                }
-              }
-            }
-          }
-
-          src += 'this.';
-          src += fn;
-          src += '(';
-          src += args.map(emitArg).join(',');
-          src += ');\n';
-
-          args.length = 0;
-        } else {
-          assertWellFormed(args.length <= 33, 'Too many arguments');
-          args.push(obj);
-        }
-      }
-
-      var fn = Function('objpool', src);
-      return function(gfx) { fn.call(gfx, objpool); };
-    },
-
     endDrawing: function() {
       this.ctx.restore();
     },