1
#Copyright ReportLab Europe Ltd. 2000-2004
2
#see license.txt for license details
3
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/colors.py
4
__version__=''' $Id$ '''
7
from types import StringType, ListType, TupleType
8
from reportlab.lib.utils import fp_str
9
_SeqTypes = (ListType,TupleType)
12
"""This class is used to represent color. Components red, green, blue
13
are in the range 0 (dark) to 1 (full intensity)."""
15
def __init__(self, red=0, green=0, blue=0):
16
"Initialize with red, green, blue in range [0-1]."
17
self.red, self.green, self.blue = red,green,blue
20
return "Color(%s)" % string.replace(fp_str(self.red, self.green, self.blue),' ',',')
23
return hash( (self.red, self.green, self.blue) )
25
def __cmp__(self,other):
27
dsum = 4*self.red-4*other.red + 2*self.green-2*other.green + self.blue-other.blue
31
if dsum < 0: return -1
35
"Returns a three-tuple of components"
36
return (self.red, self.green, self.blue)
39
return tuple(map(lambda x: int(x*255)&255, self.rgb()))
42
return '0x%02x%02x%02x' % self.bitmap_rgb()
44
class CMYKColor(Color):
45
"""This represents colors using the CMYK (cyan, magenta, yellow, black)
46
model commonly used in professional printing. This is implemented
47
as a derived class so that renderers which only know about RGB "see it"
48
as an RGB color through its 'red','green' and 'blue' attributes, according
49
to an approximate function.
51
The RGB approximation is worked out when the object in constructed, so
52
the color attributes should not be changed afterwards.
54
Extra attributes may be attached to the class to support specific ink models,
55
and renderers may look for these."""
57
def __init__(self, cyan=0, magenta=0, yellow=0, black=0,
58
spotName=None, density=1, knockout=None):
60
Initialize with four colors in range [0-1]. the optional
61
spotName, density & knockout may be of use to specific renderers.
62
spotName is intended for use as an identifier to the renderer not client programs.
63
density is used to modify the overall amount of ink.
64
knockout is a renderer dependent option that determines whether the applied colour
65
knocksout (removes) existing colour; None means use the global default.
68
self.magenta = magenta
71
self.spotName = spotName
72
self.density = max(min(density,1),0) # force into right range
73
self.knockout = knockout
75
# now work out the RGB approximation. override
76
self.red, self.green, self.blue = cmyk2rgb( (cyan, magenta, yellow, black) )
79
#density adjustment of rgb approximants, effectively mix with white
80
r, g, b = self.red, self.green, self.blue
84
self.red, self.green, self.blue = (r,g,b)
87
return "CMYKColor(%s%s%s%s)" % (
88
string.replace(fp_str(self.cyan, self.magenta, self.yellow, self.black),' ',','),
89
(self.spotName and (',spotName='+repr(self.spotName)) or ''),
90
(self.density!=1 and (',density='+fp_str(self.density)) or ''),
91
(self.knockout is not None and (',knockout=%d' % self.knockout) or ''),
95
return hash( (self.cyan, self.magenta, self.yellow, self.black, self.density, self.spotName) )
97
def __cmp__(self,other):
98
"""Partial ordering of colors according to a notion of distance.
100
Comparing across the two color models is of limited use."""
101
# why the try-except? What can go wrong?
102
if isinstance(other, CMYKColor):
103
dsum = (((( (self.cyan-other.cyan)*2 +
104
(self.magenta-other.magenta))*2+
105
(self.yellow-other.yellow))*2+
106
(self.black-other.black))*2+
107
(self.density-other.density))*2 + cmp(self.spotName or '',other.spotName or '')
108
else: # do the RGB comparison
110
dsum = ((self.red-other.red)*2+(self.green-other.green))*2+(self.blue-other.blue)
111
except: # or just return 'not equal' if not a color
119
"Returns a tuple of four color components - syntactic sugar"
120
return (self.cyan, self.magenta, self.yellow, self.black)
122
def _density_str(self):
123
return fp_str(self.density)
125
class PCMYKColor(CMYKColor):
126
'''100 based CMYKColor with density and a spotName; just like Rimas uses'''
127
def __init__(self,cyan,magenta,yellow,black,density=100,spotName=None,knockout=None):
128
CMYKColor.__init__(self,cyan/100.,magenta/100.,yellow/100.,black/100.,spotName,density/100.,knockout=knockout)
131
return "PCMYKColor(%s%s%s%s)" % (
132
string.replace(fp_str(self.cyan*100, self.magenta*100, self.yellow*100, self.black*100),' ',','),
133
(self.spotName and (',spotName='+repr(self.spotName)) or ''),
134
(self.density!=1 and (',density='+fp_str(self.density*100)) or ''),
135
(self.knockout is not None and (',knockout=%d' % self.knockout) or ''),
138
def cmyk2rgb((c,m,y,k),density=1):
139
"Convert from a CMYK color tuple to an RGB color tuple"
140
# From the Adobe Postscript Ref. Manual 2nd ed.
141
r = 1.0 - min(1.0, c + k)
142
g = 1.0 - min(1.0, m + k)
143
b = 1.0 - min(1.0, y + k)
147
'''one way to get cmyk from rgb'''
152
c = min(1,max(0,c-k))
153
m = min(1,max(0,m-k))
154
y = min(1,max(0,y-k))
158
def color2bw(colorRGB):
159
"Transform an RGB color to a black and white equivalent."
162
r, g, b = col.red, col.green, col.blue
163
n = (r + g + b) / 3.0
164
bwColorRGB = Color(n, n, n)
168
"""This function converts a hex string, or an actual integer number,
169
into the corresponding color. E.g., in "AABBCC" or 0xAABBCC,
170
AA is the red, BB is the green, and CC is the blue (00-FF).
172
HTML uses a hex string with a preceding hash; if this is present,
173
it is stripped off. (AR, 3-3-2000)
175
For completeness I assume that #aabbcc or 0xaabbcc are hex numbers
176
otherwise a pure integer is converted as decimal rgb
179
if type(val) == StringType:
184
elif string.lower(val[:2]) == '0x':
187
val = string.atoi(val,b)
188
return Color(((val>>16)&0xFF)/255.0,((val>>8)&0xFF)/255.0,(val&0xFF)/255.0)
190
def linearlyInterpolatedColor(c0, c1, x0, x1, x):
192
Linearly interpolates colors. Can handle RGB, CMYK and PCMYK
193
colors - give ValueError if colours aren't the same.
194
Doesn't currently handle 'Spot Color Interpolation'.
197
if c0.__class__ != c1.__class__:
198
raise ValueError, "Color classes must be the same for interpolation!"
200
x0,x1,c0,c1 = x1,x0,c1,c0 # normalized so x1>x0
201
if x<x0-1e-8 or x>x1+1e-8: # fudge factor for numerical problems
202
raise ValueError, "Can't interpolate: x=%f is not between %f and %f!" % (x,x0,x1)
208
cname = c0.__class__.__name__
212
if cname == 'Color': # RGB
213
r = c0.red+x*(c1.red - c0.red)/dx
214
g = c0.green+x*(c1.green- c0.green)/dx
215
b = c0.blue+x*(c1.blue - c0.blue)/dx
217
elif cname == 'CMYKColor':
218
c = c0.cyan+x*(c1.cyan - c0.cyan)/dx
219
m = c0.magenta+x*(c1.magenta - c0.magenta)/dx
220
y = c0.yellow+x*(c1.yellow - c0.yellow)/dx
221
k = c0.black+x*(c1.black - c0.black)/dx
222
d = c0.density+x*(c1.density - c0.density)/dx
223
return CMYKColor(c,m,y,k, density=d)
224
elif cname == 'PCMYKColor':
225
if cmykDistance(c0,c1)<1e-8:
226
#colors same do density and preserve spotName if any
227
assert c0.spotName == c1.spotName, "Identical cmyk, but different spotName"
232
d = c0.density+x*(c1.density - c0.density)/dx
233
return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, spotName=c0.spotName)
234
elif cmykDistance(c0,_CMYK_white)<1e-8:
241
return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, spotName=c1.spotName)
242
elif cmykDistance(c1,_CMYK_white)<1e-8:
249
d = c0.density*(1-x/dx)
250
return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, spotName=c0.spotName)
252
c = c0.cyan+x*(c1.cyan - c0.cyan)/dx
253
m = c0.magenta+x*(c1.magenta - c0.magenta)/dx
254
y = c0.yellow+x*(c1.yellow - c0.yellow)/dx
255
k = c0.black+x*(c1.black - c0.black)/dx
256
d = c0.density+x*(c1.density - c0.density)/dx
257
return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100)
259
raise ValueError, "Can't interpolate: Unknown color class %s!" % cname
261
# special case -- indicates no drawing should be done
262
# this is a hangover from PIDDLE - suggest we ditch it since it is not used anywhere
263
#transparent = Color(-1, -1, -1)
265
_CMYK_white=CMYKColor(0,0,0,0)
266
_PCMYK_white=PCMYKColor(0,0,0,0)
267
_CMYK_black=CMYKColor(0,0,0,1)
268
_PCMYK_black=PCMYKColor(0,0,0,100)
271
ReportLabBlueOLD = HexColor(0x4e5688)
272
ReportLabBlue = HexColor(0x00337f)
273
ReportLabBluePCMYK = PCMYKColor(100,65,0,30,spotName='Pantone 288U')
274
ReportLabLightBlue = HexColor(0xb7b9d3)
275
ReportLabFidBlue=HexColor(0x3366cc)
276
ReportLabFidRed=HexColor(0xcc0033)
277
ReportLabGreen = HexColor(0x336600)
278
ReportLabLightGreen = HexColor(0x339933)
280
# color constants -- mostly from HTML standard
281
aliceblue = HexColor(0xF0F8FF)
282
antiquewhite = HexColor(0xFAEBD7)
283
aqua = HexColor(0x00FFFF)
284
aquamarine = HexColor(0x7FFFD4)
285
azure = HexColor(0xF0FFFF)
286
beige = HexColor(0xF5F5DC)
287
bisque = HexColor(0xFFE4C4)
288
black = HexColor(0x000000)
289
blanchedalmond = HexColor(0xFFEBCD)
290
blue = HexColor(0x0000FF)
291
blueviolet = HexColor(0x8A2BE2)
292
brown = HexColor(0xA52A2A)
293
burlywood = HexColor(0xDEB887)
294
cadetblue = HexColor(0x5F9EA0)
295
chartreuse = HexColor(0x7FFF00)
296
chocolate = HexColor(0xD2691E)
297
coral = HexColor(0xFF7F50)
298
cornflowerblue = cornflower = HexColor(0x6495ED)
299
cornsilk = HexColor(0xFFF8DC)
300
crimson = HexColor(0xDC143C)
301
cyan = HexColor(0x00FFFF)
302
darkblue = HexColor(0x00008B)
303
darkcyan = HexColor(0x008B8B)
304
darkgoldenrod = HexColor(0xB8860B)
305
darkgray = HexColor(0xA9A9A9)
306
darkgreen = HexColor(0x006400)
307
darkkhaki = HexColor(0xBDB76B)
308
darkmagenta = HexColor(0x8B008B)
309
darkolivegreen = HexColor(0x556B2F)
310
darkorange = HexColor(0xFF8C00)
311
darkorchid = HexColor(0x9932CC)
312
darkred = HexColor(0x8B0000)
313
darksalmon = HexColor(0xE9967A)
314
darkseagreen = HexColor(0x8FBC8B)
315
darkslateblue = HexColor(0x483D8B)
316
darkslategray = HexColor(0x2F4F4F)
317
darkturquoise = HexColor(0x00CED1)
318
darkviolet = HexColor(0x9400D3)
319
deeppink = HexColor(0xFF1493)
320
deepskyblue = HexColor(0x00BFFF)
321
dimgray = HexColor(0x696969)
322
dodgerblue = HexColor(0x1E90FF)
323
firebrick = HexColor(0xB22222)
324
floralwhite = HexColor(0xFFFAF0)
325
forestgreen = HexColor(0x228B22)
326
fuchsia = HexColor(0xFF00FF)
327
gainsboro = HexColor(0xDCDCDC)
328
ghostwhite = HexColor(0xF8F8FF)
329
gold = HexColor(0xFFD700)
330
goldenrod = HexColor(0xDAA520)
331
gray = HexColor(0x808080)
333
green = HexColor(0x008000)
334
greenyellow = HexColor(0xADFF2F)
335
honeydew = HexColor(0xF0FFF0)
336
hotpink = HexColor(0xFF69B4)
337
indianred = HexColor(0xCD5C5C)
338
indigo = HexColor(0x4B0082)
339
ivory = HexColor(0xFFFFF0)
340
khaki = HexColor(0xF0E68C)
341
lavender = HexColor(0xE6E6FA)
342
lavenderblush = HexColor(0xFFF0F5)
343
lawngreen = HexColor(0x7CFC00)
344
lemonchiffon = HexColor(0xFFFACD)
345
lightblue = HexColor(0xADD8E6)
346
lightcoral = HexColor(0xF08080)
347
lightcyan = HexColor(0xE0FFFF)
348
lightgoldenrodyellow = HexColor(0xFAFAD2)
349
lightgreen = HexColor(0x90EE90)
350
lightgrey = HexColor(0xD3D3D3)
351
lightpink = HexColor(0xFFB6C1)
352
lightsalmon = HexColor(0xFFA07A)
353
lightseagreen = HexColor(0x20B2AA)
354
lightskyblue = HexColor(0x87CEFA)
355
lightslategray = HexColor(0x778899)
356
lightsteelblue = HexColor(0xB0C4DE)
357
lightyellow = HexColor(0xFFFFE0)
358
lime = HexColor(0x00FF00)
359
limegreen = HexColor(0x32CD32)
360
linen = HexColor(0xFAF0E6)
361
magenta = HexColor(0xFF00FF)
362
maroon = HexColor(0x800000)
363
mediumaquamarine = HexColor(0x66CDAA)
364
mediumblue = HexColor(0x0000CD)
365
mediumorchid = HexColor(0xBA55D3)
366
mediumpurple = HexColor(0x9370DB)
367
mediumseagreen = HexColor(0x3CB371)
368
mediumslateblue = HexColor(0x7B68EE)
369
mediumspringgreen = HexColor(0x00FA9A)
370
mediumturquoise = HexColor(0x48D1CC)
371
mediumvioletred = HexColor(0xC71585)
372
midnightblue = HexColor(0x191970)
373
mintcream = HexColor(0xF5FFFA)
374
mistyrose = HexColor(0xFFE4E1)
375
moccasin = HexColor(0xFFE4B5)
376
navajowhite = HexColor(0xFFDEAD)
377
navy = HexColor(0x000080)
378
oldlace = HexColor(0xFDF5E6)
379
olive = HexColor(0x808000)
380
olivedrab = HexColor(0x6B8E23)
381
orange = HexColor(0xFFA500)
382
orangered = HexColor(0xFF4500)
383
orchid = HexColor(0xDA70D6)
384
palegoldenrod = HexColor(0xEEE8AA)
385
palegreen = HexColor(0x98FB98)
386
paleturquoise = HexColor(0xAFEEEE)
387
palevioletred = HexColor(0xDB7093)
388
papayawhip = HexColor(0xFFEFD5)
389
peachpuff = HexColor(0xFFDAB9)
390
peru = HexColor(0xCD853F)
391
pink = HexColor(0xFFC0CB)
392
plum = HexColor(0xDDA0DD)
393
powderblue = HexColor(0xB0E0E6)
394
purple = HexColor(0x800080)
395
red = HexColor(0xFF0000)
396
rosybrown = HexColor(0xBC8F8F)
397
royalblue = HexColor(0x4169E1)
398
saddlebrown = HexColor(0x8B4513)
399
salmon = HexColor(0xFA8072)
400
sandybrown = HexColor(0xF4A460)
401
seagreen = HexColor(0x2E8B57)
402
seashell = HexColor(0xFFF5EE)
403
sienna = HexColor(0xA0522D)
404
silver = HexColor(0xC0C0C0)
405
skyblue = HexColor(0x87CEEB)
406
slateblue = HexColor(0x6A5ACD)
407
slategray = HexColor(0x708090)
408
snow = HexColor(0xFFFAFA)
409
springgreen = HexColor(0x00FF7F)
410
steelblue = HexColor(0x4682B4)
411
tan = HexColor(0xD2B48C)
412
teal = HexColor(0x008080)
413
thistle = HexColor(0xD8BFD8)
414
tomato = HexColor(0xFF6347)
415
turquoise = HexColor(0x40E0D0)
416
violet = HexColor(0xEE82EE)
417
wheat = HexColor(0xF5DEB3)
418
white = HexColor(0xFFFFFF)
419
whitesmoke = HexColor(0xF5F5F5)
420
yellow = HexColor(0xFFFF00)
421
yellowgreen = HexColor(0x9ACD32)
422
fidblue=HexColor(0x3366cc)
423
fidlightblue=HexColor("#d6e0f5")
425
ColorType=type(black)
427
################################################################
429
# Helper functions for dealing with colors. These tell you
430
# which are predefined, so you can print color charts;
431
# and can give the nearest match to an arbitrary color object
433
#################################################################
435
def colorDistance(col1, col2):
436
"""Returns a number between 0 and root(3) stating how similar
437
two colours are - distance in r,g,b, space. Only used to find
440
(col1.red - col2.red)**2 +
441
(col1.green - col2.green)**2 +
442
(col1.blue - col2.blue)**2
445
def cmykDistance(col1, col2):
446
"""Returns a number between 0 and root(4) stating how similar
447
two colours are - distance in r,g,b, space. Only used to find
450
(col1.cyan - col2.cyan)**2 +
451
(col1.magenta - col2.magenta)**2 +
452
(col1.yellow - col2.yellow)**2 +
453
(col1.black - col2.black)**2
458
def getAllNamedColors():
459
#returns a dictionary of all the named ones in the module
460
# uses a singleton for efficiency
462
if _namedColors is not None: return _namedColors
465
for (name, value) in colors.__dict__.items():
466
if isinstance(value, Color):
467
_namedColors[name] = value
471
def describe(aColor,mode=0):
472
'''finds nearest colour match to aColor.
473
mode=0 print a string desription
474
mode=1 return a string description
475
mode=2 return (distance, colorName)
477
namedColors = getAllNamedColors()
478
closest = (10, None, None) #big number, name, color
479
for (name, color) in namedColors.items():
480
distance = colorDistance(aColor, color)
481
if distance < closest[0]:
482
closest = (distance, name, color)
484
s = 'best match is %s, distance %0.4f' % (closest[1], closest[0])
488
return (closest[1], closest[0])
490
raise ValueError, "Illegal value for mode "+str(mode)
492
def toColor(arg,default=None):
493
'''try to map an arbitrary arg to a color instance'''
494
if isinstance(arg,Color): return arg
496
if tArg in _SeqTypes:
497
assert 3<=len(arg)<=4, 'Can only convert 3 and 4 sequences to color'
498
assert 0<=min(arg) and max(arg)<=1
499
return len(arg)==3 and Color(arg[0],arg[1],arg[2]) or CMYKColor(arg[0],arg[1],arg[2],arg[3])
500
elif tArg == StringType:
501
C = getAllNamedColors()
502
s = string.lower(arg)
503
if C.has_key(s): return C[s]
505
return toColor(eval(arg))
513
raise 'Invalid color value', str(arg)
516
def toColorOrNone(arg,default=None):
517
'''as above but allows None as a legal value'''
521
return toColor(arg, default)
527
while kw and progress:
529
for k, v in kw.items():
530
if type(v) in (type(()),type([])):
531
c = map(lambda x,UNDEF=UNDEF: toColor(x,UNDEF),v)
532
if type(v) is type(()): c = tuple(c)
542
if kw: raise ValueError("Can't convert\n%s" % str(kw))
544
for k, c in assigned.items():
546
if isinstance(c,Color): _namedColors[k] = c
549
'''given a color combine with white as c*f w*(1-f) 0<=f<=1'''
551
if isinstance(c,PCMYKColor):
553
elif isinstance(c,CMYKColor): w = _CMYK_white
555
return linearlyInterpolatedColor(w, c, 0, 1, f)
558
'''given a color combine with black as c*f+b*(1-f) 0<=f<=1'''
560
if isinstance(c,PCMYKColor):
562
elif isinstance(c,CMYKColor): b = _CMYK_black
564
return linearlyInterpolatedColor(b, c, 0, 1, f)