|
|
|
@ -617,6 +617,19 @@ function IsRef(v) {
@@ -617,6 +617,19 @@ function IsRef(v) {
|
|
|
|
|
return v instanceof Ref; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function IsPDFFunction(v) { |
|
|
|
|
var fnDict; |
|
|
|
|
if (typeof v != "object") |
|
|
|
|
return false; |
|
|
|
|
else if (IsDict(v)) |
|
|
|
|
fnDict = v; |
|
|
|
|
else if (IsStream(v)) |
|
|
|
|
fnDict = v.dict; |
|
|
|
|
else |
|
|
|
|
return false; |
|
|
|
|
return fnDict.has("FunctionType"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var EOF = {}; |
|
|
|
|
|
|
|
|
|
function IsEOF(v) { |
|
|
|
@ -2055,16 +2068,16 @@ var CanvasGraphics = (function() {
@@ -2055,16 +2068,16 @@ var CanvasGraphics = (function() {
|
|
|
|
|
|
|
|
|
|
// Shading
|
|
|
|
|
shadingFill: function(entryRef) { |
|
|
|
|
var shadingRes = this.res.get("Shading"); |
|
|
|
|
var xref = this.xref; |
|
|
|
|
var res = this.res; |
|
|
|
|
|
|
|
|
|
var shadingRes = xref.fetchIfRef(res.get("Shading")); |
|
|
|
|
if (!shadingRes) |
|
|
|
|
return; |
|
|
|
|
shadingRes = this.xref.fetchIfRef(shadingRes); |
|
|
|
|
var shading = shadingRes.get(entryRef.name); |
|
|
|
|
if (!shading) |
|
|
|
|
return; |
|
|
|
|
shading = this.xref.fetchIfRef(shading); |
|
|
|
|
error("No shading resource found"); |
|
|
|
|
|
|
|
|
|
var shading = xref.fetchIfRef(shadingRes.get(entryRef.name)); |
|
|
|
|
if (!shading) |
|
|
|
|
return; |
|
|
|
|
error("No shading object found"); |
|
|
|
|
|
|
|
|
|
this.save(); |
|
|
|
|
|
|
|
|
@ -2075,27 +2088,25 @@ var CanvasGraphics = (function() {
@@ -2075,27 +2088,25 @@ var CanvasGraphics = (function() {
|
|
|
|
|
this.endPath(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var cs = shading.get2("ColorSpace", "CS"); |
|
|
|
|
TODO("shading-fill color space"); |
|
|
|
|
|
|
|
|
|
var type = shading.get("ShadingType"); |
|
|
|
|
switch (type) { |
|
|
|
|
case 1: |
|
|
|
|
this.fillFunctionShading(shading); |
|
|
|
|
break; |
|
|
|
|
case 2: |
|
|
|
|
this.fillAxialShading(shading); |
|
|
|
|
break; |
|
|
|
|
case 3: |
|
|
|
|
this.fillRadialShading(shading); |
|
|
|
|
break; |
|
|
|
|
case 4: case 5: case 6: case 7: |
|
|
|
|
TODO("shading fill type "+ type); |
|
|
|
|
default: |
|
|
|
|
malformed("Unknown shading type "+ type); |
|
|
|
|
} |
|
|
|
|
var background = shading.get("Background"); |
|
|
|
|
if (background) |
|
|
|
|
TODO("handle background colors"); |
|
|
|
|
|
|
|
|
|
const types = [null, this.fillFunctionShading, |
|
|
|
|
this.fillAxialShading, this.fillRadialShading]; |
|
|
|
|
|
|
|
|
|
var typeNum = shading.get("ShadingType"); |
|
|
|
|
var fillFn = types[typeNum]; |
|
|
|
|
if (!fillFn) |
|
|
|
|
error("Unknown type of shading"); |
|
|
|
|
fillFn.apply(this, [shading]); |
|
|
|
|
|
|
|
|
|
this.restore(); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
fillAxialShading: function(sh) { |
|
|
|
|
var coordsArr = sh.get("Coords"); |
|
|
|
|
var x0 = coordsArr[0], y0 = coordsArr[1], |
|
|
|
@ -2111,19 +2122,30 @@ var CanvasGraphics = (function() {
@@ -2111,19 +2122,30 @@ var CanvasGraphics = (function() {
|
|
|
|
|
if (sh.has("Extend")) { |
|
|
|
|
var extendArr = sh.get("Extend"); |
|
|
|
|
extendStart = extendArr[0], extendEnd = extendArr[1]; |
|
|
|
|
TODO("Support extend"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var fn = sh.get("Function"); |
|
|
|
|
fn = this.xref.fetchIfRef(fn); |
|
|
|
|
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"); |
|
|
|
|
fn = new PDFFunction(this.xref, fnObj); |
|
|
|
|
|
|
|
|
|
var gradient = this.ctx.createLinearGradient(x0, y0, x1, y1); |
|
|
|
|
var step = (t1 - t0) / 10; |
|
|
|
|
|
|
|
|
|
gradient.addColorStop(0, 'rgb(0,0,255)'); |
|
|
|
|
gradient.addColorStop(1, 'rgb(0,255,0)'); |
|
|
|
|
for (var i = t0; i <= t1; i += step) { |
|
|
|
|
var c = fn.func([i]); |
|
|
|
|
gradient.addColorStop(i, this.makeCssRgb.apply(this, c)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.ctx.fillStyle = gradient; |
|
|
|
|
this.ctx.fill(); |
|
|
|
|
this.consumePath(); |
|
|
|
|
|
|
|
|
|
// HACK to draw the gradient onto an infinite rectangle.
|
|
|
|
|
// PDF gradients are drawn across the entire image while
|
|
|
|
|
// Canvas only allows gradients to be drawn in a rectangle
|
|
|
|
|
this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
// Images
|
|
|
|
@ -2471,3 +2493,154 @@ var ColorSpace = (function() {
@@ -2471,3 +2493,154 @@ var ColorSpace = (function() {
|
|
|
|
|
|
|
|
|
|
return constructor; |
|
|
|
|
})(); |
|
|
|
|
|
|
|
|
|
var PDFFunction = (function() { |
|
|
|
|
function constructor(xref, fn) { |
|
|
|
|
var dict = fn.dict; |
|
|
|
|
if (!dict) |
|
|
|
|
dict = fn; |
|
|
|
|
|
|
|
|
|
const types = [this.constructSampled, null, |
|
|
|
|
this.constructInterpolated, this.constructStiched, |
|
|
|
|
this.constructPostScript]; |
|
|
|
|
|
|
|
|
|
var typeNum = dict.get("FunctionType"); |
|
|
|
|
var typeFn = types[typeNum]; |
|
|
|
|
if (!typeFn) |
|
|
|
|
error("Unknown type of function"); |
|
|
|
|
|
|
|
|
|
typeFn.apply(this, [fn, dict]); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
})(); |
|
|
|
|