diff --git a/pdf.js b/pdf.js index 418db2462..87335317a 100644 --- a/pdf.js +++ b/pdf.js @@ -1911,14 +1911,19 @@ var CanvasGraphics = (function() { // Shading shadingFill: function(entryRef) { + if (!this.current.bbox) + TODO("bbox"); + var shadingRes = this.res.get("Shading"); if (!shadingRes) return; - shadingRes = this.xref.fetchIfRef(shadingRes); + + var xref = this.xref; + shadingRes = xref.fetchIfRef(shadingRes); var shading = shadingRes.get(entryRef.name); if (!shading) return; - shading = this.xref.fetchIfRef(shading); + shading = xref.fetchIfRef(shading); if (!shading) return; @@ -1931,8 +1936,13 @@ var CanvasGraphics = (function() { this.endPath(); } + var cs = shading.get2("ColorSpace", "CS"); TODO("shading-fill color space"); + var background = shading.get("Background"); + if (background) + TODO("handle background colors"); + var type = shading.get("ShadingType"); switch (type) { case 1: @@ -1952,10 +1962,10 @@ var CanvasGraphics = (function() { this.restore(); }, + fillAxialShading: function(sh) { - var coordsArr = sh.get("Coords"); - var x0 = coordsArr[0], y0 = coordsArr[1], - x1 = coordsArr[2], y1 = coordsArr[3]; + var cds = sh.get("Coords"); + var t0 = 0.0, t1 = 1.0; if (sh.has("Domain")) { @@ -1967,19 +1977,29 @@ var CanvasGraphics = (function() { 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 (!IsFunction(fnObj)) + error("invalid function"); + fn = new Function(this.xref, fnObj); + + var gradient = this.ctx.createLinearGradient(cds[0], cds[1], cds[2], cds[3]); + var step = (t1 - t0) / 10; + + for (var i = t0; i <= t1; i += step) { + var c = fn.func([i]); + var clength = c.length; + for (var j = 0; j < clength; ++j) + c[j] = Math.round(c[j] * 255); + gradient.addColorStop(i, "rgb("+c[0] + "," + c[1] + "," + c[2] + ")"); } - var fn = sh.get("Function"); - fn = this.xref.fetchIfRef(fn); - - var gradient = this.ctx.createLinearGradient(x0, y0, x1, y1); - - gradient.addColorStop(0, 'rgb(0,0,255)'); - gradient.addColorStop(1, 'rgb(0,255,0)'); - this.ctx.fillStyle = gradient; - this.ctx.fill(); - this.consumePath(); + + // HACK to draw the gradient onto an infinite rectangle + this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10); }, // XObjects @@ -2292,3 +2312,162 @@ var ColorSpace = (function() { return constructor; })(); + +var Function = (function() { + function constructor(xref, fn) { + var dict = fn.dict; + if (!dict) + dict = fn; + + var type = dict.get("FunctionType"); + + switch(type) { + case 0: + this.constructSampled(fn, dict); + break; + case 2: + this.constructInterpolated(); + break; + case 3: + this.constructStiched(); + break; + case 4: + this.constructPostScript(); + break; + default: + error("Unknown type of function"); + } + }; + + constructor.prototype = { + constructSampled: function(str, dict) { + var domain = dict.get("Domain"); + var range = dict.get("Range"); + + if (!domain || !range) + error("No domain or range"); + + var inputSize = domain.length / 2; + var outputSize = range.length / 2; + + if (inputSize != 1) + error("No support for multi-variable inputs to functions"); + + var size = dict.get("Size"); + var bps = dict.get("BitsPerSample"); + var order = dict.get("Order"); + if (!order) + order = 1; + if (order !== 1) + error ("No support for cubic spline interpolation"); + + var encode = dict.get("Encode"); + if (!encode) { + encode = []; + for (var i = 0; i < inputSize; ++i) { + encode.push(0); + encode.push(size[i] - 1); + } + } + var decode = dict.get("Decode"); + if (!decode) + decode = range; + + var samples = this.getSampleArray(size, outputSize, bps, str); + + this.func = function(args) { + var clip = function(v, min, max) { + if (v > max) + v = max; + else if (v < min) + v = min + return v; + } + + if (inputSize != args.length) + error("Incorrect number of arguments"); + + for (var i = 0; i < inputSize; i++) { + var i2 = i * 2; + + // clip to the domain + var v = clip(args[i], domain[i2], domain[i2 + 1]); + + // encode + v = encode[i2] + ((v - domain[i2]) * + (encode[i2 + 1] - encode[i2]) / + (domain[i2 + 1] - domain[i2])); + + // clip to the size + args[i] = clip(v, 0, size[i] - 1); + } + + // interpolate to table + TODO("Multi-dimensional interpolation"); + var floor = Math.floor(args[0]); + var ceil = Math.ceil(args[0]); + var scale = args[0] - floor; + + floor *= outputSize; + ceil *= outputSize; + + var output = []; + for (var i = 0; i < outputSize; ++i) { + if (ceil == floor) { + var v = samples[ceil + i]; + } else { + var low = samples[floor + i]; + var high = samples[ceil + i]; + var v = low * scale + high * (1 - scale); + } + + var i2 = i * 2; + // decode + v = decode[i2] + (v * (decode[i2 + 1] - decode[i2]) / + ((1 << bps) - 1)); + + // clip to the domain + output.push(clip(v, range[i2], range[i2 + 1])); + } + + return output; + } + }, + getSampleArray: function(size, outputSize, bps, str) { + var length = 1; + for (var i = 0; i < size.length; i++) + length *= size[i]; + length *= outputSize; + + var array = []; + var codeSize = 0; + var codeBuf = 0; + + var strBytes = str.getBytes((length * bps + 7) / 8); + var strIdx = 0; + for (var i = 0; i < length; i++) { + var b; + while (codeSize < bps) { + codeBuf <<= 8; + codeBuf |= strBytes[strIdx++]; + codeSize += 8; + } + codeSize -= bps + array.push(codeBuf >> codeSize); + codeBuf &= (1 << codeSize) - 1; + } + return array; + }, + constructInterpolated: function() { + error("unhandled type of function"); + }, + constructStiched: function() { + error("unhandled type of function"); + }, + constructPostScript: function() { + error("unhandled type of function"); + } + }; + + return constructor; +})();