]> git.parisson.com Git - pdf.js.git/commitdiff
fixed gradients, need to work on tiling
authorsbarman <sbarman@L3CWZ5T.(none)>
Wed, 13 Jul 2011 22:43:09 +0000 (15:43 -0700)
committersbarman <sbarman@L3CWZ5T.(none)>
Wed, 13 Jul 2011 22:43:09 +0000 (15:43 -0700)
pdf.js

diff --git a/pdf.js b/pdf.js
index 6f2e0710fa9d173e7a114f7ede7f781286d22af3..2af03dce815f0b975049121315a0598f26d3af3c 100644 (file)
--- a/pdf.js
+++ b/pdf.js
@@ -4019,7 +4019,7 @@ var CanvasGraphics = (function() {
       var fillColor = this.current.fillColor;
 
       if (fillColor.type === "Pattern") {
-        this.ctx.fillStyle = fillColor.patternFn.apply(this, ctx);
+        this.ctx.fillStyle = fillColor.getPattern(ctx);
         ctx.fill();
       } else {
         ctx.fill();
@@ -4241,9 +4241,9 @@ var CanvasGraphics = (function() {
       var cs = this.current.fillColorSpace;
 
       if (cs.name == 'Pattern') {
-        // return a set of functions which will set the pattern
-        // when fill is called
-        var pattern = this.getPattern(cs, arguments);
+        // wait until fill to actually get the pattern
+        var pattern = Pattern.parse(cs, arguments, this.xref, this.res,
+                                    this.ctx);
         this.current.fillColor = pattern;
         this.current.fillColor.type = "Pattern";
       } else {
@@ -4251,139 +4251,12 @@ var CanvasGraphics = (function() {
       }
     },
     getPattern: function(cs, args) {
-      var length = args.length;
-      var base = cs.base;
-      if (base) {
-        var baseComps = base.numComps;
-
-        var color = [];
-        for (var i = 0; i < baseComps; ++i)
-          color.push(args[i]);
-
-        color = base.getRgb(color);
-      }
-
-      var patternName = args[length - 1];
-      if (!IsName(patternName))
-        error("Bad args to getPattern");
-
-      var xref = this.xref;
-      var patternRes = xref.fetchIfRef(this.res.get("Pattern"));
-      if (!patternRes)
-        error("Unable to find pattern resource");
-
-      var pattern = xref.fetchIfRef(patternRes.get(patternName.name));
-      var dict = IsStream(pattern) ? pattern.dict : pattern;
-
-      var types = [null, this.getTilingPattern, this.getShadingPattern];
-
-      var typeNum = dict.get("PatternType");
-      var patternFn = types[typeNum];
-      if (!patternFn)
-        error("Unhandled pattern type");
-      return patternFn.call(this, pattern, dict, color);
     },
     getShadingPattern: function(pattern, dict) {
-      var matrix = dict.get("Matrix");
-      
-      this.save();
-      this.transform.apply(this, matrix);
-      var shading = this.getShading(pattern.get("Shading"));
-      this.restore();
-      return shading;
      // TODO('store transform so it can be applied before every fill');
      // return shading;
     },
     getTilingPattern: function(pattern, dict, color) {
-      function multiply(m, tm) {
-        var a = m[0] * tm[0] + m[1] * tm[2];
-        var b = m[0] * tm[1] + m[1] * tm[3];
-        var c = m[2] * tm[0] + m[3] * tm[2];
-        var d = m[2] * tm[1] + m[3] * tm[3];
-        var e = m[4] * tm[0] + m[5] * tm[2] + tm[4];
-        var f = m[4] * tm[1] + m[5] * tm[3] + tm[5];
-        return [a, b, c, d, e, f];
-      };
-
-      this.save();
-      var ctx = this.ctx;
-
-
-      TODO('TilingType');
-
-      var matrix = dict.get('Matrix') || IDENTITY_MATRIX;
-
-      var bbox = dict.get('BBox');
-      var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3];
-
-      var xstep = dict.get('XStep');
-      var ystep = dict.get('YStep');
-
-      // top left corner should correspond to the top left of the bbox
-      var topLeft = this.applyTransform(x0, y0, matrix);
-      // we want the canvas to be as large as the step size
-      var botRight = this.applyTransform(x0 + xstep, y0 + ystep, matrix);
-
-      var width = botRight[0] - topLeft[0];
-      var height = botRight[1] - topLeft[1];
-
-      // TODO: hack to avoid OOM, remove then pattern code is fixed
-      if (Math.abs(width) > 8192 || Math.abs(height) > 8192) {
-        this.restore();
-        return 'hotpink';
-      }
-
-      var tmpCanvas = new this.ScratchCanvas(width, height);
-
-      // set the new canvas element context as the graphics context
-      var tmpCtx = tmpCanvas.getContext('2d');
-      var savedCtx = ctx;
-      this.ctx = tmpCtx;
-
-      var paintType = dict.get('PaintType');
-      switch (paintType) {
-      case PAINT_TYPE_COLORED:
-        tmpCtx.fillStyle = savedCtx.fillStyle;
-        tmpCtx.strokeStyle = savedCtx.strokeStyle;
-        break;
-      case PAINT_TYPE_UNCOLORED:
-        color = this.makeCssRgb.apply(this, color);
-        tmpCtx.fillStyle = color;
-        tmpCtx.strokeStyle = color;
-        break;
-      default:
-        error('Unsupported paint type');
-      }
-
-      // normalize transform matrix so each step
-      // takes up the entire tmpCanvas (need to remove white borders)
-      if (matrix[1] === 0 && matrix[2] === 0) {
-        matrix[0] = tmpCanvas.width / xstep;
-        matrix[3] = tmpCanvas.height / ystep;
-        topLeft = this.applyTransform(x0, y0, matrix);
-      }
-
-      // move the top left corner of bounding box to [0,0]
-      matrix = multiply(matrix, [1, 0, 0, 1, -topLeft[0], -topLeft[1]]);
-      
-      this.transform.apply(this, matrix);
-
-      if (bbox && IsArray(bbox) && 4 == bbox.length) {
-        this.rectangle.apply(this, bbox);
-        this.clip();
-        this.endPath();
-      }
-
-      var xref = this.xref;
-      var res = xref.fetchIfRef(dict.get('Resources'));
-      if (!pattern.code)
-        pattern.code = this.compile(pattern, xref, res, []);
-      this.execute(pattern.code, xref, res);
-
-      this.ctx = savedCtx;
-      this.restore();
-
-      return this.ctx.createPattern(tmpCanvas, 'repeat');
     },
     setStrokeGray: function(gray) {
       this.setStrokeRGBColor(gray, gray, gray);
@@ -4421,10 +4294,10 @@ var CanvasGraphics = (function() {
       if (!shading)
         error('No shading object found');
 
-      var shadingFill = this.getShading(shading);
+      var shadingFill = Pattern.parseShading(shading, null, xref, res, ctx);
 
       this.save();
-      ctx.fillStyle = shadingFill.patternFn.apply(this, ctx);
+      ctx.fillStyle = shadingFill.getPattern(ctx);
 
       var inv = ctx.mozCurrentTransformInverse;
       if (inv) {
@@ -4456,181 +4329,10 @@ var CanvasGraphics = (function() {
       this.restore();
     },
     getShading: function(shading) {
-      this.save();
-      
-      shading = this.xref.fetchIfRef(shading);
-      var dict = IsStream(shading) ? shading.dict : shading;
-
-      var bbox = dict.get('BBox');
-      if (bbox && IsArray(bbox) && 4 == bbox.length) {
-        this.rectangle.apply(this, bbox);
-        this.clip();
-        this.endPath();
-      }
-
-      var background = dict.get('Background');
-      if (background)
-        TODO('handle background colors');
-
-      var cs = dict.get('ColorSpace', 'CS');
-      cs = ColorSpace.parse(cs, this.xref, this.res);
-
-      var types = [null,
-          null,
-          this.getAxialShading,
-          this.getRadialShading];
-
-      var typeNum = dict.get('ShadingType');
-      var shadingFn = types[typeNum];
-      
-      this.restore();
-
-      // Most likely we will not implement other types of shading
-      // unless the browser supports them
-      if (!shadingFn) {
-        warn("Unknown or NYI type of shading '"+ typeNum +"'");
-        return 'hotpink';
-      }
-
-      return shadingFn.call(this, shading, cs);
     },
     getAxialShading: function(sh, cs) {
-      var coordsArr = sh.get('Coords');
-
-      var t0 = 0.0, t1 = 1.0;
-      if (sh.has('Domain')) {
-        var domainArr = sh.get('Domain');
-        t0 = domainArr[0], t1 = domainArr[1];
-      }
-
-      var extendStart = false, extendEnd = false;
-      if (sh.has('Extend')) {
-        var extendArr = sh.get('Extend');
-        extendStart = extendArr[0], extendEnd = extendArr[1];
-        TODO('Support extend');
-      }
-      var fnObj = sh.get('Function');
-      fnObj = this.xref.fetchIfRef(fnObj);
-      if (IsArray(fnObj))
-        error('No support for array of functions');
-      else if (!IsPDFFunction(fnObj))
-        error('Invalid function');
-      var fn = new PDFFunction(this.xref, fnObj);
-
-      // 10 samples seems good enough for now, but probably won't work
-      // if there are sharp color changes. Ideally, we would implement
-      // the spec faithfully and add lossless optimizations.
-      var step = (t1 - t0) / 10;
-      var diff = t1 - t0;
-
-      var colorStops = [];
-      for (var i = t0; i <= t1; i += step) {
-        var color = fn.func([i]);
-        var rgbColor = this.makeCssRgb.apply(this, cs.getRgb(color));
-        colorStops.push([(i - t0) / diff, rgbColor]);
-      }
-      
-      var patternMatrix = this.ctx.mozCurrentTransform;
-
-      return {
-        patternFn : function() {
-          var x0 = coordsArr[0], y0 = coordsArr[1];
-          var x1 = coordsArr[2], y1 = coordsArr[3];
-          
-          // if the browser supports getting the tranform matrix, convert
-          // gradient coordinates from pattern space to current user space
-          if (patternMatrix) {
-            var userMatrix = this.ctx.mozCurrentTransformInverse;
-            
-            var p = this.applyTransform(x0, y0, patternMatrix);
-            p = this.applyTransform(p[0], p[1], userMatrix);
-            x0 = p[0];
-            y0 = p[1];
-
-            var p = this.applyTransform(x1, y1, patternMatrix);
-            p = this.applyTransform(p[0], p[1], userMatrix);
-            x1 = p[0];
-            y1 = p[1];
-          }
-
-          var gradient = 
-              this.ctx.createLinearGradient(x0, y0, x1, y1);
-          for (var i = 0, ii = colorStops.length; i < ii; ++i) {
-            var c = colorStops[i];
-            gradient.addColorStop(c[0], c[1]);
-          }
-          return gradient;
-        }
-      }
     },
     getRadialShading: function(sh, cs) {
-      var coordsArr = sh.get('Coords');
-
-      var t0 = 0.0, t1 = 1.0;
-      if (sh.has('Domain')) {
-        var domainArr = sh.get('Domain');
-        t0 = domainArr[0], t1 = domainArr[1];
-      }
-
-      var extendStart = false, extendEnd = false;
-      if (sh.has('Extend')) {
-        var extendArr = sh.get('Extend');
-        extendStart = extendArr[0], extendEnd = extendArr[1];
-        TODO('Support extend');
-      }
-      var fnObj = sh.get('Function');
-      fnObj = this.xref.fetchIfRef(fnObj);
-      if (IsArray(fnObj))
-        error('No support for array of functions');
-      else if (!IsPDFFunction(fnObj))
-        error('Invalid function');
-      var fn = new PDFFunction(this.xref, fnObj);
-
-      // 10 samples seems good enough for now, but probably won't work
-      // if there are sharp color changes. Ideally, we would implement
-      // the spec faithfully and add lossless optimizations.
-      var step = (t1 - t0) / 10;
-      var diff = t1 - t0;
-
-      var colorStops = [];
-      for (var i = t0; i <= t1; i += step) {
-        var color = fn.func([i]);
-        var rgbColor = this.makeCssRgb.apply(this, cs.getRgb(color));
-        colorStops.push([(i - t0) / diff, rgbColor]);
-      }
-
-      var patternMatrix = this.ctx.mozCurrentTransform;
-
-      return {
-        patternFn : function() {
-          var x0 = coordsArr[0], y0 = coordsArr[1], r0 = coordsArr[2];
-          var x1 = coordsArr[3], y1 = coordsArr[4], r1 = coordsArr[5];
-          
-          // if the browser supports getting the tranform matrix, convert
-          // gradient coordinates from pattern space to current user space
-          if (patternMatrix) {
-            var userMatrix = this.ctx.mozCurrentTransformInverse;
-            
-            var p = this.applyTransform(x0, y0, patternMatrix);
-            p = this.applyTransform(p[0], p[1], userMatrix);
-            x0 = p[0];
-            y0 = p[1];
-
-            var p = this.applyTransform(x1, y1, patternMatrix);
-            p = this.applyTransform(p[0], p[1], userMatrix);
-            x1 = p[0];
-            y1 = p[1];
-          }
-
-          var gradient = 
-              this.ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
-          for (var i = 0, ii = colorStops.length; i < ii; ++i) {
-            var c = colorStops[i];
-            gradient.addColorStop(c[0], c[1]);
-          }
-          return gradient;
-        }
-      }
     },
 
     // Images
@@ -4832,6 +4534,8 @@ var ColorSpace = (function() {
       }
     }
 
+    if (!xref.fetchIfRef)
+      console.log("blah");
     cs = xref.fetchIfRef(cs);
 
     if (IsName(cs)) {
@@ -5154,6 +4858,385 @@ var DeviceCmykCS = (function() {
   return constructor;
 })();
 
+var Pattern = (function() {
+  // Constructor should define this.getPattern
+  function constructor() {
+    error('should not call Pattern constructor');
+  };
+
+  constructor.prototype = {
+    // Input: current Canvas context
+    // Output: the appropriate fillStyle or strokeStyle
+    getPattern: function pattern_getStyle(ctx) {
+      error('Should not call Pattern.getStyle');
+    },
+  };
+
+  constructor.parse = function pattern_parse(cs, args, xref, res, ctx) {
+    var length = args.length;
+
+    var patternName = args[length - 1];
+    if (!IsName(patternName))
+      error("Bad args to getPattern");
+
+    var patternRes = xref.fetchIfRef(res.get("Pattern"));
+    if (!patternRes)
+      error("Unable to find pattern resource");
+
+    var pattern = xref.fetchIfRef(patternRes.get(patternName.name));
+    var dict = IsStream(pattern) ? pattern.dict : pattern;
+
+    var typeNum = dict.get("PatternType");
+    
+    var matrix = dict.get('Matrix');
+    var patMatrix = ctx.mozCurrentTransform;
+    if (patMatrix) {
+      if (matrix) {
+        ctx.save();
+        ctx.transform.apply(ctx, matrix);
+        patMatrix = ctx.mozCurrentTransform;
+        ctx.restore();
+      }
+    }
+
+    switch(typeNum) {
+    case 1:
+      var base = cs.base;
+      var color;
+      if (base) {
+        var baseComps = base.numComps;
+
+        color = [];
+        for (var i = 0; i < baseComps; ++i)
+          color.push(args[i]);
+
+        color = base.getRgb(color);
+      }
+      return new TilingPattern(pattern, dict, color);
+      break;
+    case 2:
+      var shading = xref.fetchIfRef(pattern.get('Shading'));
+      return Pattern.parseShading(shading, patMatrix, xref, res, ctx);
+    default:
+      error('Unknown type of pattern');
+    }
+  };
+
+  constructor.parseShading = function pattern_shading(shading, patMatrix,
+      xref, res, ctx) {
+
+    var dict = IsStream(shading) ? shading.dict : shading;
+
+    var bbox = dict.get('BBox');
+    var bg = dict.get('Background');
+
+    var cs = dict.get('ColorSpace', 'CS');
+    cs = ColorSpace.parse(cs, xref, res);
+
+    var type = dict.get('ShadingType');
+
+    switch (type) {
+    case 2:
+      return new AxialShading(dict, patMatrix, bbox, cs, bg, xref);
+      break;
+    case 3:
+      return new RadialShading(dict, patMatrix, bbox, cs, bg, xref);
+      break;
+    default:
+      return new DummyShading();
+    }
+  }
+  return constructor;
+})();
+
+var AxialShading = (function() {
+  function constructor(dict, patternMatrix, bbox, cs, background, xref) {
+    this.bbox = bbox;
+    this.cs = cs;
+    this.background = background;
+    this.patternMatrix = patternMatrix;
+
+    this.coordsArr = dict.get('Coords');
+
+    var t0 = 0.0, t1 = 1.0;
+    if (dict.has('Domain')) {
+      var domainArr = dict.get('Domain');
+      t0 = domainArr[0], t1 = domainArr[1];
+    }
+
+    var extendStart = false, extendEnd = false;
+    if (dict.has('Extend')) {
+      var extendArr = dict.get('Extend');
+      extendStart = extendArr[0], extendEnd = extendArr[1];
+      TODO('Support extend');
+    }
+
+    this.extendStart = extendStart;
+    this.extendEnd = extendEnd;
+
+    var fnObj = dict.get('Function');
+    fnObj = xref.fetchIfRef(fnObj);
+    if (IsArray(fnObj))
+      error('No support for array of functions');
+    else if (!IsPDFFunction(fnObj))
+      error('Invalid function');
+    var fn = new PDFFunction(xref, fnObj);
+
+    // 10 samples seems good enough for now, but probably won't work
+    // if there are sharp color changes. Ideally, we would implement
+    // the spec faithfully and add lossless optimizations.
+    var step = (t1 - t0) / 10;
+    var diff = t1 - t0;
+
+    var colorStops = [];
+    for (var i = t0; i <= t1; i += step) {
+      var color = fn.func([i]);
+      var rgbColor = this.makeCssRgb.apply(this, cs.getRgb(color));
+      colorStops.push([(i - t0) / diff, rgbColor]);
+    }
+
+    this.colorStops = colorStops;
+  };
+
+  constructor.prototype = {
+    getPattern: function(ctx) {
+      var coordsArr = this.coordsArr;
+      var x0 = coordsArr[0], y0 = coordsArr[1];
+      var x1 = coordsArr[2], y1 = coordsArr[3];
+
+      // if the browser supports getting the tranform matrix, convert
+      // gradient coordinates from pattern space to current user space
+      var patternMatrix = this.patternMatrix;
+      if (patternMatrix) {
+        var userMatrix = ctx.mozCurrentTransformInverse;
+
+        var p = this.applyTransform(x0, y0, patternMatrix);
+        p = this.applyTransform(p[0], p[1], userMatrix);
+        x0 = p[0];
+        y0 = p[1];
+
+        var p = this.applyTransform(x1, y1, patternMatrix);
+        p = this.applyTransform(p[0], p[1], userMatrix);
+        x1 = p[0];
+        y1 = p[1];
+      }
+
+      var colorStops = this.colorStops;
+      var gradient = 
+        ctx.createLinearGradient(x0, y0, x1, y1);
+      for (var i = 0, ii = colorStops.length; i < ii; ++i) {
+        var c = colorStops[i];
+        gradient.addColorStop(c[0], c[1]);
+      }
+      return gradient;
+    },
+    applyTransform: function(x0, y0, m) {
+      var xt = x0 * m[0] + y0 * m[2] + m[4];
+      var yt = x0 * m[1] + y0 * m[3] + m[5];
+      return [xt, yt];
+    },
+    makeCssRgb: function(r, g, b) {
+      var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0;
+      return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
+    }
+  };
+  return constructor;
+})();
+
+var RadialShading = (function() {
+  function constructor(dict, patternMatrix, bbox, cs, background, xref) {
+    this.bbox = bbox;
+    this.cs = cs;
+    this.background = background;
+    this.patternMatrix = patternMatrix;
+
+    this.coordsArr = dict.get('Coords');
+
+    var t0 = 0.0, t1 = 1.0;
+    if (dict.has('Domain')) {
+      var domainArr = dict.get('Domain');
+      t0 = domainArr[0], t1 = domainArr[1];
+    }
+
+    var extendStart = false, extendEnd = false;
+    if (dict.has('Extend')) {
+      var extendArr = dict.get('Extend');
+      extendStart = extendArr[0], extendEnd = extendArr[1];
+      TODO('Support extend');
+    }
+
+    this.extendStart = extendStart;
+    this.extendEnd = extendEnd;
+
+    var fnObj = dict.get('Function');
+    fnObj = xref.fetchIfRef(fnObj);
+    if (IsArray(fnObj))
+      error('No support for array of functions');
+    else if (!IsPDFFunction(fnObj))
+      error('Invalid function');
+    var fn = new PDFFunction(xref, fnObj);
+
+    // 10 samples seems good enough for now, but probably won't work
+    // if there are sharp color changes. Ideally, we would implement
+    // the spec faithfully and add lossless optimizations.
+    var step = (t1 - t0) / 10;
+    var diff = t1 - t0;
+
+    var colorStops = [];
+    for (var i = t0; i <= t1; i += step) {
+      var color = fn.func([i]);
+      var rgbColor = this.makeCssRgb.apply(this, cs.getRgb(color));
+      colorStops.push([(i - t0) / diff, rgbColor]);
+    }
+
+    this.colorStops = colorStops;
+  };
+
+  constructor.prototype = {
+    getPattern: function(ctx) {
+      var coordsArr = this.coordsArr;
+      var x0 = coordsArr[0], y0 = coordsArr[1], r0 = coordsArr[2];
+      var x1 = coordsArr[3], y1 = coordsArr[4], r1 = coordsArr[5];
+
+      // if the browser supports getting the tranform matrix, convert
+      // gradient coordinates from pattern space to current user space
+      var patternMatrix = this.patternMatrix;
+      if (patternMatrix) {
+        var userMatrix = ctx.mozCurrentTransformInverse;
+
+        var p = this.applyTransform(x0, y0, patternMatrix);
+        p = this.applyTransform(p[0], p[1], userMatrix);
+        x0 = p[0];
+        y0 = p[1];
+
+        var p = this.applyTransform(x1, y1, patternMatrix);
+        p = this.applyTransform(p[0], p[1], userMatrix);
+        x1 = p[0];
+        y1 = p[1];
+      }
+
+      var colorStops = this.colorStops;
+      var gradient = 
+        ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
+      for (var i = 0, ii = colorStops.length; i < ii; ++i) {
+        var c = colorStops[i];
+        gradient.addColorStop(c[0], c[1]);
+      }
+      return gradient;
+    },
+    applyTransform: function(x0, y0, m) {
+      var xt = x0 * m[0] + y0 * m[2] + m[4];
+      var yt = x0 * m[1] + y0 * m[3] + m[5];
+      return [xt, yt];
+    },
+    makeCssRgb: function(r, g, b) {
+      var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0;
+      return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
+    }
+  };
+
+  return constructor;
+})();
+
+var TilingPattern = (function() {
+  function constructor(tiling) {
+      function multiply(m, tm) {
+        var a = m[0] * tm[0] + m[1] * tm[2];
+        var b = m[0] * tm[1] + m[1] * tm[3];
+        var c = m[2] * tm[0] + m[3] * tm[2];
+        var d = m[2] * tm[1] + m[3] * tm[3];
+        var e = m[4] * tm[0] + m[5] * tm[2] + tm[4];
+        var f = m[4] * tm[1] + m[5] * tm[3] + tm[5];
+        return [a, b, c, d, e, f];
+      };
+
+      this.save();
+      var ctx = this.ctx;
+
+
+      TODO('TilingType');
+
+      var matrix = dict.get('Matrix') || IDENTITY_MATRIX;
+
+      var bbox = dict.get('BBox');
+      var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3];
+
+      var xstep = dict.get('XStep');
+      var ystep = dict.get('YStep');
+
+      // top left corner should correspond to the top left of the bbox
+      var topLeft = this.applyTransform(x0, y0, matrix);
+      // we want the canvas to be as large as the step size
+      var botRight = this.applyTransform(x0 + xstep, y0 + ystep, matrix);
+
+      var width = botRight[0] - topLeft[0];
+      var height = botRight[1] - topLeft[1];
+
+      // TODO: hack to avoid OOM, remove then pattern code is fixed
+      if (Math.abs(width) > 8192 || Math.abs(height) > 8192) {
+        this.restore();
+        return 'hotpink';
+      }
+
+      var tmpCanvas = new this.ScratchCanvas(width, height);
+
+      // set the new canvas element context as the graphics context
+      var tmpCtx = tmpCanvas.getContext('2d');
+      var savedCtx = ctx;
+      this.ctx = tmpCtx;
+
+      var paintType = dict.get('PaintType');
+      switch (paintType) {
+      case PAINT_TYPE_COLORED:
+        tmpCtx.fillStyle = savedCtx.fillStyle;
+        tmpCtx.strokeStyle = savedCtx.strokeStyle;
+        break;
+      case PAINT_TYPE_UNCOLORED:
+        color = this.makeCssRgb.apply(this, color);
+        tmpCtx.fillStyle = color;
+        tmpCtx.strokeStyle = color;
+        break;
+      default:
+        error('Unsupported paint type');
+      }
+
+      // normalize transform matrix so each step
+      // takes up the entire tmpCanvas (need to remove white borders)
+      if (matrix[1] === 0 && matrix[2] === 0) {
+        matrix[0] = tmpCanvas.width / xstep;
+        matrix[3] = tmpCanvas.height / ystep;
+        topLeft = this.applyTransform(x0, y0, matrix);
+      }
+
+      // move the top left corner of bounding box to [0,0]
+      matrix = multiply(matrix, [1, 0, 0, 1, -topLeft[0], -topLeft[1]]);
+      
+      this.transform.apply(this, matrix);
+
+      if (bbox && IsArray(bbox) && 4 == bbox.length) {
+        this.rectangle.apply(this, bbox);
+        this.clip();
+        this.endPath();
+      }
+
+      var xref = this.xref;
+      var res = xref.fetchIfRef(dict.get('Resources'));
+      if (!pattern.code)
+        pattern.code = this.compile(pattern, xref, res, []);
+      this.execute(pattern.code, xref, res);
+
+      this.ctx = savedCtx;
+      this.restore();
+
+      return this.ctx.createPattern(tmpCanvas, 'repeat');
+  };
+
+  constructor.prototype = {
+  };
+  return constructor;
+})();
+
+
 var PDFImage = (function() {
   function constructor(xref, res, image, inline) {
     this.image = image;