Implemented type 2 shading for the pdf (aka gradients)

This commit is contained in:
sbarman 2011-06-14 11:55:27 -07:00
parent 609842b76c
commit de9150b528

209
pdf.js
View File

@ -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;
})();