diff --git a/src/colorspace.js b/src/colorspace.js index 8acbe8cd3..69da82536 100644 --- a/src/colorspace.js +++ b/src/colorspace.js @@ -416,28 +416,103 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() { return DeviceCmykCS; })(); +// +// LabCS: Based on "PDF Reference, Sixth Ed", p.250 +// var LabCS = (function LabCSClosure() { function LabCS(whitePoint, blackPoint, range) { this.name = 'Lab'; this.numComps = 3; this.defaultColor = [0, 0, 0]; - this.whitePoint = whitePoint; - this.blackPoint = blackPoint; - this.range = range; + + if (!whitePoint) + error('WhitePoint missing - required for color space Lab'); + blackPoint = blackPoint || [0, 0, 0]; + range = range || [-100, 100, -100, 100]; + + // Translate args to spec variables + this.XW = whitePoint[0]; + this.YW = whitePoint[1]; + this.ZW = whitePoint[2]; + this.amin = range[0]; + this.amax = range[1]; + this.bmin = range[2]; + this.bmax = range[3]; + + // These are here just for completeness - the spec doesn't offer any + // formulas that use BlackPoint in Lab + this.XB = blackPoint[0]; + this.YB = blackPoint[1]; + this.ZB = blackPoint[2]; + + // Validate vars as per spec + if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) + error('Invalid WhitePoint components, no fallback available'); + + if (this.XB < 0 || this.YB < 0 || this.ZB < 0) { + warn('Invalid BlackPoint, falling back to default'); + this.XB = this.YB = this.ZB = 0; + } + + if (this.amin > this.amax || this.bmin > this.bmax) { + warn('Invalid Range, falling back to defaults'); + this.amin = -100; + this.amax = 100; + this.bmin = -100; + this.bmax = 100; + } + }; + + // Function g(x) from spec + function g(x) { + if (x >= 6 / 29) + return x * x * x; + else + return (108 / 841) * (x - 4 / 29); } + LabCS.prototype = { getRgb: function labcs_getRgb(color) { - return [0, 0, 0]; + // Ls,as,bs <---> L*,a*,b* in the spec + var Ls = color[0], as = color[1], bs = color[2]; + + // Adjust limits of 'as' and 'bs' + as = as > this.amax ? this.amax : as; + as = as < this.amin ? this.amin : as; + bs = bs > this.bmax ? this.bmax : bs; + bs = bs < this.bmin ? this.bmin : bs; + + // Computes intermediate variables X,Y,Z as per spec + var M = (Ls + 16) / 116; + var L = M + (as / 500); + var N = M - (bs / 200); + var X = this.XW * g(L); + var Y = this.YW * g(M); + var Z = this.ZW * g(N); + + // XYZ to RGB 3x3 matrix, from: + // http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html#RTFToC18 + var XYZtoRGB = [3.240479, -1.537150, -0.498535, + -0.969256, 1.875992, 0.041556, + 0.055648, -0.204043, 1.057311]; + + return Util.apply3dTransform(XYZtoRGB, [X, Y, Z]); }, getRgbBuffer: function labcs_getRgbBuffer(input, bits) { if (bits == 8) return input; var scale = 255 / ((1 << bits) - 1); - var i, length = input.length; + var i, length = input.length / 3; var rgbBuf = new Uint8Array(length); - for (i = 0; i < length; ++i) - rgbBuf[i] = 0; + var j = 0; + for (i = 0; i < length; ++i) { + // Convert L*, a*, s* into RGB + var rgb = this.getRgb([input[i], input[i + 1], input[i + 2]]); + rgbBuf[j++] = rgb[0]; + rgbBuf[j++] = rgb[1]; + rgbBuf[j++] = rgb[2]; + } return rgbBuf; }, diff --git a/src/util.js b/src/util.js index 759908e9e..caa523ec7 100644 --- a/src/util.js +++ b/src/util.js @@ -78,21 +78,39 @@ var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; var Util = (function UtilClosure() { function Util() {} + Util.makeCssRgb = function makergb(r, g, b) { var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0; return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; }; + Util.makeCssCmyk = function makecmyk(c, m, y, k) { c = (new DeviceCmykCS()).getRgb([c, m, y, k]); var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0; return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; }; + + // For 2d affine transforms Util.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]; }; + // Apply a generic 3d matrix M on a 3-vector v: + // | a b c | | X | + // | d e f | x | Y | + // | g h i | | Z | + // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i], + // with v as [X,Y,Z] + Util.apply3dTransform = function apply3d(m, v) { + return [ + m[0] * v[0] + m[1] * v[1] + m[2] * v[2], + m[3] * v[0] + m[4] * v[1] + m[5] * v[2], + m[6] * v[0] + m[7] * v[1] + m[8] * v[2] + ]; + } + return Util; })(); diff --git a/test/pdfs/issue1133.pdf.link b/test/pdfs/issue1133.pdf.link new file mode 100644 index 000000000..2480ba8f8 --- /dev/null +++ b/test/pdfs/issue1133.pdf.link @@ -0,0 +1 @@ +http://www.cscw2012.org/docs/program.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index c6fed0a35..dd5c54147 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -444,5 +444,12 @@ "rounds": 1, "link": false, "type": "eq" + }, + { "id": "issue1133", + "file": "pdfs/issue1133.pdf", + "md5": "d1b61580cb100e3df93d33703af1773a", + "rounds": 1, + "link": true, + "type": "eq" } ]