1 /* AbiSource Program Utilities
2  * Copyright (C) 1998 AbiSource, Inc.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301 USA.
18  */
19 
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <math.h>
24 #include <ctype.h>
25 
26 #include "ut_color.h"
27 #include "ut_assert.h"
28 #include "ut_string.h"
29 #include "ut_debugmsg.h"
30 
31 #include <limits.h>
32 
33 /*****************************************************************/
34 /*****************************************************************/
35 
36 /* These are the colors defined in the SVG standard (I haven't checked the final recommendation for changes)
37  */
38 struct colorToRGBMapping
39 {
40   const char * m_name;
41 
42   unsigned char m_red;
43   unsigned char m_green;
44   unsigned char m_blue;
45 };
46 
47 static struct colorToRGBMapping s_Colors[] =
48 {
49 	{ "aliceblue",			240, 248, 255 },
50 	{ "antiquewhite",		250, 235, 215 },
51 	{ "aqua",			  0, 255, 255 },
52 	{ "aquamarine",			127, 255, 212 },
53 	{ "azure",			240, 255, 255 },
54 	{ "beige",			245, 245, 220 },
55 	{ "bisque",			255, 228, 196 },
56 	{ "black",			  0,   0,   0 },
57 	{ "blanchedalmond",		255, 235, 205 },
58 	{ "blue",			  0,   0, 255 },
59 	{ "blueviolet",			138,  43, 226 },
60 	{ "brown",			165,  42,  42 },
61 	{ "burlywood",			222, 184, 135 },
62 	{ "cadetblue",			 95, 158, 160 },
63 	{ "chartreuse",			127, 255,   0 },
64 	{ "chocolate",			210, 105,  30 },
65 	{ "coral",			255, 127,  80 },
66 	{ "cornflowerblue",		100, 149, 237 },
67 	{ "cornsilk",			255, 248, 220 },
68 	{ "crimson",			220,  20,  60 },
69 	{ "cyan",			  0, 255, 255 },
70 	{ "darkblue",			  0,   0, 139 },
71 	{ "darkcyan",			  0, 139, 139 },
72 	{ "darkgoldenrod",		184, 134,  11 },
73 	{ "darkgray",			169, 169, 169 },
74 	{ "darkgreen",			  0, 100,   0 },
75 	{ "darkgrey",			169, 169, 169 },
76 	{ "darkkhaki",			189, 183, 107 },
77 	{ "darkmagenta",		139,   0, 139 },
78 	{ "darkolivegreen",		 85, 107,  47 },
79 	{ "darkorange",			255, 140,   0 },
80 	{ "darkorchid",			153,  50, 204 },
81 	{ "darkred",			139,   0,   0 },
82 	{ "darksalmon",			233, 150, 122 },
83 	{ "darkseagreen",		143, 188, 143 },
84 	{ "darkslateblue",		 72,  61, 139 },
85 	{ "darkslategray",		 47,  79,  79 },
86 	{ "darkslategrey",		 47,  79,  79 },
87 	{ "darkturquoise",		  0, 206, 209 },
88 	{ "darkviolet",			148,   0, 211 },
89 	{ "deeppink",			255,  20, 147 },
90 	{ "deepskyblue",		  0, 191, 255 },
91 	{ "dimgray",			105, 105, 105 },
92 	{ "dimgrey",			105, 105, 105 },
93 	{ "dodgerblue",			 30, 144, 255 },
94 	{ "firebrick",			178,  34,  34 },
95 	{ "floralwhite",		255, 250, 240 },
96 	{ "forestgreen",		 34, 139,  34 },
97 	{ "fuchsia",			255,   0, 255 },
98 	{ "gainsboro",			220, 220, 220 },
99 	{ "ghostwhite",			248, 248, 255 },
100 	{ "gold",			255, 215,   0 },
101 	{ "goldenrod",			218, 165,  32 },
102 	{ "gray",			128, 128, 128 },
103 	{ "grey",			128, 128, 128 },
104 	{ "green",			  0, 128,   0 },
105 	{ "greenyellow",		173, 255,  47 },
106 	{ "honeydew",			240, 255, 240 },
107 	{ "hotpink",			255, 105, 180 },
108 	{ "indianred",			205,  92,  92 },
109 	{ "indigo",			 75,   0, 130 },
110 	{ "ivory",			255, 255, 240 },
111 	{ "khaki",			240, 230, 140 },
112 	{ "lavender",			230, 230, 250 },
113 	{ "lavenderblush",		255, 240, 245 },
114 	{ "lawngreen",			124, 252,   0 },
115 	{ "lemonchiffon",		255, 250, 205 },
116 	{ "lightblue",			173, 216, 230 },
117 	{ "lightcoral",			240, 128, 128 },
118 	{ "lightcyan",			224, 255, 255 },
119 	{ "lightgoldenrodyellow",	250, 250, 210 },
120 	{ "lightgray",			211, 211, 211 },
121 	{ "lightgreen",			144, 238, 144 },
122 	{ "lightgrey",			211, 211, 211 },
123 	{ "lightpink",			255, 182, 193 },
124 	{ "lightsalmon",		255, 160, 122 },
125 	{ "lightseagreen",		 32, 178, 170 },
126 	{ "lightskyblue",		135, 206, 250 },
127 	{ "lightslategray",		119, 136, 153 },
128 	{ "lightslategrey",		119, 136, 153 },
129 	{ "lightsteelblue",		176, 196, 222 },
130 	{ "lightyellow",		255, 255, 224 },
131 	{ "lime",			  0, 255,   0 },
132 	{ "limegreen",			 50, 205,  50 },
133 	{ "linen",			250, 240, 230 },
134 	{ "magenta",			255,   0, 255 },
135 	{ "maroon",			128,   0,   0 },
136 	{ "mediumaquamarine",		102, 205, 170 },
137 	{ "mediumblue",			  0,   0, 205 },
138 	{ "mediumorchid",		186,  85, 211 },
139 	{ "mediumpurple",		147, 112, 219 },
140 	{ "mediumseagreen",		 60, 179, 113 },
141 	{ "mediumslateblue",		123, 104, 238 },
142 	{ "mediumspringgreen",		  0, 250, 154 },
143 	{ "mediumturquoise",		 72, 209, 204 },
144 	{ "mediumvioletred",		199,  21, 133 },
145 	{ "midnightblue",		 25,  25, 112 },
146 	{ "mintcream",			245, 255, 250 },
147 	{ "mistyrose",			255, 228, 225 },
148 	{ "moccasin",			255, 228, 181 },
149 	{ "navajowhite",		255, 222, 173 },
150 	{ "navy",			  0,   0, 128 },
151 	{ "oldlace",			253, 245, 230 },
152 	{ "olive",			128, 128,   0 },
153 	{ "olivedrab",			107, 142,  35 },
154 	{ "orange",			255, 165,   0 },
155 	{ "orangered",			255,  69,   0 },
156 	{ "orchid",			218, 112, 214 },
157 	{ "palegoldenrod",		238, 232, 170 },
158 	{ "palegreen",			152, 251, 152 },
159 	{ "paleturquoise",		175, 238, 238 },
160 	{ "palevioletred",		219, 112, 147 },
161 	{ "papayawhip",			255, 239, 213 },
162 	{ "peachpuff",			255, 218, 185 },
163 	{ "peru",			205, 133,  63 },
164 	{ "pink",			255, 192, 203 },
165 	{ "plum",			221, 160, 221 },
166 	{ "powderblue",			176, 224, 230 },
167 	{ "purple",			128,   0, 128 },
168 	{ "red",			255,   0,   0 },
169 	{ "rosybrown",			188, 143, 143 },
170 	{ "royalblue",			 65, 105, 225 },
171 	{ "saddlebrown",		139,  69,  19 },
172 	{ "salmon",			250, 128, 114 },
173 	{ "sandybrown",			244, 164,  96 },
174 	{ "seagreen",			 46, 139,  87 },
175 	{ "seashell",			255, 245, 238 },
176 	{ "sienna",			160,  82,  45 },
177 	{ "silver",			192, 192, 192 },
178 	{ "skyblue",			135, 206, 235 },
179 	{ "slateblue",			106,  90, 205 },
180 	{ "slategray",			112, 128, 144 },
181 	{ "slategrey",			112, 128, 144 },
182 	{ "snow",			255, 250, 250 },
183 	{ "springgreen",		  0, 255, 127 },
184 	{ "steelblue",			 70, 130, 180 },
185 	{ "tan",			210, 180, 140 },
186 	{ "teal",			  0, 128, 128 },
187 	{ "thistle",			216, 191, 216 },
188 	{ "tomato",			255,  99,  71 },
189 	{ "turquoise",			 64, 224, 208 },
190 	{ "violet",			238, 130, 238 },
191 	{ "wheat",			245, 222, 179 },
192 	{ "white",			255, 255, 255 },
193 	{ "whitesmoke",			245, 245, 245 },
194 	{ "yellow",			255, 255,   0 },
195 	{ "yellowgreen",		154, 205,  50 }
196 };
197 
198 
color_compare(const void * a,const void * b)199 static int color_compare (const void * a, const void * b)
200 {
201   const char * name = static_cast<const char *>(a);
202   const colorToRGBMapping * id = static_cast<const colorToRGBMapping *>(b);
203 
204   return g_ascii_strcasecmp (name, id->m_name);
205 }
206 
207 
x_hexDigit(char c)208 static int x_hexDigit(char c)
209 {
210 	if ((c>='0') && (c<='9'))
211 	{
212 		return c-'0';
213 	}
214 
215 	if ((c>='a') && (c<='f'))
216 	{
217 		return c - 'a' + 10;
218 	}
219 
220 	if ((c>='A') && (c<='F'))
221 	{
222 		return c - 'A' + 10;
223 	}
224 
225 	UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
226 
227 	return 0;
228 }
229 
UT_HashColor()230 UT_HashColor::UT_HashColor ()
231 {
232 	m_colorBuffer[0] = 0;
233 }
234 
~UT_HashColor()235 UT_HashColor::~UT_HashColor ()
236 {
237 	//
238 }
239 
setColor(const char * color)240 const char * UT_HashColor::setColor (const char * color)
241 {
242 	m_colorBuffer[0] = 0;
243 	if (color == 0) return 0;
244 
245 	if (color[0] == '#') return setHashIfValid (color + 1);
246 
247 	return lookupNamedColor (color);
248 }
249 
setColor(unsigned char r,unsigned char g,unsigned char b)250 const char * UT_HashColor::setColor (unsigned char r, unsigned char g, unsigned char b)
251 {
252 	static const char hexval[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
253 
254 	m_colorBuffer[0] = '#';
255 	m_colorBuffer[1] = hexval[(r >> 4) & 0x0f];
256 	m_colorBuffer[2] = hexval[ r       & 0x0f];
257 	m_colorBuffer[3] = hexval[(g >> 4) & 0x0f];
258 	m_colorBuffer[4] = hexval[ g       & 0x0f];
259 	m_colorBuffer[5] = hexval[(b >> 4) & 0x0f];
260 	m_colorBuffer[6] = hexval[ b       & 0x0f];
261 	m_colorBuffer[7] = 0;
262 
263 	return static_cast<const char *>(m_colorBuffer);
264 }
265 
lookupNamedColor(const char * color_name)266 const char * UT_HashColor::lookupNamedColor (const char * color_name)
267 {
268 	m_colorBuffer[0] = 0;
269 	if (color_name == 0) return 0;
270 
271 	size_t length = sizeof (s_Colors) / sizeof (s_Colors[0]);
272 
273 	colorToRGBMapping * id = 0;
274 	id = static_cast<colorToRGBMapping *>(bsearch (color_name, s_Colors, static_cast<int>(length), sizeof (colorToRGBMapping), color_compare));
275 
276 	if (id == 0) return 0;
277 
278 	return setColor (id->m_red, id->m_green, id->m_blue);
279 }
280 
setHashIfValid(const char * color_hash)281 const char * UT_HashColor::setHashIfValid (const char * color_hash)
282 {
283 	m_colorBuffer[0] = 0;
284 	if (color_hash == 0) return 0;
285 
286 	bool isValid = true;
287 	for (int i = 0; i < 6; i++)
288 	{
289 		switch (color_hash[i])
290 		{
291 		case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
292 		case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
293 			m_colorBuffer[i+1] = color_hash[i];
294 		break;
295 		case 'A': m_colorBuffer[i+1] = 'a'; break;
296 		case 'B': m_colorBuffer[i+1] = 'b'; break;
297 		case 'C': m_colorBuffer[i+1] = 'c'; break;
298 		case 'D': m_colorBuffer[i+1] = 'd'; break;
299 		case 'E': m_colorBuffer[i+1] = 'e'; break;
300 		case 'F': m_colorBuffer[i+1] = 'f'; break;
301 		default:
302 			isValid = false;
303 		break;
304 		}
305 		if (!isValid) break;
306 	}
307 	if (!isValid) return 0;
308 
309 	m_colorBuffer[0] = '#';
310 	m_colorBuffer[7] = 0;
311 
312 	return static_cast<const char *>(m_colorBuffer);
313 }
314 
rgb()315 const UT_RGBColor UT_HashColor::rgb ()
316 {
317 	unsigned char r = 0;
318 	unsigned char g = 0;
319 	unsigned char b = 0;
320 
321 	if (m_colorBuffer[0])
322 	{
323 		r = static_cast<unsigned char>(x_hexDigit (m_colorBuffer[1]) << 4) | static_cast<unsigned char>(x_hexDigit (m_colorBuffer[2]));
324 		g = static_cast<unsigned char>(x_hexDigit (m_colorBuffer[3]) << 4) | static_cast<unsigned char>(x_hexDigit (m_colorBuffer[4]));
325 		b = static_cast<unsigned char>(x_hexDigit (m_colorBuffer[5]) << 4) | static_cast<unsigned char>(x_hexDigit (m_colorBuffer[6]));
326 	}
327 
328 	return UT_RGBColor (r, g, b);
329 }
330 
parseColorToNextDelim(const char * p,UT_uint32 & index)331 static int parseColorToNextDelim ( const char * p, UT_uint32 & index )
332 {
333   char buffer[7] = "" ;
334   index = 0 ;
335 
336   while (isdigit(*p))
337     {
338       buffer[index++] = *p++;
339     }
340   buffer[index] = 0;
341   return atoi(buffer);
342 }
343 
UT_parseGrayColor(const char * p,UT_RGBColor & c)344 static void UT_parseGrayColor(const char *p, UT_RGBColor& c)
345 {
346   UT_DEBUGMSG(("DOM: parsing gray value\n"));
347 
348   int grayVal = 0 ;
349 
350   p+=5; // go past gray(
351 
352   UT_uint32 index = 0;
353   grayVal= parseColorToNextDelim ( p, index ) ;
354 
355   c.m_red = grayVal;
356   c.m_grn = grayVal;
357   c.m_blu = grayVal;
358 }
359 
UT_parseCMYKColor(const char * p,UT_RGBColor & c)360 static void UT_parseCMYKColor(const char *p, UT_RGBColor& c)
361 {
362   // yes, i know that CMYK->RGB is lossy... DAL
363   // WARNING: !!!!UNTESTED!!!!
364 
365   UT_DEBUGMSG(("DOM: parsing CMYK value!!\n"));
366 
367   int cyanVal    = 0;
368   int magentaVal = 0;
369   int yellowVal  = 0;
370   int kVal       = 0;
371 
372   p += 5; // advance past "CMYK("
373 
374   UT_uint32 index = 0;
375 
376   cyanVal = parseColorToNextDelim ( p, index ) ;
377   p+=(index+1); index = 0;
378 
379   magentaVal = parseColorToNextDelim ( p, index ) ;
380   p+=(index+1); index = 0;
381 
382   yellowVal = parseColorToNextDelim ( p, index ) ;
383   p+=(index+1); index = 0;
384 
385   kVal = parseColorToNextDelim ( p, index ) ;
386 
387   int  cPlusK = cyanVal + kVal;
388   int  mPlusK  = magentaVal + kVal;
389   int  yPlusK = yellowVal + kVal;
390 
391   if (cPlusK < 255)
392     c.m_red = 255 - cPlusK;
393 
394   if (mPlusK < 255)
395     c.m_grn = 255 - mPlusK;
396 
397   if (yPlusK < 255)
398     c.m_blu = 255 - yPlusK;
399 
400   UT_DEBUGMSG(("DOM: CMYK (%d %d %d %d) -> RGB (%d %d %d)!!\n",
401 	       cyanVal, magentaVal, yellowVal, kVal,
402 	       c.m_red, c.m_grn, c.m_blu));
403 }
404 
405 
~UT_ColorPatImpl()406 UT_ColorPatImpl::~UT_ColorPatImpl()
407 {
408 }
409 
410 
UT_RGBColor()411 UT_RGBColor::UT_RGBColor()
412     : m_patImpl(NULL)
413 {
414 	m_red = 0;
415 	m_grn = 0;
416 	m_blu = 0;
417 	m_bIsTransparent = false;
418 }
419 
UT_RGBColor(unsigned char red,unsigned char grn,unsigned char blu,bool bTransparent)420 UT_RGBColor::UT_RGBColor(unsigned char red, unsigned char grn, unsigned char blu, bool bTransparent)
421     : m_patImpl(NULL)
422 {
423 	m_red = red;
424 	m_grn = grn;
425 	m_blu = blu;
426 	m_bIsTransparent = bTransparent;
427 }
428 
UT_RGBColor(const UT_RGBColor & c)429 UT_RGBColor::UT_RGBColor(const UT_RGBColor &c)
430 {
431 	m_red = c.m_red;
432 	m_grn = c.m_grn;
433 	m_blu = c.m_blu;
434 	m_bIsTransparent = c.m_bIsTransparent;
435     m_patImpl = ( c.m_patImpl ? c.m_patImpl->clone() : NULL );
436 }
437 
UT_RGBColor(const UT_ColorPatImpl * pat)438 UT_RGBColor::UT_RGBColor(const UT_ColorPatImpl * pat)
439     : m_red(0)
440     , m_grn(0)
441     , m_blu(0)
442     , m_bIsTransparent(false)
443     , m_patImpl(pat)
444 {
445 }
446 
447 
~UT_RGBColor()448 UT_RGBColor::~UT_RGBColor()
449 {
450     DELETEP(m_patImpl);
451 }
452 
453 
operator =(const UT_RGBColor & c)454 UT_RGBColor & UT_RGBColor::operator=(const  UT_RGBColor &c)
455 {
456 	m_red = c.m_red;
457 	m_grn = c.m_grn;
458 	m_blu = c.m_blu;
459 	m_bIsTransparent = c.m_bIsTransparent;
460     if(m_patImpl) {
461         delete m_patImpl;
462     }
463     m_patImpl = ( c.m_patImpl ? c.m_patImpl->clone() : NULL );
464 
465     return *this;
466 }
467 
468 
setColor(const char * pszColor)469 bool UT_RGBColor::setColor(const char * pszColor)
470 {
471 	unsigned char r = m_red, g = m_grn, b = m_blu;
472 
473 	if(!pszColor || !strcmp(pszColor,"transparent") /* || !strcmp(pszColor,"ffffff") */)
474 	{
475 		m_red = m_grn = m_blu = 255;
476 		m_bIsTransparent = true;
477 	}
478 	else
479 	{
480 		UT_parseColor(pszColor, *this);
481 		m_bIsTransparent = false;
482 	}
483 
484 	return (r != m_red || g != m_grn || b != m_blu);
485 }
486 
487 
UT_setColor(UT_RGBColor & col,unsigned char r,unsigned char g,unsigned char b,bool bTransparent)488 void UT_setColor(UT_RGBColor & col, unsigned char r, unsigned char g, unsigned char b, bool bTransparent)
489 {
490 	col.m_red = r;
491 	col.m_grn = g;
492 	col.m_blu = b;
493 	col.m_bIsTransparent = bTransparent;
494 }
495 
UT_parseColor(const char * p,UT_RGBColor & c)496 void UT_parseColor(const char *p, UT_RGBColor& c)
497 {
498         UT_uint32 len = strlen (p);
499 
500 	if ( len > 7 && strncmp ( p, "cmyk(", 5 ) == 0 )
501 	  {
502 	    // CMYK color. parse that out
503 	    UT_parseCMYKColor ( p, c ) ;
504 	    return;
505 	  }
506 
507 	if ( len > 6 && strncmp ( p, "gray(", 5 ) == 0 )
508 	  {
509 	    // grayscale color. parse that out
510 	    UT_parseGrayColor ( p, c ) ;
511 	    return ;
512 	  }
513 
514 
515 	if(!strcmp(p,"transparent") /* || !strcmp(p,"ffffff") */)
516 	{
517 		c.m_red = c.m_grn = c.m_blu = 255;
518 		c.m_bIsTransparent = true;
519 		return;
520 	}
521 
522 	UT_HashColor hash;
523 
524 	if (hash.setColor (p))
525 	  {
526 	    c = hash.rgb ();
527 	  }
528 	else if (hash.setHashIfValid (p))
529 	  {
530 	    c = hash.rgb ();
531 	  }
532 	else
533 	  {
534 	    UT_DEBUGMSG(("String = %s \n",p));
535 	    UT_ASSERT(UT_NOT_IMPLEMENTED);
536 	  }
537 
538 }
539 
540 /*! This function takes in a color string of any form (e.g. "red", "CMYK()",
541 	 "#000000", "000000", etc.) and returns an RGB hexadecimal string.
542 	 \param szColor The incoming string to parse
543 	 \param bPrefix The return string will be prefixed with a '#'
544 	 if bPrefix is true.  Defaults to false.
545 	 \return An RGB hexadecimal string or an empty string if szColor is empty
546 
547     WARNING: Will return 000000 or #000000 if an invalid color is passed in
548 */
549 
UT_colorToHex(const char * szColor,bool bPrefix)550 std::string UT_colorToHex(const char * szColor, bool bPrefix)
551 {
552 	std::string sColor;
553 	UT_return_val_if_fail(szColor && *szColor, sColor);
554 
555 	// This initialization will cause black to be returned if an invalid
556 	// color is passed in. TODO: make UT_parseColor() return a bool to
557 	// make this unnecessary?
558 	UT_RGBColor color(0,0,0);
559 	UT_HashColor hashColor;
560 
561 	UT_parseColor(szColor, color);
562 	sColor = hashColor.setColor(color.m_red, color.m_grn, color.m_blu);
563 
564 	if(!bPrefix)
565 		sColor.erase(0, 1);
566 
567 	return sColor;
568 }
569 
570