~openerp-groupes/openobject-server/6.0-fix-setup-windows

« back to all changes in this revision

Viewing changes to bin/reportlab/lib/colors.py

  • Committer: pinky
  • Date: 2006-12-07 13:41:40 UTC
  • Revision ID: pinky-3f10ee12cea3c4c75cef44ab04ad33ef47432907
New trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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$ '''
 
5
 
 
6
import string, math
 
7
from types import StringType, ListType, TupleType
 
8
from reportlab.lib.utils import fp_str
 
9
_SeqTypes = (ListType,TupleType)
 
10
 
 
11
class Color:
 
12
    """This class is used to represent color.  Components red, green, blue
 
13
    are in the range 0 (dark) to 1 (full intensity)."""
 
14
 
 
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
 
18
 
 
19
    def __repr__(self):
 
20
        return "Color(%s)" % string.replace(fp_str(self.red, self.green, self.blue),' ',',')
 
21
 
 
22
    def __hash__(self):
 
23
        return hash( (self.red, self.green, self.blue) )
 
24
 
 
25
    def __cmp__(self,other):
 
26
        try:
 
27
            dsum = 4*self.red-4*other.red + 2*self.green-2*other.green + self.blue-other.blue
 
28
        except:
 
29
            return -1
 
30
        if dsum > 0: return 1
 
31
        if dsum < 0: return -1
 
32
        return 0
 
33
 
 
34
    def rgb(self):
 
35
        "Returns a three-tuple of components"
 
36
        return (self.red, self.green, self.blue)
 
37
 
 
38
    def bitmap_rgb(self):
 
39
        return tuple(map(lambda x: int(x*255)&255, self.rgb()))
 
40
 
 
41
    def hexval(self):
 
42
        return '0x%02x%02x%02x' % self.bitmap_rgb()
 
43
 
 
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.
 
50
 
 
51
    The RGB approximation is worked out when the object in constructed, so
 
52
    the color attributes should not be changed afterwards.
 
53
 
 
54
    Extra attributes may be attached to the class to support specific ink models,
 
55
    and renderers may look for these."""
 
56
 
 
57
    def __init__(self, cyan=0, magenta=0, yellow=0, black=0,
 
58
                spotName=None, density=1, knockout=None):
 
59
        """
 
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.
 
66
        """
 
67
        self.cyan = cyan
 
68
        self.magenta = magenta
 
69
        self.yellow = yellow
 
70
        self.black = black
 
71
        self.spotName = spotName
 
72
        self.density = max(min(density,1),0)    # force into right range
 
73
        self.knockout = knockout
 
74
 
 
75
        # now work out the RGB approximation. override
 
76
        self.red, self.green, self.blue = cmyk2rgb( (cyan, magenta, yellow, black) )
 
77
 
 
78
        if density<1:
 
79
            #density adjustment of rgb approximants, effectively mix with white
 
80
            r, g, b = self.red, self.green, self.blue
 
81
            r = density*(r-1)+1
 
82
            g = density*(g-1)+1
 
83
            b = density*(b-1)+1
 
84
            self.red, self.green, self.blue = (r,g,b)
 
85
 
 
86
    def __repr__(self):
 
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 ''),
 
92
            )
 
93
 
 
94
    def __hash__(self):
 
95
        return hash( (self.cyan, self.magenta, self.yellow, self.black, self.density, self.spotName) )
 
96
 
 
97
    def __cmp__(self,other):
 
98
        """Partial ordering of colors according to a notion of distance.
 
99
 
 
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
 
109
            try:
 
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
 
112
                return -1
 
113
        if dsum >= 0:
 
114
            return dsum>0
 
115
        else:
 
116
            return -1
 
117
 
 
118
    def cmyk(self):
 
119
        "Returns a tuple of four color components - syntactic sugar"
 
120
        return (self.cyan, self.magenta, self.yellow, self.black)
 
121
 
 
122
    def _density_str(self):
 
123
        return fp_str(self.density)
 
124
 
 
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)
 
129
 
 
130
    def __repr__(self):
 
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 ''),
 
136
            )
 
137
 
 
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)
 
144
    return (r,g,b)
 
145
 
 
146
def rgb2cmyk(r,g,b):
 
147
    '''one way to get cmyk from rgb'''
 
148
    c = 1 - r
 
149
    m = 1 - g
 
150
    y = 1 - b
 
151
    k = min(c,m,y)
 
152
    c = min(1,max(0,c-k))
 
153
    m = min(1,max(0,m-k))
 
154
    y = min(1,max(0,y-k))
 
155
    k = min(1,max(0,k))
 
156
    return (c,m,y,k)
 
157
 
 
158
def color2bw(colorRGB):
 
159
    "Transform an RGB color to a black and white equivalent."
 
160
 
 
161
    col = colorRGB
 
162
    r, g, b = col.red, col.green, col.blue
 
163
    n = (r + g + b) / 3.0
 
164
    bwColorRGB = Color(n, n, n)
 
165
    return bwColorRGB
 
166
 
 
167
def HexColor(val):
 
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).
 
171
 
 
172
    HTML uses a hex string with a preceding hash; if this is present,
 
173
    it is stripped off.  (AR, 3-3-2000)
 
174
 
 
175
    For completeness I assume that #aabbcc or 0xaabbcc are hex numbers
 
176
    otherwise a pure integer is converted as decimal rgb
 
177
    """
 
178
 
 
179
    if type(val) == StringType:
 
180
        b = 10
 
181
        if val[:1] == '#':
 
182
            val = val[1:]
 
183
            b = 16
 
184
        elif string.lower(val[:2]) == '0x':
 
185
            b = 16
 
186
            val = val[2:]
 
187
        val = string.atoi(val,b)
 
188
    return Color(((val>>16)&0xFF)/255.0,((val>>8)&0xFF)/255.0,(val&0xFF)/255.0)
 
189
 
 
190
def linearlyInterpolatedColor(c0, c1, x0, x1, x):
 
191
    """
 
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'.
 
195
    """
 
196
 
 
197
    if c0.__class__ != c1.__class__:
 
198
        raise ValueError, "Color classes must be the same for interpolation!"
 
199
    if x1<x0:
 
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)
 
203
    if x<=x0:
 
204
        return c0
 
205
    elif x>=x1:
 
206
        return c1
 
207
 
 
208
    cname = c0.__class__.__name__
 
209
    dx = float(x1-x0)
 
210
    x = x-x0
 
211
 
 
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
 
216
        return Color(r,g,b)
 
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"
 
228
            c = c0.cyan
 
229
            m = c0.magenta
 
230
            y = c0.yellow
 
231
            k = c0.black
 
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:
 
235
            #special c0 is white
 
236
            c = c1.cyan
 
237
            m = c1.magenta
 
238
            y = c1.yellow
 
239
            k = c1.black
 
240
            d = x*c1.density/dx
 
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:
 
243
            #special c1 is white
 
244
            c = c0.cyan
 
245
            m = c0.magenta
 
246
            y = c0.yellow
 
247
            k = c0.black
 
248
            d = x*c0.density/dx
 
249
            d = c0.density*(1-x/dx)
 
250
            return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, spotName=c0.spotName)
 
251
        else:
 
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)
 
258
    else:
 
259
        raise ValueError, "Can't interpolate: Unknown color class %s!" % cname
 
260
 
 
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)
 
264
 
 
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)
 
269
 
 
270
# Special colors
 
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)
 
279
 
 
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)
 
332
grey = gray
 
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")
 
424
 
 
425
ColorType=type(black)
 
426
 
 
427
    ################################################################
 
428
    #
 
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
 
432
    #
 
433
    #################################################################
 
434
 
 
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
 
438
    names for things."""
 
439
    return math.sqrt(
 
440
            (col1.red - col2.red)**2 +
 
441
            (col1.green - col2.green)**2 +
 
442
            (col1.blue - col2.blue)**2
 
443
            )
 
444
 
 
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
 
448
    names for things."""
 
449
    return math.sqrt(
 
450
            (col1.cyan - col2.cyan)**2 +
 
451
            (col1.magenta - col2.magenta)**2 +
 
452
            (col1.yellow - col2.yellow)**2 +
 
453
            (col1.black - col2.black)**2
 
454
            )
 
455
 
 
456
_namedColors = None
 
457
 
 
458
def getAllNamedColors():
 
459
    #returns a dictionary of all the named ones in the module
 
460
    # uses a singleton for efficiency
 
461
    global _namedColors
 
462
    if _namedColors is not None: return _namedColors
 
463
    import colors
 
464
    _namedColors = {}
 
465
    for (name, value) in colors.__dict__.items():
 
466
        if isinstance(value, Color):
 
467
            _namedColors[name] = value
 
468
 
 
469
    return _namedColors
 
470
 
 
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)
 
476
    '''
 
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)
 
483
    if mode<=1:
 
484
        s = 'best match is %s, distance %0.4f' % (closest[1], closest[0])
 
485
        if mode==0: print s
 
486
        else: return s
 
487
    elif mode==2:
 
488
        return (closest[1], closest[0])
 
489
    else:
 
490
        raise ValueError, "Illegal value for mode "+str(mode)
 
491
 
 
492
def toColor(arg,default=None):
 
493
    '''try to map an arbitrary arg to a color instance'''
 
494
    if isinstance(arg,Color): return arg
 
495
    tArg = type(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]
 
504
        try:
 
505
            return toColor(eval(arg))
 
506
        except:
 
507
            pass
 
508
 
 
509
    try:
 
510
        return HexColor(arg)
 
511
    except:
 
512
        if default is None:
 
513
            raise 'Invalid color value', str(arg)
 
514
        return default
 
515
 
 
516
def toColorOrNone(arg,default=None):
 
517
    '''as above but allows None as a legal value'''
 
518
    if arg is None:
 
519
        return None
 
520
    else:
 
521
        return toColor(arg, default)
 
522
 
 
523
def setColors(**kw):
 
524
    UNDEF = []
 
525
    progress = 1
 
526
    assigned = {}
 
527
    while kw and progress:
 
528
        progress = 0
 
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)
 
533
                ok = UNDEF not in c
 
534
            else:
 
535
                c = toColor(v,UNDEF)
 
536
                ok = c is not UNDEF
 
537
            if ok:
 
538
                assigned[k] = c
 
539
                del kw[k]
 
540
                progress = 1
 
541
 
 
542
    if kw: raise ValueError("Can't convert\n%s" % str(kw))
 
543
    getAllNamedColors()
 
544
    for k, c in assigned.items():
 
545
        globals()[k] = c
 
546
        if isinstance(c,Color): _namedColors[k] = c
 
547
 
 
548
def Whiter(c,f):
 
549
    '''given a color combine with white as c*f w*(1-f) 0<=f<=1'''
 
550
    c = toColor(c)
 
551
    if isinstance(c,PCMYKColor):
 
552
        w = _PCMYK_white
 
553
    elif isinstance(c,CMYKColor): w = _CMYK_white
 
554
    else: w = white
 
555
    return linearlyInterpolatedColor(w, c, 0, 1, f)
 
556
 
 
557
def Blacker(c,f):
 
558
    '''given a color combine with black as c*f+b*(1-f) 0<=f<=1'''
 
559
    c = toColor(c)
 
560
    if isinstance(c,PCMYKColor):
 
561
        b = _PCMYK_black
 
562
    elif isinstance(c,CMYKColor): b = _CMYK_black
 
563
    else: b = black
 
564
    return linearlyInterpolatedColor(b, c, 0, 1, f)