From 74ec7a410389cd589400b30218021d8b0a7cb1a6 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Wed, 1 Feb 2012 16:04:12 -0500 Subject: [PATCH 1/4] LabCS infra working --- src/colorspace.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/colorspace.js b/src/colorspace.js index d67d928b1..0fb99bdcc 100644 --- a/src/colorspace.js +++ b/src/colorspace.js @@ -57,6 +57,8 @@ var ColorSpace = (function ColorSpaceClosure() { return new AlternateCS(numComps, ColorSpace.fromIR(alt), PDFFunction.fromIR(tintFnIR)); + case 'LabCS': + return new LabCS(); default: error('Unkown name ' + name); } @@ -146,6 +148,7 @@ var ColorSpace = (function ColorSpaceClosure() { var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3])); return ['AlternateCS', numComps, alt, tintFnIR]; case 'Lab': + return 'LabCS'; default: error('unimplemented color space object "' + mode + '"'); } @@ -409,3 +412,32 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() { return DeviceCmykCS; })(); +var LabCS = (function LabCSClosure() { + function LabCS() { + this.name = 'Lab'; + this.numComps = 3; + this.defaultColor = [0, 0, 0]; + } + LabCS.prototype = { + getRgb: function labcs_getRgb(color) { + return [0, 0, 0]; + }, + getRgbBuffer: function labcs_getRgbBuffer(input, bits) { + if (bits == 8) + return input; + var scale = 255 / ((1 << bits) - 1); + var i, length = input.length; + var rgbBuf = new Uint8Array(length); + + for (i = 0; i < length; ++i) + rgbBuf[i] = 0; + + return rgbBuf; + }, + isDefaultDecode: function labcs_isDefaultDecode(decodeMap) { + // TODO: not sure about this yet + return true; + } + }; + return LabCS; +})(); From a39d4872837600bb20544f3099353a05fec624d8 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Wed, 1 Feb 2012 16:13:42 -0500 Subject: [PATCH 2/4] LabCS infra: args passed OK --- src/colorspace.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/colorspace.js b/src/colorspace.js index 0fb99bdcc..8acbe8cd3 100644 --- a/src/colorspace.js +++ b/src/colorspace.js @@ -58,7 +58,10 @@ var ColorSpace = (function ColorSpaceClosure() { return new AlternateCS(numComps, ColorSpace.fromIR(alt), PDFFunction.fromIR(tintFnIR)); case 'LabCS': - return new LabCS(); + var whitePoint = IR[1].WhitePoint; + var blackPoint = IR[1].BlackPoint; + var range = IR[1].Range; + return new LabCS(whitePoint, blackPoint, range); default: error('Unkown name ' + name); } @@ -148,7 +151,8 @@ var ColorSpace = (function ColorSpaceClosure() { var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3])); return ['AlternateCS', numComps, alt, tintFnIR]; case 'Lab': - return 'LabCS'; + var params = cs[1].map; + return ['LabCS', params]; default: error('unimplemented color space object "' + mode + '"'); } @@ -413,10 +417,13 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() { })(); var LabCS = (function LabCSClosure() { - function LabCS() { + 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; } LabCS.prototype = { getRgb: function labcs_getRgb(color) { From 0fc6c03956923e770808d6768a0ac76016545869 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Wed, 1 Feb 2012 17:48:44 -0500 Subject: [PATCH 3/4] Lab color space, closes #1133 --- src/colorspace.js | 89 +++++++++++++++++++++++++++++++++--- src/util.js | 18 ++++++++ test/pdfs/issue1133.pdf.link | 1 + test/test_manifest.json | 7 +++ 4 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 test/pdfs/issue1133.pdf.link 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" } ] From 00b7e7d2552e66a71451f2fc4d06b644f76263c1 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Mon, 6 Feb 2012 16:10:15 -0800 Subject: [PATCH 4/4] isDefaultDecode(), as per reviewer comments --- src/colorspace.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/colorspace.js b/src/colorspace.js index 69da82536..57bc9c846 100644 --- a/src/colorspace.js +++ b/src/colorspace.js @@ -517,8 +517,14 @@ var LabCS = (function LabCSClosure() { return rgbBuf; }, isDefaultDecode: function labcs_isDefaultDecode(decodeMap) { - // TODO: not sure about this yet - return true; + // From Table 90 in Adobe's: + // "Document management - Portable document format", 1st ed, 2008 + if (decodeMap[0] === 0 && decodeMap[1] === 100 && + decodeMap[2] === this.amin && decodeMap[3] === this.amax && + decodeMap[4] === this.bmin && decodeMap[5] === this.bmax) + return true; + else + return false; } }; return LabCS;