From da845ec5e6345bff293819a22d3f4edcf0a09034 Mon Sep 17 00:00:00 2001 From: sbarman Date: Wed, 13 Jul 2011 11:41:04 -0700 Subject: [PATCH 01/11] git radial gradients working --- pdf.js | 194 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 137 insertions(+), 57 deletions(-) diff --git a/pdf.js b/pdf.js index a4c10fef7..452304d12 100644 --- a/pdf.js +++ b/pdf.js @@ -3793,7 +3793,7 @@ var PartialEvaluator = (function() { // 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() { + function constructor(old) { // Are soft masks and alpha values shapes or opacities? this.alphaIsShape = false; this.fontSize = 0; @@ -3810,10 +3810,70 @@ var CanvasExtraState = (function() { this.wordSpace = 0; this.textHScale = 100; // Color spaces - this.fillColorSpace = null; - this.strokeColorSpace = null; + this.fillColorSpaceObj = null; + this.strokeColorSpaceObj = null; + this.fillColorObj = null; + this.strokeColorObj = null; + + this.old = old; } constructor.prototype = { + get fillColorSpace() { + var cs = this.fillColorSpaceObj; + if (cs) + return cs; + + var old = this.old; + if (old) + return old.fillColorSpace; + + return null; + }, + set fillColorSpace(cs) { + this.fillColorSpaceObj = cs; + }, + get strokeColorSpace() { + var cs = this.strokeColorSpaceObj; + if (cs) + return cs; + + var old = this.old; + if (old) + return old.strokeColorSpace; + + return null; + }, + set strokeColorSpace(cs) { + this.strokeColorSpaceObj = cs; + }, + get fillColor() { + var color = this.fillColorObj; + if (color) + return color; + + var old = this.old; + if (old) + return old.fillColor; + + return null; + }, + set fillColor(color) { + this.fillColorObj = color; + }, + get strokeColor() { + var color = this.strokeColorObj; + if (color) + return color; + + var old = this.old; + if (old) + return old.strokeColor; + + return null; + }, + set strokeColor(color) { + this.strokeColorObj = color; + } }; return constructor; })(); @@ -3906,8 +3966,9 @@ var CanvasGraphics = (function() { if (this.ctx.$saveCurrentX) { this.ctx.$saveCurrentX(); } - this.stateStack.push(this.current); - this.current = new CanvasExtraState(); + var old = this.current; + this.stateStack.push(old); + this.current = new CanvasExtraState(old); }, restore: function() { var prev = this.stateStack.pop(); @@ -3954,7 +4015,16 @@ var CanvasGraphics = (function() { this.stroke(); }, fill: function() { - this.ctx.fill(); + var ctx = this.ctx; + var fillColor = this.current.fillColor; + + if (fillColor.type === "Pattern") { + this.ctx.fillStyle = fillColor.patternFn.apply(this, ctx); + ctx.fill(); + } else { + ctx.fill(); + } + this.consumePath(); }, eoFill: function() { @@ -3963,8 +4033,14 @@ var CanvasGraphics = (function() { this.restoreFillRule(savedFillRule); }, fillStroke: function() { - this.ctx.fill(); - this.ctx.stroke(); + var ctx = this.ctx; + var fillCS = this.current.fillColorSpace; + + if (fillCS && fillCS.name === "Pattern") + this.current.fillPattern(ctx); + + ctx.fill(); + ctx.stroke(); this.consumePath(); }, eoFillStroke: function() { @@ -4143,12 +4219,12 @@ var CanvasGraphics = (function() { ColorSpace.parse(space, this.xref, this.res); }, setStrokeColor: function(/*...*/) { - var cs = this.getStrokeColorSpace(); + var cs = this.current.strokeColorSpace; var color = cs.getRgb(arguments); this.setStrokeRGBColor.apply(this, color); }, setStrokeColorN: function(/*...*/) { - var cs = this.getStrokeColorSpace(); + var cs = this.current.strokeColorSpace; if (cs.name == 'Pattern') { this.ctx.strokeStyle = this.getPattern(cs, arguments); @@ -4157,15 +4233,19 @@ var CanvasGraphics = (function() { } }, setFillColor: function(/*...*/) { - var cs = this.getFillColorSpace(); + var cs = this.current.fillColorSpace; var color = cs.getRgb(arguments); this.setFillRGBColor.apply(this, color); }, setFillColorN: function(/*...*/) { - var cs = this.getFillColorSpace(); + var cs = this.current.fillColorSpace; if (cs.name == 'Pattern') { - this.ctx.fillStyle = this.getPattern(cs, arguments); + // return a set of functions which will set the pattern + // when fill is called + var pattern = this.getPattern(cs, arguments); + this.current.fillColor = pattern; + this.current.fillColor.type = "Pattern"; } else { this.setFillColor.apply(this, arguments); } @@ -4210,9 +4290,9 @@ var CanvasGraphics = (function() { this.transform.apply(this, matrix); var shading = this.getShading(pattern.get("Shading")); this.restore(); - - TODO('store transform so it can be applied before every fill'); return shading; + // TODO('store transform so it can be applied before every fill'); + // return shading; }, getTilingPattern: function(pattern, dict, color) { function multiply(m, tm) { @@ -4315,13 +4395,16 @@ var CanvasGraphics = (function() { this.ctx.strokeStyle = this.makeCssRgb(r, g, b); }, setFillRGBColor: function(r, g, b) { - this.ctx.fillStyle = this.makeCssRgb(r, g, b); + var color = this.makeCssRgb(r, g, b); + this.ctx.fillStyle = color; + this.current.fillColor = color; }, setStrokeCMYKColor: function(c, m, y, k) { this.ctx.strokeStyle = this.makeCssCmyk(c, m, y, k); }, setFillCMYKColor: function(c, m, y, k) { - this.ctx.fillStyle = this.makeCssCmyk(c, m, y, k); + var color = (new DeviceCmykCS()).getRgb([c, m, y, k]); + this.setFillRGBColor.apply(this, color); }, // Shading @@ -4341,7 +4424,7 @@ var CanvasGraphics = (function() { var shadingFill = this.getShading(shading); this.save(); - ctx.fillStyle = shadingFill; + ctx.fillStyle = shadingFill.patternFn.apply(this, ctx); var inv = ctx.mozCurrentTransformInverse; if (inv) { @@ -4455,8 +4538,6 @@ var CanvasGraphics = (function() { }, getRadialShading: function(sh, cs) { var coordsArr = sh.get('Coords'); - var x0 = coordsArr[0], y0 = coordsArr[1], r0 = coordsArr[2]; - var x1 = coordsArr[3], y1 = coordsArr[4], r1 = coordsArr[5]; var t0 = 0.0, t1 = 1.0; if (sh.has('Domain')) { @@ -4478,22 +4559,51 @@ var CanvasGraphics = (function() { error('Invalid function'); var fn = new PDFFunction(this.xref, fnObj); - var gradient = - this.ctx.createRadialGradient(x0, y0, r0, x1, y1, r1); - // 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 = cs.getRgb(color); - gradient.addColorStop((i - t0) / diff, - this.makeCssRgb.apply(this, rgbColor)); + 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; + } } - return gradient; }, // Images @@ -4645,36 +4755,6 @@ var CanvasGraphics = (function() { var bi = (255 * (1 - Math.min(1, y * (1 - k) + k))) | 0; return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; }, - getFillColorSpace: function() { - var cs = this.current.fillColorSpace; - if (cs) - return cs; - - var states = this.stateStack; - var i = states.length - 1; - while (i >= 0 && !(cs = states[i].fillColorSpace)) - --i; - - if (cs) - return cs; - else - return new DeviceRgbCS(); - }, - getStrokeColorSpace: function() { - var cs = this.current.strokeColorSpace; - if (cs) - return cs; - - var states = this.stateStack; - var i = states.length - 1; - while (i >= 0 && !(cs = states[i].strokeColorSpace)) - --i; - - if (cs) - return cs; - else - return new DeviceRgbCS(); - }, // We generally keep the canvas context set for // nonzero-winding, and just set evenodd for the operations // that need them. From 01d5e5579c0ff7fd6d8d277752478029555ca42c Mon Sep 17 00:00:00 2001 From: sbarman Date: Wed, 13 Jul 2011 11:55:54 -0700 Subject: [PATCH 02/11] Changed linear gradients --- pdf.js | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/pdf.js b/pdf.js index 452304d12..6f2e0710f 100644 --- a/pdf.js +++ b/pdf.js @@ -4496,8 +4496,6 @@ var CanvasGraphics = (function() { }, getAxialShading: function(sh, cs) { var coordsArr = sh.get('Coords'); - var x0 = coordsArr[0], y0 = coordsArr[1], - x1 = coordsArr[2], y1 = coordsArr[3]; var t0 = 0.0, t1 = 1.0; if (sh.has('Domain')) { @@ -4519,22 +4517,51 @@ var CanvasGraphics = (function() { error('Invalid function'); var fn = new PDFFunction(this.xref, fnObj); - var gradient = this.ctx.createLinearGradient(x0, y0, x1, y1); - // 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 = cs.getRgb(color); - gradient.addColorStop((i - t0) / diff, - this.makeCssRgb.apply(this, rgbColor)); + var rgbColor = this.makeCssRgb.apply(this, cs.getRgb(color)); + colorStops.push([(i - t0) / diff, rgbColor]); } + + var patternMatrix = this.ctx.mozCurrentTransform; - return gradient; + 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'); From 7986c35213d9cd1a657eeac43ec7ae76e54e9e9b Mon Sep 17 00:00:00 2001 From: sbarman Date: Wed, 13 Jul 2011 15:43:09 -0700 Subject: [PATCH 03/11] fixed gradients, need to work on tiling --- pdf.js | 691 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 387 insertions(+), 304 deletions(-) diff --git a/pdf.js b/pdf.js index 6f2e0710f..2af03dce8 100644 --- 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; From 9dcac17a3e5e264011da0495fab9bcbc40c2b5b0 Mon Sep 17 00:00:00 2001 From: sbarman Date: Thu, 14 Jul 2011 10:59:12 -0700 Subject: [PATCH 04/11] patterns working --- pdf.js | 364 ++++++++++++++++++++++----------------------------------- 1 file changed, 141 insertions(+), 223 deletions(-) diff --git a/pdf.js b/pdf.js index 2af03dce8..5ce4c47bd 100644 --- a/pdf.js +++ b/pdf.js @@ -2969,6 +2969,7 @@ var Page = (function() { var fonts = [ ]; this.compile(gfx, fonts); + fonts = [] stats.compile = Date.now(); FontLoader.bind( @@ -3901,9 +3902,6 @@ var CanvasGraphics = (function() { 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; @@ -4019,11 +4017,9 @@ var CanvasGraphics = (function() { var fillColor = this.current.fillColor; if (fillColor.type === "Pattern") { - this.ctx.fillStyle = fillColor.getPattern(ctx); - ctx.fill(); - } else { - ctx.fill(); + ctx.fillStyle = fillColor.getPattern(ctx); } + ctx.fill(); this.consumePath(); }, @@ -4227,7 +4223,7 @@ var CanvasGraphics = (function() { var cs = this.current.strokeColorSpace; if (cs.name == 'Pattern') { - this.ctx.strokeStyle = this.getPattern(cs, arguments); + // this.ctx.strokeStyle = this.getPattern(cs, arguments); } else { this.setStrokeColor.apply(this, arguments); } @@ -4242,7 +4238,7 @@ var CanvasGraphics = (function() { if (cs.name == 'Pattern') { // wait until fill to actually get the pattern - var pattern = Pattern.parse(cs, arguments, this.xref, this.res, + var pattern = Pattern.parse(arguments, cs, this.xref, this.res, this.ctx); this.current.fillColor = pattern; this.current.fillColor.type = "Pattern"; @@ -4250,8 +4246,6 @@ var CanvasGraphics = (function() { this.setFillColor.apply(this, arguments); } }, - getPattern: function(cs, args) { - }, getShadingPattern: function(pattern, dict) { // TODO('store transform so it can be applied before every fill'); // return shading; @@ -4297,7 +4291,7 @@ var CanvasGraphics = (function() { var shadingFill = Pattern.parseShading(shading, null, xref, res, ctx); this.save(); - ctx.fillStyle = shadingFill.getPattern(ctx); + ctx.fillStyle = shadingFill.getPattern(); var inv = ctx.mozCurrentTransformInverse; if (inv) { @@ -4328,12 +4322,6 @@ var CanvasGraphics = (function() { this.restore(); }, - getShading: function(shading) { - }, - getAxialShading: function(sh, cs) { - }, - getRadialShading: function(sh, cs) { - }, // Images beginInlineImage: function() { @@ -4534,8 +4522,6 @@ var ColorSpace = (function() { } } - if (!xref.fetchIfRef) - console.log("blah"); cs = xref.fetchIfRef(cs); if (IsName(cs)) { @@ -4872,7 +4858,7 @@ var Pattern = (function() { }, }; - constructor.parse = function pattern_parse(cs, args, xref, res, ctx) { + constructor.parse = function pattern_parse(args, cs, xref, res, ctx) { var length = args.length; var patternName = args[length - 1]; @@ -4885,19 +4871,7 @@ var Pattern = (function() { 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: @@ -4912,51 +4886,53 @@ var Pattern = (function() { color = base.getRgb(color); } - return new TilingPattern(pattern, dict, color); + return new TilingPattern(pattern, dict, color, xref, ctx); break; case 2: - var shading = xref.fetchIfRef(pattern.get('Shading')); - return Pattern.parseShading(shading, patMatrix, xref, res, ctx); + var shading = xref.fetchIfRef(dict.get('Shading')); + var matrix = dict.get('Matrix'); + return Pattern.parseShading(shading, matrix, xref, res, ctx); + break; default: error('Unknown type of pattern'); } }; - constructor.parseShading = function pattern_shading(shading, patMatrix, + constructor.parseShading = function pattern_shading(shading, matrix, 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); + return new SimpleShading(dict, matrix, xref, res, ctx); break; case 3: - return new RadialShading(dict, patMatrix, bbox, cs, bg, xref); + return new SimpleShading(dict, matrix, xref, res, ctx); break; default: - return new DummyShading(); + error('Unsupported shading'); } } 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; - +var SimpleShading = (function() { + function constructor(dict, matrix, xref, res, ctx) { + this.matrix = matrix; + var bbox = dict.get('BBox'); + var background = dict.get('Background'); this.coordsArr = dict.get('Coords'); + this.shadingType = dict.get('ShadingType'); + + this.ctx = ctx; + this.curMatrix = ctx.mozCurrentTransform; + console.log(ctx.mozCurrentTransform); + + var cs = dict.get('ColorSpace', 'CS'); + cs = ColorSpace.parse(cs, xref, res); + this.cs = cs; var t0 = 0.0, t1 = 1.0; if (dict.has('Domain')) { @@ -4999,40 +4975,60 @@ var AxialShading = (function() { }; constructor.prototype = { - getPattern: function(ctx) { + getPattern: function() { var coordsArr = this.coordsArr; - var x0 = coordsArr[0], y0 = coordsArr[1]; - var x1 = coordsArr[2], y1 = coordsArr[3]; + var type = this.shadingType; + if (type == 2) { + var p0 = [coordsArr[0], coordsArr[1]]; + var p1 = [coordsArr[2], coordsArr[3]]; + } else if (type == 3) { + var p0 = [coordsArr[0], coordsArr[1]]; + var p1 = [coordsArr[3], coordsArr[4]]; + var r0 = coordsArr[2], r1 = coordsArr[5] + } else { + error() + } + + var matrix = this.matrix; + if (matrix) { + p0 = this.applyTransform(p0, matrix); + p1 = this.applyTransform(p1, matrix); + } // 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 curMatrix = this.curMatrix; + var ctx = this.ctx; + if (curMatrix) { var userMatrix = ctx.mozCurrentTransformInverse; + console.log(p0 + ',' + p1); + console.log(curMatrix); + console.log(userMatrix); - var p = this.applyTransform(x0, y0, patternMatrix); - p = this.applyTransform(p[0], p[1], userMatrix); - x0 = p[0]; - y0 = p[1]; + p0 = this.applyTransform(p0, curMatrix); + p0 = this.applyTransform(p0, userMatrix); + console.log(p0); - var p = this.applyTransform(x1, y1, patternMatrix); - p = this.applyTransform(p[0], p[1], userMatrix); - x1 = p[0]; - y1 = p[1]; + p1 = this.applyTransform(p1, curMatrix); + p1 = this.applyTransform(p1, userMatrix); + console.log(p0 + ',' + p1); } var colorStops = this.colorStops; - var gradient = - ctx.createLinearGradient(x0, y0, x1, y1); + if (type == 2) + var grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]); + else if (type == 3) + var grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1); + for (var i = 0, ii = colorStops.length; i < ii; ++i) { var c = colorStops[i]; - gradient.addColorStop(c[0], c[1]); + grad.addColorStop(c[0], c[1]); } - return gradient; + return grad; }, - 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]; + applyTransform: function(p, m) { + var xt = p[0] * m[0] + p[1] * m[2] + m[4]; + var yt = p[0] * m[1] + p[1] * m[3] + m[5]; return [xt, yt]; }, makeCssRgb: function(r, g, b) { @@ -5043,63 +5039,85 @@ var AxialShading = (function() { 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; +var TilingPattern = (function() { + var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2; + + function constructor(pattern, dict, color, patMatrix, xref, ctx) { + 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.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]; - } + TODO('TilingType'); - var extendStart = false, extendEnd = false; - if (dict.has('Extend')) { - var extendArr = dict.get('Extend'); - extendStart = extendArr[0], extendEnd = extendArr[1]; - TODO('Support extend'); - } + this.patMatrix = patMatrix; - this.extendStart = extendStart; - this.extendEnd = extendEnd; + var bbox = dict.get('BBox'); + var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3]; - 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); + var xstep = dict.get('XStep'); + var ystep = dict.get('YStep'); - // 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 topLeft = [x0, y0]; + // we want the canvas to be as large as the step size + var botRight = [x0 + xstep, y0 + ystep] - 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 width = botRight[0] - topLeft[0]; + var height = botRight[1] - topLeft[1]; - this.colorStops = colorStops; + // TODO: hack to avoid OOM, remove then pattern code is fixed + while (Math.abs(width) > 8192 || Math.abs(height) > 8192) { + width /= 2; + height /= 2; + } + + var tmpCanvas = new ScratchCanvas(width, height); + + // set the new canvas element context as the graphics context + var tmpCtx = tmpCanvas.getContext('2d'); + var graphics = new CanvasGraphics(tmpCtx); + + var paintType = dict.get('PaintType'); + switch (paintType) { + case PAINT_TYPE_COLORED: + tmpCtx.fillStyle = ctx.fillStyle; + tmpCtx.strokeStyle = ctx.strokeStyle; + break; + case PAINT_TYPE_UNCOLORED: + color = this.makeCssRgb.apply(this, color); + tmpCtx.fillStyle = color; + tmpCtx.strokeStyle = color; + break; + default: + error('Unsupported paint type'); + } + + // transform coordinates to pattern space + var tmpTransform = [width / xstep, 0, 0, height / ystep, -topLeft[0], -topLeft[1]]; + graphics.transform.apply(graphics, matrix); + + if (bbox && IsArray(bbox) && 4 == bbox.length) { + graphics.rectangle.apply(graphics, bbox); + graphics.clip(); + graphics.endPath(); + } + + var res = xref.fetchIfRef(dict.get('Resources')); + if (!pattern.code) + pattern.code = graphics.compile(pattern, xref, res, []); + graphics.execute(pattern.code, xref, res); + + this.canvas = tmpCanvas; }; 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 + getPattern: function tiling_getPattern(ctx) { var patternMatrix = this.patternMatrix; if (patternMatrix) { var userMatrix = ctx.mozCurrentTransformInverse; @@ -5118,11 +5136,10 @@ var RadialShading = (function() { var colorStops = this.colorStops; var gradient = ctx.createRadialGradient(x0, y0, r0, x1, y1, r1); - for (var i = 0, ii = colorStops.length; i < ii; ++i) { + for (var i = 0, ii = colorStops.length; i < ii; ++i) var c = colorStops[i]; - gradient.addColorStop(c[0], c[1]); - } - return gradient; + ctx.transform.apply + return ctx.createPattern(this.canvas, 'repeat'); }, applyTransform: function(x0, y0, m) { var xt = x0 * m[0] + y0 * m[2] + m[4]; @@ -5134,105 +5151,6 @@ var RadialShading = (function() { 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; })(); From dc77e076c28e7abb776e9ddb6de101f95f167aeb Mon Sep 17 00:00:00 2001 From: sbarman Date: Thu, 14 Jul 2011 11:50:27 -0700 Subject: [PATCH 05/11] Fixed tiling patterns --- pdf.js | 72 ++++++++++++++++++++++++---------------------------------- 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/pdf.js b/pdf.js index 5ce4c47bd..af0daeb41 100644 --- a/pdf.js +++ b/pdf.js @@ -2969,7 +2969,6 @@ var Page = (function() { var fonts = [ ]; this.compile(gfx, fonts); - fonts = [] stats.compile = Date.now(); FontLoader.bind( @@ -4017,9 +4016,13 @@ var CanvasGraphics = (function() { var fillColor = this.current.fillColor; if (fillColor.type === "Pattern") { + this.ctx.save(); ctx.fillStyle = fillColor.getPattern(ctx); + ctx.fill(); + this.ctx.restore(); + } else { + ctx.fill(); } - ctx.fill(); this.consumePath(); }, @@ -4246,12 +4249,6 @@ var CanvasGraphics = (function() { this.setFillColor.apply(this, arguments); } }, - getShadingPattern: function(pattern, dict) { - // TODO('store transform so it can be applied before every fill'); - // return shading; - }, - getTilingPattern: function(pattern, dict, color) { - }, setStrokeGray: function(gray) { this.setStrokeRGBColor(gray, gray, gray); }, @@ -4906,9 +4903,8 @@ var Pattern = (function() { switch (type) { case 2: - return new SimpleShading(dict, matrix, xref, res, ctx); - break; case 3: + // both radial and axial shadings are handled by simple shading return new SimpleShading(dict, matrix, xref, res, ctx); break; default: @@ -4928,7 +4924,6 @@ var SimpleShading = (function() { this.ctx = ctx; this.curMatrix = ctx.mozCurrentTransform; - console.log(ctx.mozCurrentTransform); var cs = dict.get('ColorSpace', 'CS'); cs = ColorSpace.parse(cs, xref, res); @@ -5001,17 +4996,12 @@ var SimpleShading = (function() { var ctx = this.ctx; if (curMatrix) { var userMatrix = ctx.mozCurrentTransformInverse; - console.log(p0 + ',' + p1); - console.log(curMatrix); - console.log(userMatrix); p0 = this.applyTransform(p0, curMatrix); p0 = this.applyTransform(p0, userMatrix); - console.log(p0); p1 = this.applyTransform(p1, curMatrix); p1 = this.applyTransform(p1, userMatrix); - console.log(p0 + ',' + p1); } var colorStops = this.colorStops; @@ -5042,7 +5032,7 @@ var SimpleShading = (function() { var TilingPattern = (function() { var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2; - function constructor(pattern, dict, color, patMatrix, xref, ctx) { + function constructor(pattern, dict, color, xref, ctx) { function multiply(m, tm) { var a = m[0] * tm[0] + m[1] * tm[2]; var b = m[0] * tm[1] + m[1] * tm[3]; @@ -5053,10 +5043,12 @@ var TilingPattern = (function() { return [a, b, c, d, e, f]; }; - TODO('TilingType'); - this.patMatrix = patMatrix; + this.matrix = dict.get("Matrix"); + this.curMatrix = ctx.mozCurrentTransform; + this.invMatrix = ctx.mozCurrentTransformInverse; + this.ctx = ctx; var bbox = dict.get('BBox'); var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3]; @@ -5072,9 +5064,9 @@ var TilingPattern = (function() { var height = botRight[1] - topLeft[1]; // TODO: hack to avoid OOM, remove then pattern code is fixed - while (Math.abs(width) > 8192 || Math.abs(height) > 8192) { - width /= 2; - height /= 2; + while (Math.abs(width) > 512 || Math.abs(height) > 512) { + width = 512; + height = 512; } var tmpCanvas = new ScratchCanvas(width, height); @@ -5098,9 +5090,14 @@ var TilingPattern = (function() { error('Unsupported paint type'); } + var scale = [width / xstep, height / ystep]; + this.scale = scale; + // transform coordinates to pattern space - var tmpTransform = [width / xstep, 0, 0, height / ystep, -topLeft[0], -topLeft[1]]; - graphics.transform.apply(graphics, matrix); + var tmpTranslate = [1, 0, 0, 1, -topLeft[0], -topLeft[1]]; + var tmpScale = [scale[0], 0, 0, scale[1], 0, 0]; + graphics.transform.apply(graphics, tmpScale); + graphics.transform.apply(graphics, tmpTranslate); if (bbox && IsArray(bbox) && 4 == bbox.length) { graphics.rectangle.apply(graphics, bbox); @@ -5118,27 +5115,18 @@ var TilingPattern = (function() { constructor.prototype = { getPattern: function tiling_getPattern(ctx) { - var patternMatrix = this.patternMatrix; - if (patternMatrix) { - var userMatrix = ctx.mozCurrentTransformInverse; + var matrix = this.matrix; + var curMatrix = this.curMatrix; - var p = this.applyTransform(x0, y0, patternMatrix); - p = this.applyTransform(p[0], p[1], userMatrix); - x0 = p[0]; - y0 = p[1]; + if (curMatrix) + ctx.setTransform.apply(ctx, curMatrix); - var p = this.applyTransform(x1, y1, patternMatrix); - p = this.applyTransform(p[0], p[1], userMatrix); - x1 = p[0]; - y1 = p[1]; - } + if (matrix) + ctx.transform.apply(ctx, matrix); + + var scale = this.scale; + ctx.scale(1 / scale[0], 1 / scale[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]; - ctx.transform.apply return ctx.createPattern(this.canvas, 'repeat'); }, applyTransform: function(x0, y0, m) { From faa6875b80b6d9513874960ce2e8a23869c46824 Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 15 Jul 2011 09:48:40 -0700 Subject: [PATCH 06/11] cleanup, add support for stroking --- pdf.js | 126 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/pdf.js b/pdf.js index af0daeb41..9a5944d5e 100644 --- a/pdf.js +++ b/pdf.js @@ -4226,7 +4226,10 @@ var CanvasGraphics = (function() { var cs = this.current.strokeColorSpace; if (cs.name == 'Pattern') { - // this.ctx.strokeStyle = this.getPattern(cs, arguments); + // wait until fill to actually get the pattern + var pattern = Pattern.parse(arguments, cs, this.xref, this.res, + this.ctx); + this.current.strokeColor = pattern; } else { this.setStrokeColor.apply(this, arguments); } @@ -4243,8 +4246,9 @@ var CanvasGraphics = (function() { // wait until fill to actually get the pattern var pattern = Pattern.parse(arguments, cs, this.xref, this.res, this.ctx); - this.current.fillColor = pattern; - this.current.fillColor.type = "Pattern"; +// this.current.fillColor = pattern; + this.ctx.fillStyle = pattern.getPattern(); + this.current.fillColor = "blah"; } else { this.setFillColor.apply(this, arguments); } @@ -4256,19 +4260,24 @@ var CanvasGraphics = (function() { this.setFillRGBColor(gray, gray, gray); }, setStrokeRGBColor: function(r, g, b) { - this.ctx.strokeStyle = this.makeCssRgb(r, g, b); + var color = Util.makeCssRgb(r, g, b); + this.ctx.strokeStyle = color; + this.current.fillColor = color; }, setFillRGBColor: function(r, g, b) { - var color = this.makeCssRgb(r, g, b); + var color = Util.makeCssRgb(r, g, b); this.ctx.fillStyle = color; this.current.fillColor = color; }, setStrokeCMYKColor: function(c, m, y, k) { - this.ctx.strokeStyle = this.makeCssCmyk(c, m, y, k); + var color = Util.makeCssCmyk(c, m, y, k); + this.ctx.strokeStyle = color; + this.current.fillColor = color; }, setFillCMYKColor: function(c, m, y, k) { - var color = (new DeviceCmykCS()).getRgb([c, m, y, k]); - this.setFillRGBColor.apply(this, color); + var color = Util.makeCssCmyk(c, m, y, k); + this.ctx.fillStyle = color; + this.current.fillColor = color; }, // Shading @@ -4296,10 +4305,10 @@ var CanvasGraphics = (function() { var width = canvas.width; var height = canvas.height; - var bl = this.applyTransform(0, 0, inv); - var br = this.applyTransform(0, width, inv); - var ul = this.applyTransform(height, 0, inv); - var ur = this.applyTransform(height, width, inv); + 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 x0 = Math.min(bl[0], br[0], ul[0], ur[0]); var y0 = Math.min(bl[1], br[1], ul[1], ur[1]); @@ -4457,18 +4466,6 @@ var CanvasGraphics = (function() { } this.ctx.beginPath(); }, - makeCssRgb: function(r, g, b) { - var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0; - return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; - }, - makeCssCmyk: function(c, m, y, k) { - // while waiting on CSS's cmyk()... - // http://www.ilkeratalay.com/colorspacesfaq.php#rgb - var ri = (255 * (1 - Math.min(1, c * (1 - k) + k))) | 0; - var gi = (255 * (1 - Math.min(1, m * (1 - k) + k))) | 0; - var bi = (255 * (1 - Math.min(1, y * (1 - k) + k))) | 0; - return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; - }, // We generally keep the canvas context set for // nonzero-winding, and just set evenodd for the operations // that need them. @@ -4480,16 +4477,30 @@ var CanvasGraphics = (function() { restoreFillRule: function(rule) { this.ctx.mozFillRule = rule; }, - 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]; - } }; return constructor; })(); +var Util = (function() { + function constructor() {}; + constructor.makeCssRgb = function makergb(r, g, b) { + var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0; + return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; + }; + constructor.makeCssCmyk = function makecmyk(c, m, y, k) { + var c = (new DeviceCmykCS()).getRgb([c, m, y, k]); + return 'rgb(' + c[0] + ',' + c[1] + ',' + c[2] + ')'; + }; + constructor.applyTransform = function apply(p, m) { + var xt = p[0] * m[0] + p[1] * m[2] + m[4]; + var yt = p[0] * m[1] + p[1] * m[3] + m[5]; + return [xt, yt]; + }; + + return constructor; +})(); + var ColorSpace = (function() { // Constructor should define this.numComps, this.defaultColor, this.name function constructor() { @@ -4884,12 +4895,10 @@ var Pattern = (function() { color = base.getRgb(color); } return new TilingPattern(pattern, dict, color, xref, ctx); - break; case 2: var shading = xref.fetchIfRef(dict.get('Shading')); var matrix = dict.get('Matrix'); return Pattern.parseShading(shading, matrix, xref, res, ctx); - break; default: error('Unknown type of pattern'); } @@ -4904,23 +4913,33 @@ var Pattern = (function() { switch (type) { case 2: case 3: - // both radial and axial shadings are handled by simple shading - return new SimpleShading(dict, matrix, xref, res, ctx); - break; + // both radial and axial shadings are handled by RadialAxial shading + return new RadialAxialShading(dict, matrix, xref, res, ctx); default: - error('Unsupported shading'); + return new DummyShading(); } } return constructor; })(); -var SimpleShading = (function() { +var DummyShading = (function() { + function constructor() {}; + constructor.prototype = { + getPattern: function dummy_getpattern() { + return 'hotpink'; + } + }; + return constructor; +})(); + +var RadialAxialShading = (function() { function constructor(dict, matrix, xref, res, ctx) { this.matrix = matrix; var bbox = dict.get('BBox'); var background = dict.get('Background'); this.coordsArr = dict.get('Coords'); this.shadingType = dict.get('ShadingType'); + this.type = 'Pattern'; this.ctx = ctx; this.curMatrix = ctx.mozCurrentTransform; @@ -4962,7 +4981,7 @@ var SimpleShading = (function() { var colorStops = []; for (var i = t0; i <= t1; i += step) { var color = fn.func([i]); - var rgbColor = this.makeCssRgb.apply(this, cs.getRgb(color)); + var rgbColor = Util.makeCssRgb.apply(this, cs.getRgb(color)); colorStops.push([(i - t0) / diff, rgbColor]); } @@ -4986,8 +5005,8 @@ var SimpleShading = (function() { var matrix = this.matrix; if (matrix) { - p0 = this.applyTransform(p0, matrix); - p1 = this.applyTransform(p1, matrix); + p0 = Util.applyTransform(p0, matrix); + p1 = Util.applyTransform(p1, matrix); } // if the browser supports getting the tranform matrix, convert @@ -4997,11 +5016,11 @@ var SimpleShading = (function() { if (curMatrix) { var userMatrix = ctx.mozCurrentTransformInverse; - p0 = this.applyTransform(p0, curMatrix); - p0 = this.applyTransform(p0, userMatrix); + p0 = Util.applyTransform(p0, curMatrix); + p0 = Util.applyTransform(p0, userMatrix); - p1 = this.applyTransform(p1, curMatrix); - p1 = this.applyTransform(p1, userMatrix); + p1 = Util.applyTransform(p1, curMatrix); + p1 = Util.applyTransform(p1, userMatrix); } var colorStops = this.colorStops; @@ -5015,15 +5034,6 @@ var SimpleShading = (function() { grad.addColorStop(c[0], c[1]); } return grad; - }, - applyTransform: function(p, m) { - var xt = p[0] * m[0] + p[1] * m[2] + m[4]; - var yt = p[0] * m[1] + p[1] * m[3] + m[5]; - return [xt, yt]; - }, - 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; @@ -5049,6 +5059,7 @@ var TilingPattern = (function() { this.curMatrix = ctx.mozCurrentTransform; this.invMatrix = ctx.mozCurrentTransformInverse; this.ctx = ctx; + this.type = 'Pattern'; var bbox = dict.get('BBox'); var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3]; @@ -5082,7 +5093,7 @@ var TilingPattern = (function() { tmpCtx.strokeStyle = ctx.strokeStyle; break; case PAINT_TYPE_UNCOLORED: - color = this.makeCssRgb.apply(this, color); + color = Util.makeCssRgb.apply(this, color); tmpCtx.fillStyle = color; tmpCtx.strokeStyle = color; break; @@ -5128,15 +5139,6 @@ var TilingPattern = (function() { ctx.scale(1 / scale[0], 1 / scale[1]); return ctx.createPattern(this.canvas, 'repeat'); - }, - 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; From dd1d90213bfb1f714124fe722c85b30a60f60ae4 Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 15 Jul 2011 11:18:37 -0700 Subject: [PATCH 07/11] cleanup --- pdf.js | 55 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/pdf.js b/pdf.js index 9a5944d5e..d371330ac 100644 --- a/pdf.js +++ b/pdf.js @@ -4004,7 +4004,17 @@ var CanvasGraphics = (function() { this.ctx.rect(x, y, width, height); }, stroke: function() { - this.ctx.stroke(); + var ctx = this.ctx; + var strokeColor = this.current.strokeColor; + if (strokeColor && strokeColor.type === "Pattern") { + ctx.save(); + ctx.strokeStyle = strokeColor.getPattern(ctx); + ctx.stroke(); + ctx.restore(); + } else { + ctx.stroke(); + } + this.consumePath(); }, closeStroke: function() { @@ -4015,11 +4025,11 @@ var CanvasGraphics = (function() { var ctx = this.ctx; var fillColor = this.current.fillColor; - if (fillColor.type === "Pattern") { - this.ctx.save(); + if (fillColor && fillColor.type === "Pattern") { + ctx.save(); ctx.fillStyle = fillColor.getPattern(ctx); ctx.fill(); - this.ctx.restore(); + ctx.restore(); } else { ctx.fill(); } @@ -4033,13 +4043,27 @@ var CanvasGraphics = (function() { }, fillStroke: function() { var ctx = this.ctx; - var fillCS = this.current.fillColorSpace; - if (fillCS && fillCS.name === "Pattern") - this.current.fillPattern(ctx); - - ctx.fill(); - ctx.stroke(); + var fillColor = this.current.fillColor; + if (fillColor && fillColor.type === "Pattern") { + ctx.save(); + ctx.fillStyle = fillColor.getPattern(ctx); + ctx.fill(); + ctx.restore(); + } else { + ctx.fill(); + } + + var strokeColor = this.current.strokeColor; + if (stokeColor && strokeColor.type === "Pattern") { + ctx.save(); + ctx.strokeStyle = strokeColor.getPattern(ctx); + ctx.stroke(); + ctx.restore(); + } else { + ctx.stroke(); + } + this.consumePath(); }, eoFillStroke: function() { @@ -4246,9 +4270,7 @@ var CanvasGraphics = (function() { // wait until fill to actually get the pattern var pattern = Pattern.parse(arguments, cs, this.xref, this.res, this.ctx); -// this.current.fillColor = pattern; - this.ctx.fillStyle = pattern.getPattern(); - this.current.fillColor = "blah"; + this.current.fillColor = pattern; } else { this.setFillColor.apply(this, arguments); } @@ -4262,7 +4284,7 @@ var CanvasGraphics = (function() { setStrokeRGBColor: function(r, g, b) { var color = Util.makeCssRgb(r, g, b); this.ctx.strokeStyle = color; - this.current.fillColor = color; + this.current.strokeColor = color; }, setFillRGBColor: function(r, g, b) { var color = Util.makeCssRgb(r, g, b); @@ -4272,7 +4294,7 @@ var CanvasGraphics = (function() { setStrokeCMYKColor: function(c, m, y, k) { var color = Util.makeCssCmyk(c, m, y, k); this.ctx.strokeStyle = color; - this.current.fillColor = color; + this.current.strokeColor = color; }, setFillCMYKColor: function(c, m, y, k) { var color = Util.makeCssCmyk(c, m, y, k); @@ -5125,9 +5147,10 @@ var TilingPattern = (function() { }; constructor.prototype = { - getPattern: function tiling_getPattern(ctx) { + getPattern: function tiling_getPattern() { var matrix = this.matrix; var curMatrix = this.curMatrix; + var ctx = this.ctx; if (curMatrix) ctx.setTransform.apply(ctx, curMatrix); From c438a8748a9ac46155d9844ea7ba19a70c4df231 Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 15 Jul 2011 11:41:39 -0700 Subject: [PATCH 08/11] fix type --- pdf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdf.js b/pdf.js index d371330ac..fe2d38ac6 100644 --- a/pdf.js +++ b/pdf.js @@ -4055,7 +4055,7 @@ var CanvasGraphics = (function() { } var strokeColor = this.current.strokeColor; - if (stokeColor && strokeColor.type === "Pattern") { + if (strokeColor && strokeColor.type === "Pattern") { ctx.save(); ctx.strokeStyle = strokeColor.getPattern(ctx); ctx.stroke(); From 5f4b84146f2ab2bd95ff1a620e61d87f136236df Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 15 Jul 2011 12:05:14 -0700 Subject: [PATCH 09/11] fix to Util.getCssCmyk --- pdf.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pdf.js b/pdf.js index fe2d38ac6..bf34d9398 100644 --- a/pdf.js +++ b/pdf.js @@ -4512,7 +4512,8 @@ var Util = (function() { }; constructor.makeCssCmyk = function makecmyk(c, m, y, k) { var c = (new DeviceCmykCS()).getRgb([c, m, y, k]); - return 'rgb(' + c[0] + ',' + c[1] + ',' + c[2] + ')'; + var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0; + return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; }; constructor.applyTransform = function apply(p, m) { var xt = p[0] * m[0] + p[1] * m[2] + m[4]; From 2098d840dae1222f4e998b56d1eaa396fec67dad Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 15 Jul 2011 12:06:38 -0700 Subject: [PATCH 10/11] fix to dummyshading --- pdf.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pdf.js b/pdf.js index bf34d9398..bc726ccd2 100644 --- a/pdf.js +++ b/pdf.js @@ -4946,7 +4946,9 @@ var Pattern = (function() { })(); var DummyShading = (function() { - function constructor() {}; + function constructor() { + this.type = 'Pattern'; + }; constructor.prototype = { getPattern: function dummy_getpattern() { return 'hotpink'; From 636b1105b028a598fb61c7a825848a702f4f1d55 Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 18 Jul 2011 09:13:02 -0700 Subject: [PATCH 11/11] cleanup --- pdf.js | 73 ++++++++++++---------------------------------------------- 1 file changed, 15 insertions(+), 58 deletions(-) diff --git a/pdf.js b/pdf.js index bc726ccd2..bab032119 100644 --- a/pdf.js +++ b/pdf.js @@ -3817,62 +3817,10 @@ var CanvasExtraState = (function() { this.old = old; } + constructor.prototype = { - get fillColorSpace() { - var cs = this.fillColorSpaceObj; - if (cs) - return cs; - - var old = this.old; - if (old) - return old.fillColorSpace; - - return null; - }, - set fillColorSpace(cs) { - this.fillColorSpaceObj = cs; - }, - get strokeColorSpace() { - var cs = this.strokeColorSpaceObj; - if (cs) - return cs; - - var old = this.old; - if (old) - return old.strokeColorSpace; - - return null; - }, - set strokeColorSpace(cs) { - this.strokeColorSpaceObj = cs; - }, - get fillColor() { - var color = this.fillColorObj; - if (color) - return color; - - var old = this.old; - if (old) - return old.fillColor; - - return null; - }, - set fillColor(color) { - this.fillColorObj = color; - }, - get strokeColor() { - var color = this.strokeColorObj; - if (color) - return color; - - var old = this.old; - if (old) - return old.strokeColor; - - return null; - }, - set strokeColor(color) { - this.strokeColorObj = color; + clone: function canvasextra_clone() { + return Object.create(this); } }; return constructor; @@ -3965,7 +3913,7 @@ var CanvasGraphics = (function() { } var old = this.current; this.stateStack.push(old); - this.current = new CanvasExtraState(old); + this.current = old.clone(); }, restore: function() { var prev = this.stateStack.pop(); @@ -4007,6 +3955,8 @@ var CanvasGraphics = (function() { var ctx = this.ctx; var strokeColor = this.current.strokeColor; if (strokeColor && strokeColor.type === "Pattern") { + // for patterns, we transform to pattern space, calculate + // the pattern, call stroke, and restore to user space ctx.save(); ctx.strokeStyle = strokeColor.getPattern(ctx); ctx.stroke(); @@ -4250,7 +4200,9 @@ var CanvasGraphics = (function() { var cs = this.current.strokeColorSpace; if (cs.name == 'Pattern') { - // wait until fill to actually get the pattern + // wait until fill to actually get the pattern, since Canvas + // calcualtes the pattern according to the current coordinate space, + // not the space when the pattern is set. var pattern = Pattern.parse(arguments, cs, this.xref, this.res, this.ctx); this.current.strokeColor = pattern; @@ -4957,6 +4909,8 @@ var DummyShading = (function() { return constructor; })(); +// Radial and axial shading have very similar implementations +// If needed, the implementations can be broken into two classes var RadialAxialShading = (function() { function constructor(dict, matrix, xref, res, ctx) { this.matrix = matrix; @@ -5099,7 +5053,10 @@ var TilingPattern = (function() { var width = botRight[0] - topLeft[0]; var height = botRight[1] - topLeft[1]; - // TODO: hack to avoid OOM, remove then pattern code is fixed + // TODO: hack to avoid OOM, we would idealy 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;