add support for virtual fonts in virtual fonts
[PyX.git] / pyx / color.py
blobe9e17ace194ff0371ae4a359e3ec65aaf929b16d
1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2002-2004, 2006, 2013 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2003-2006 Michael Schindler <m-schindler@users.sourceforge.net>
6 # Copyright (C) 2002-2011 André Wobst <wobsta@users.sourceforge.net>
8 # This file is part of PyX (http://pyx.sourceforge.net/).
10 # PyX is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # PyX is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with PyX; if not, write to the Free Software
22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 import binascii, colorsys, logging, math, struct
25 from . import attr, style, pdfwriter
27 logger = logging.getLogger("pyx")
29 # device-dependent (nonlinear) functions for color conversion
30 # UCRx : [0,1] -> [-1, 1] UnderColorRemoval (removes black from c, y, m)
31 # BG : [0,1] -> [0, 1] BlackGeneration (generate the black from the nominal k-value)
32 # as long as we have no further knowledge we define them linearly with constants 1
33 def _UCRc(x): return x
34 def _UCRm(x): return x
35 def _UCRy(x): return x
36 def _BG(x): return x
38 def set(UCRc=None, UCRm=None, UCRy=None, BG=None):
39 global _UCRc
40 global _UCRm
41 global _UCRy
42 global _BG
44 if UCRc is not None:
45 _UCRc = UCRc
46 if UCRm is not None:
47 _UCRm = UCRm
48 if UCRy is not None:
49 _UCRy = UCRy
50 if BG is not None:
51 _BG = BG
54 class color(attr.exclusiveattr, style.strokestyle, style.fillstyle):
55 """base class for all colors"""
56 def __init__(self):
57 super().__init__(color)
59 def processSVGattrs(self, attrs, writer, context, registry):
60 if context.strokeattr:
61 context.strokecolor = self.rgb().tohexstring()
62 if context.fillattr:
63 context.fillcolor = self.rgb().tohexstring()
66 clear = attr.clearclass(color)
69 class gray(color):
71 """grey tones"""
73 def __init__(self, g=0.0):
74 super().__init__()
75 if g<0 or g>1:
76 raise ValueError("Value g out of range [0,1]")
77 self.g = g
79 def processPS(self, file, writer, context, registry):
80 file.write("%f setgray\n" % self.g)
82 def processPDF(self, file, writer, context, registry):
83 if context.strokeattr:
84 file.write("%f G\n" % self.g)
85 if context.fillattr:
86 file.write("%f g\n" % self.g)
88 def cmyk(self):
89 return cmyk(0, 0, 0, 1 - self.g)
91 def gray(self):
92 return gray(self.g)
93 grey = gray
95 def hsb(self):
96 return hsb(0, 0, self.g)
98 def rgb(self):
99 return rgb(self.g, self.g, self.g)
101 def colorspacestring(self):
102 return "/DeviceGray"
104 def to8bitbytes(self):
105 return bytes((int(self.g*255),))
107 gray.black = gray(0.0)
108 gray.white = gray(1.0)
109 grey = gray
112 class rgb(color):
114 """rgb colors"""
116 def __init__(self, r=0.0, g=0.0, b=0.0):
117 super().__init__()
118 if r<0 or r>1:
119 raise ValueError("Value r out of range [0,1]")
120 if g<0 or g>1:
121 raise ValueError("Value g out of range [0,1]")
122 if b<0 or b>1:
123 raise ValueError("Value b out of range [0,1]")
124 self.r = r
125 self.g = g
126 self.b = b
128 def processPS(self, file, writer, context, registry):
129 file.write("%f %f %f setrgbcolor\n" % (self.r, self.g, self.b))
131 def processPDF(self, file, writer, context, registry):
132 if context.strokeattr:
133 file.write("%f %f %f RG\n" % (self.r, self.g, self.b))
134 if context.fillattr:
135 file.write("%f %f %f rg\n" % (self.r, self.g, self.b))
137 def cmyk(self):
138 # conversion to cmy
139 c, m, y = 1 - self.r, 1 - self.g, 1 - self.b
140 # conversion from cmy to cmyk with device-dependent functions
141 k = min([c, m, y])
142 return cmyk(min(1, max(0, c - _UCRc(k))),
143 min(1, max(0, m - _UCRm(k))),
144 min(1, max(0, y - _UCRy(k))),
145 _BG(k))
147 def gray(self):
148 return gray(0.3*self.r + 0.59*self.g + 0.11*self.b)
149 grey = gray
151 def hsb(self):
152 h, s, b = colorsys.rgb_to_hsv(self.r, self.g, self.b)
153 return hsb(h, s, b)
155 def rgb(self):
156 return rgb(self.r, self.g, self.b)
158 def colorspacestring(self):
159 return "/DeviceRGB"
161 def to8bitbytes(self):
162 return struct.pack("BBB", int(self.r*255), int(self.g*255), int(self.b*255))
164 def tohexstring(self, cssstrip=1, addhash=1):
165 hexstring = binascii.b2a_hex(self.to8bitbytes()).decode('ascii')
166 if cssstrip and hexstring[0] == hexstring[1] and hexstring[2] == hexstring[3] and hexstring[4] == hexstring[5]:
167 hexstring = "".join([hexstring[0], hexstring[2], hexstring[4]])
168 if addhash:
169 hexstring = "#" + hexstring
170 return hexstring
173 def rgbfromhexstring(hexstring):
174 hexstring = hexstring.strip().lstrip("#")
175 if len(hexstring) == 3:
176 hexstring = "".join([hexstring[0], hexstring[0], hexstring[1], hexstring[1], hexstring[2], hexstring[2]])
177 elif len(hexstring) != 6:
178 raise ValueError("3 or 6 digit hex number expected (with optional leading hash character)")
179 return rgb(*[value/255.0 for value in struct.unpack("BBB", binascii.a2b_hex(hexstring))])
181 rgb.red = rgb(1, 0, 0)
182 rgb.green = rgb(0, 1, 0)
183 rgb.blue = rgb(0, 0, 1)
184 rgb.white = rgb(1, 1, 1)
185 rgb.black = rgb(0, 0, 0)
188 class hsb(color):
190 """hsb colors"""
192 def __init__(self, h=0.0, s=0.0, b=0.0):
193 super().__init__()
194 if h<0 or h>1:
195 raise ValueError("Value h out of range [0,1]")
196 if s<0 or s>1:
197 raise ValueError("Value s out of range [0,1]")
198 if b<0 or b>1:
199 raise ValueError("Value b out of range [0,1]")
200 self.h = h
201 self.s = s
202 self.b = b
204 def processPS(self, file, writer, context, registry):
205 file.write("%f %f %f sethsbcolor\n" % (self.h, self.s, self.b))
207 def processPDF(self, file, writer, context, registry):
208 self.rgb().processPDF(file, writer, context, registry)
210 def cmyk(self):
211 return self.rgb().cmyk()
213 def gray(self):
214 return self.rgb().gray()
215 grey = gray
217 def hsb(self):
218 return hsb(self.h, self.s, self.b)
220 def rgb(self):
221 r, g, b = colorsys.hsv_to_rgb(self.h, self.s, self.b)
222 return rgb(r, g, b)
224 def colorspacestring(self):
225 raise RuntimeError("colorspace string not available for hsb colors")
228 class cmyk(color):
230 """cmyk colors"""
232 def __init__(self, c=0.0, m=0.0, y=0.0, k=0.0):
233 super().__init__()
234 if c<0 or c>1:
235 raise ValueError("Value c out of range [0,1]")
236 if m<0 or m>1:
237 raise ValueError("Value m out of range [0,1]")
238 if y<0 or y>1:
239 raise ValueError("Value y out of range [0,1]")
240 if k<0 or k>1:
241 raise ValueError("Value k out of range [0,1]")
242 self.c = c
243 self.m = m
244 self.y = y
245 self.k = k
247 def processPS(self, file, writer, context, registry):
248 file.write("%f %f %f %f setcmykcolor\n" % (self.c, self.m, self.y, self.k))
250 def processPDF(self, file, writer, context, registry):
251 if context.strokeattr:
252 file.write("%f %f %f %f K\n" % (self.c, self.m, self.y, self.k))
253 if context.fillattr:
254 file.write("%f %f %f %f k\n" % (self.c, self.m, self.y, self.k))
256 def cmyk(self):
257 return cmyk(self.c, self.m, self.y, self.k)
259 def gray(self):
260 return gray(1 - min([1, 0.3*self.c + 0.59*self.m + 0.11*self.y + self.k]))
261 grey = gray
263 def hsb(self):
264 return self.rgb().hsb()
266 def rgb(self):
267 # conversion to cmy:
268 c = min(1, self.c + self.k)
269 m = min(1, self.m + self.k)
270 y = min(1, self.y + self.k)
271 # conversion from cmy to rgb:
272 return rgb(1 - c, 1 - m, 1 - y)
274 def colorspacestring(self):
275 return "/DeviceCMYK"
277 def to8bitbytes(self):
278 return struct.pack("BBBB", int(self.c*255), int(self.m*255), int(self.y*255), int(self.k*255))
280 cmyk.GreenYellow = cmyk(0.15, 0, 0.69, 0)
281 cmyk.Yellow = cmyk(0, 0, 1, 0)
282 cmyk.Goldenrod = cmyk(0, 0.10, 0.84, 0)
283 cmyk.Dandelion = cmyk(0, 0.29, 0.84, 0)
284 cmyk.Apricot = cmyk(0, 0.32, 0.52, 0)
285 cmyk.Peach = cmyk(0, 0.50, 0.70, 0)
286 cmyk.Melon = cmyk(0, 0.46, 0.50, 0)
287 cmyk.YellowOrange = cmyk(0, 0.42, 1, 0)
288 cmyk.Orange = cmyk(0, 0.61, 0.87, 0)
289 cmyk.BurntOrange = cmyk(0, 0.51, 1, 0)
290 cmyk.Bittersweet = cmyk(0, 0.75, 1, 0.24)
291 cmyk.RedOrange = cmyk(0, 0.77, 0.87, 0)
292 cmyk.Mahogany = cmyk(0, 0.85, 0.87, 0.35)
293 cmyk.Maroon = cmyk(0, 0.87, 0.68, 0.32)
294 cmyk.BrickRed = cmyk(0, 0.89, 0.94, 0.28)
295 cmyk.Red = cmyk(0, 1, 1, 0)
296 cmyk.OrangeRed = cmyk(0, 1, 0.50, 0)
297 cmyk.RubineRed = cmyk(0, 1, 0.13, 0)
298 cmyk.WildStrawberry = cmyk(0, 0.96, 0.39, 0)
299 cmyk.Salmon = cmyk(0, 0.53, 0.38, 0)
300 cmyk.CarnationPink = cmyk(0, 0.63, 0, 0)
301 cmyk.Magenta = cmyk(0, 1, 0, 0)
302 cmyk.VioletRed = cmyk(0, 0.81, 0, 0)
303 cmyk.Rhodamine = cmyk(0, 0.82, 0, 0)
304 cmyk.Mulberry = cmyk(0.34, 0.90, 0, 0.02)
305 cmyk.RedViolet = cmyk(0.07, 0.90, 0, 0.34)
306 cmyk.Fuchsia = cmyk(0.47, 0.91, 0, 0.08)
307 cmyk.Lavender = cmyk(0, 0.48, 0, 0)
308 cmyk.Thistle = cmyk(0.12, 0.59, 0, 0)
309 cmyk.Orchid = cmyk(0.32, 0.64, 0, 0)
310 cmyk.DarkOrchid = cmyk(0.40, 0.80, 0.20, 0)
311 cmyk.Purple = cmyk(0.45, 0.86, 0, 0)
312 cmyk.Plum = cmyk(0.50, 1, 0, 0)
313 cmyk.Violet = cmyk(0.79, 0.88, 0, 0)
314 cmyk.RoyalPurple = cmyk(0.75, 0.90, 0, 0)
315 cmyk.BlueViolet = cmyk(0.86, 0.91, 0, 0.04)
316 cmyk.Periwinkle = cmyk(0.57, 0.55, 0, 0)
317 cmyk.CadetBlue = cmyk(0.62, 0.57, 0.23, 0)
318 cmyk.CornflowerBlue = cmyk(0.65, 0.13, 0, 0)
319 cmyk.MidnightBlue = cmyk(0.98, 0.13, 0, 0.43)
320 cmyk.NavyBlue = cmyk(0.94, 0.54, 0, 0)
321 cmyk.RoyalBlue = cmyk(1, 0.50, 0, 0)
322 cmyk.Blue = cmyk(1, 1, 0, 0)
323 cmyk.Cerulean = cmyk(0.94, 0.11, 0, 0)
324 cmyk.Cyan = cmyk(1, 0, 0, 0)
325 cmyk.ProcessBlue = cmyk(0.96, 0, 0, 0)
326 cmyk.SkyBlue = cmyk(0.62, 0, 0.12, 0)
327 cmyk.Turquoise = cmyk(0.85, 0, 0.20, 0)
328 cmyk.TealBlue = cmyk(0.86, 0, 0.34, 0.02)
329 cmyk.Aquamarine = cmyk(0.82, 0, 0.30, 0)
330 cmyk.BlueGreen = cmyk(0.85, 0, 0.33, 0)
331 cmyk.Emerald = cmyk(1, 0, 0.50, 0)
332 cmyk.JungleGreen = cmyk(0.99, 0, 0.52, 0)
333 cmyk.SeaGreen = cmyk(0.69, 0, 0.50, 0)
334 cmyk.Green = cmyk(1, 0, 1, 0)
335 cmyk.ForestGreen = cmyk(0.91, 0, 0.88, 0.12)
336 cmyk.PineGreen = cmyk(0.92, 0, 0.59, 0.25)
337 cmyk.LimeGreen = cmyk(0.50, 0, 1, 0)
338 cmyk.YellowGreen = cmyk(0.44, 0, 0.74, 0)
339 cmyk.SpringGreen = cmyk(0.26, 0, 0.76, 0)
340 cmyk.OliveGreen = cmyk(0.64, 0, 0.95, 0.40)
341 cmyk.RawSienna = cmyk(0, 0.72, 1, 0.45)
342 cmyk.Sepia = cmyk(0, 0.83, 1, 0.70)
343 cmyk.Brown = cmyk(0, 0.81, 1, 0.60)
344 cmyk.Tan = cmyk(0.14, 0.42, 0.56, 0)
345 cmyk.Gray = cmyk(0, 0, 0, 0.50)
346 cmyk.Grey = cmyk.Gray
347 cmyk.Black = cmyk(0, 0, 0, 1)
348 cmyk.White = cmyk(0, 0, 0, 0)
349 cmyk.white = cmyk.White
350 cmyk.black = cmyk.Black
353 class palette(attr.changelist):
354 """color palettes
356 A color palette is a discrete, ordered list of colors"""
358 palette.clear = attr.clearclass(palette)
361 # gradients
364 class gradient(attr.changeattr):
366 """base class for color gradients
368 A gradient is a continuous collection of colors with a single parameter ranging from 0 to 1
369 to address them"""
371 def getcolor(self, param):
372 """return color corresponding to param"""
373 pass
375 def select(self, index, n_indices):
376 """return a color corresponding to an index out of n_indices"""
377 if n_indices == 1:
378 param = 0
379 else:
380 param = index / (n_indices - 1.0)
381 return self.getcolor(param)
383 gradient.clear = attr.clearclass(gradient)
386 # gradient with arbitrary non-linear dependency
389 class functiongradient_gray(gradient):
391 """arbitrary non-linear gradients of gray colors
393 f_gray: a function mapping [0,1] to the gray value
396 def __init__(self, f_gray):
397 super().__init__()
398 self.f_gray = f_gray
400 def getcolor(self, param):
401 return gray(self.f_gray(param))
404 class functiongradient_cmyk(gradient):
406 """arbitrary non-linear gradients of cmyk colors
408 f_c: a function mapping [0,1] to the c component
409 f_m: a function mapping [0,1] to the m component
410 f_y: a function mapping [0,1] to the y component
411 f_k: a function mapping [0,1] to the k component
414 def __init__(self, f_c, f_m, f_y, f_k):
415 super().__init__()
416 self.f_c = f_c
417 self.f_m = f_m
418 self.f_y = f_y
419 self.f_k = f_k
421 def getcolor(self, param):
422 return cmyk(self.f_c(param), self.f_m(param), self.f_y(param), self.f_k(param))
425 class functiongradient_hsb(gradient):
427 """arbitrary non-linear gradients of hsb colors
429 f_h: a function mapping [0,1] to the h component
430 f_s: a function mapping [0,1] to the s component
431 f_b: a function mapping [0,1] to the b component
434 def __init__(self, f_h, f_s, f_b):
435 super().__init__()
436 self.f_h = f_h
437 self.f_s = f_s
438 self.f_b = f_b
440 def getcolor(self, param):
441 return hsb(self.f_h(param), self.f_s(param), self.f_b(param))
444 class functiongradient_rgb(gradient):
446 """arbitrary non-linear gradients of rgb colors
448 f_r: a function mapping [0,1] to the r component
449 f_g: a function mapping [0,1] to the b component
450 f_b: a function mapping [0,1] to the b component
453 def __init__(self, f_r, f_g, f_b):
454 super().__init__()
455 self.f_r = f_r
456 self.f_g = f_g
457 self.f_b = f_b
459 def getcolor(self, param):
460 return rgb(self.f_r(param), self.f_g(param), self.f_b(param))
463 # factory functions for gradients interpolating linearly between two colors
466 def lineargradient_cmyk(mincolor, maxcolor):
467 return functiongradient_cmyk(lambda x:maxcolor.c * x + mincolor.c * (1-x),
468 lambda x:maxcolor.m * x + mincolor.m * (1-x),
469 lambda x:maxcolor.y * x + mincolor.y * (1-x),
470 lambda x:maxcolor.k * x + mincolor.k * (1-x))
472 def lineargradient_gray(mincolor, maxcolor):
473 return functiongradient_gray(lambda x:maxcolor.g * x + mincolor.g * (1-x))
475 def lineargradient_hsb(mincolor, maxcolor):
476 return functiongradient_hsb(lambda x:maxcolor.h * x + mincolor.h * (1-x),
477 lambda x:maxcolor.s * x + mincolor.s * (1-x),
478 lambda x:maxcolor.b * x + mincolor.b * (1-x))
480 def lineargradient_rgb(mincolor, maxcolor):
481 return functiongradient_rgb(lambda x:maxcolor.r * x + mincolor.r * (1-x),
482 lambda x:maxcolor.g * x + mincolor.g * (1-x),
483 lambda x:maxcolor.b * x + mincolor.b * (1-x))
487 # gradients converted into other color spaces
490 class rgbgradient(gradient):
492 "a gradient, which takes another gradient and returns rgb colors"
494 def __init__(self, gradient):
495 super().__init__()
496 self.gradient = gradient
498 def getcolor(self, param):
499 return self.gradient.getcolor(param).rgb()
502 class cmykgradient(gradient):
504 "a gradient, which takes another gradient and returns cmyk colors"
506 def __init__(self, gradient):
507 super().__init__()
508 self.gradient = gradient
510 def getcolor(self, param):
511 return self.gradient.getcolor(param).cmyk()
514 gradient.Gray = lineargradient_gray(gray.white, gray.black)
515 gradient.Grey = gradient.Gray
516 gradient.ReverseGray = lineargradient_gray(gray.black, gray.white)
517 gradient.ReverseGrey = gradient.ReverseGray
518 gradient.BlackYellow = functiongradient_rgb( # compare this with reversegray above
519 f_r=lambda x: 2*x*(1-x)**5 + 3.5*x**2*(1-x)**3 + 2.1*x*x*(1-x)**2 + 3.0*x**3*(1-x)**2 + x**0.5*(1-(1-x)**2),
520 f_g=lambda x: 1.5*x**2*(1-x)**3 - 0.8*x**3*(1-x)**2 + 2.0*x**4*(1-x) + x**4,
521 f_b=lambda x: 5*x*(1-x)**5 - 0.5*x**2*(1-x)**3 + 0.3*x*x*(1-x)**2 + 5*x**3*(1-x)**2 + 0.5*x**6)
522 gradient.YellowBlack = functiongradient_rgb(
523 f_r=lambda x: 2*(1-x)*x**5 + 3.5*(1-x)**2*x**3 + 2.1*(1-x)*(1-x)*x**2 + 3.0*(1-x)**3*x**2 + (1-x)**0.5*(1-x**2),
524 f_g=lambda x: 1.5*(1-x)**2*x**3 - 0.8*(1-x)**3*x**2 + 2.0*(1-x)**4*x + (1-x)**4,
525 f_b=lambda x: 5*(1-x)*x**5 - 0.5*(1-x)**2*x**3 + 0.3*(1-x)*(1-x)*x**2 + 5*(1-x)**3*x**2 + 0.5*(1-x)**6)
526 gradient.RedGreen = lineargradient_rgb(rgb.red, rgb.green)
527 gradient.RedBlue = lineargradient_rgb(rgb.red, rgb.blue)
528 gradient.GreenRed = lineargradient_rgb(rgb.green, rgb.red)
529 gradient.GreenBlue = lineargradient_rgb(rgb.green, rgb.blue)
530 gradient.BlueRed = lineargradient_rgb(rgb.blue, rgb.red)
531 gradient.BlueGreen = lineargradient_rgb(rgb.blue, rgb.green)
532 gradient.RedBlack = lineargradient_rgb(rgb.red, rgb.black)
533 gradient.BlackRed = lineargradient_rgb(rgb.black, rgb.red)
534 gradient.RedWhite = lineargradient_rgb(rgb.red, rgb.white)
535 gradient.WhiteRed = lineargradient_rgb(rgb.white, rgb.red)
536 gradient.GreenBlack = lineargradient_rgb(rgb.green, rgb.black)
537 gradient.BlackGreen = lineargradient_rgb(rgb.black, rgb.green)
538 gradient.GreenWhite = lineargradient_rgb(rgb.green, rgb.white)
539 gradient.WhiteGreen = lineargradient_rgb(rgb.white, rgb.green)
540 gradient.BlueBlack = lineargradient_rgb(rgb.blue, rgb.black)
541 gradient.BlackBlue = lineargradient_rgb(rgb.black, rgb.blue)
542 gradient.BlueWhite = lineargradient_rgb(rgb.blue, rgb.white)
543 gradient.WhiteBlue = lineargradient_rgb(rgb.white, rgb.blue)
544 gradient.Rainbow = lineargradient_hsb(hsb(0, 1, 1), hsb(2.0/3.0, 1, 1))
545 gradient.ReverseRainbow = lineargradient_hsb(hsb(2.0/3.0, 1, 1), hsb(0, 1, 1))
546 gradient.Hue = lineargradient_hsb(hsb(0, 1, 1), hsb(1, 1, 1))
547 gradient.ReverseHue = lineargradient_hsb(hsb(1, 1, 1), hsb(0, 1, 1))
548 rgbgradient.Rainbow = rgbgradient(gradient.Rainbow)
549 rgbgradient.ReverseRainbow = rgbgradient(gradient.ReverseRainbow)
550 rgbgradient.Hue = rgbgradient(gradient.Hue)
551 rgbgradient.ReverseHue = rgbgradient(gradient.ReverseHue)
552 cmykgradient.Rainbow = cmykgradient(gradient.Rainbow)
553 cmykgradient.ReverseRainbow = cmykgradient(gradient.ReverseRainbow)
554 cmykgradient.Hue = cmykgradient(gradient.Hue)
555 cmykgradient.ReverseHue = cmykgradient(gradient.ReverseHue)
556 def jet_r(x):
557 if x < 0.38: return 0
558 elif x < 0.62: return (x-0.38)/(0.62-0.38)
559 elif x < 0.87: return 1
560 else: return 0.5 + 0.5*(1-x)/(1-0.87)
561 def jet_g(x):
562 if x < 0.13: return 0
563 elif x < 0.38: return (x-0.13)/(0.38-0.13)
564 elif x < 0.62: return 1
565 elif x < 0.87: return (0.87-x)/(0.87-0.62)
566 else: return 0
567 def jet_b(x):
568 if x < 0.13: return 0.5 + 0.5*x/0.13
569 elif x < 0.38: return 1
570 elif x < 0.62: return 1-(x-0.38)/(0.62-0.38)
571 else: return 0
572 gradient.Jet = functiongradient_rgb(f_r=jet_r, f_g=jet_g, f_b=jet_b)
573 gradient.ReverseJet = functiongradient_rgb(f_r=lambda x: jet_r(1-x), f_g=lambda x: jet_g(1-x), f_b=lambda x: jet_b(1-x))
574 cmykgradient.Jet = cmykgradient(gradient.Jet)
575 cmykgradient.ReverseJet = cmykgradient(gradient.ReverseJet)
579 class PDFextgstate(pdfwriter.PDFobject):
581 def __init__(self, name, extgstate, registry):
582 pdfwriter.PDFobject.__init__(self, "extgstate", name)
583 registry.addresource("ExtGState", name, self)
584 self.name = name
585 self.extgstate = extgstate
587 def write(self, file, writer, registry):
588 file.write("%s\n" % self.extgstate)
591 class transparency(attr.exclusiveattr, style.strokestyle, style.fillstyle):
593 def __init__(self, value):
594 self.value = 1-value
595 attr.exclusiveattr.__init__(self, transparency)
597 def processPS(self, file, writer, context, registry):
598 logger.warning("Transparency not available in PostScript, proprietary ghostscript extension code inserted.")
599 file.write("%f .setshapealpha\n" % self.value)
601 def processPDF(self, file, writer, context, registry):
602 if context.strokeattr and context.fillattr:
603 registry.add(PDFextgstate("Transparency-%f" % self.value,
604 "<< /Type /ExtGState /CA %f /ca %f >>" % (self.value, self.value), registry))
605 file.write("/Transparency-%f gs\n" % self.value)
606 elif context.strokeattr:
607 registry.add(PDFextgstate("Transparency-Stroke-%f" % self.value,
608 "<< /Type /ExtGState /CA %f >>" % self.value, registry))
609 file.write("/Transparency-Stroke-%f gs\n" % self.value)
610 elif context.fillattr:
611 registry.add(PDFextgstate("Transparency-Fill-%f" % self.value,
612 "<< /Type /ExtGState /ca %f >>" % self.value, registry))
613 file.write("/Transparency-Fill-%f gs\n" % self.value)
615 def processSVGattrs(self, attrs, writer, context, registry):
616 if context.strokeattr:
617 context.strokeopacity = self.value
618 if context.fillattr:
619 context.fillopacity = self.value