Code

Simple first pass for rough timing
[inkscape.git] / share / extensions / coloreffect.py
1 #!/usr/bin/env python 
2 '''
3 Copyright (C) 2006 Jos Hirth, kaioa.com
4 Copyright (C) 2007 Aaron C. Spike
5 Copyright (C) 2009 Monash University
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 '''
21 import sys, copy, simplestyle, inkex
22 import random
24 color_props_fill = ('fill', 'stop-color', 'flood-color', 'lighting-color')
25 color_props_stroke = ('stroke',)
26 color_props = color_props_fill + color_props_stroke
29 class ColorEffect(inkex.Effect):
30   def __init__(self):
31     inkex.Effect.__init__(self)
32     self.visited = []
34   def effect(self):
35     if len(self.selected)==0:
36       self.getAttribs(self.document.getroot())
37     else:
38       for id,node in self.selected.iteritems():
39         self.getAttribs(node)
41   def getAttribs(self,node):
42     self.changeStyle(node)
43     for child in node:
44       self.getAttribs(child)
46   def changeStyle(self,node):
47     for attr in color_props:
48         val = node.get(attr)
49         if val:
50             new_val = self.process_prop(val)
51             if new_val != val:
52                 node.set(attr, new_val)
54     if node.attrib.has_key('style'):
55         # References for style attribute:
56         # http://www.w3.org/TR/SVG11/styling.html#StyleAttribute,
57         # http://www.w3.org/TR/CSS21/syndata.html
58         #
59         # The SVG spec is ambiguous as to how style attributes should be parsed.
60         # For example, it isn't clear whether semicolons are allowed to appear
61         # within strings or comments, or indeed whether comments are allowed to
62         # appear at all.
63         #
64         # The processing here is just something simple that should usually work,
65         # without trying too hard to get everything right.
66         # (Won't work for the pathalogical case that someone escapes a property
67         # name, probably does the wrong thing if colon or semicolon is used inside
68         # a comment or string value.)
69         style = node.get('style') # fixme: this will break for presentation attributes!
70         if style:
71             #inkex.debug('old style:'+style)
72             declarations = style.split(';')
73             for i,decl in enumerate(declarations):
74                 parts = decl.split(':', 2)
75                 if len(parts) == 2:
76                     (prop, val) = parts
77                     prop = prop.strip().lower()
78                     if prop in color_props:
79                         val = val.strip()
80                         new_val = self.process_prop(val)
81                         if new_val != val:
82                             declarations[i] = prop + ':' + new_val
83             #inkex.debug('new style:'+';'.join(declarations))
84             node.set('style', ';'.join(declarations))
86   def process_prop(self,col):
87     #debug('got:'+col)
88     if simplestyle.isColor(col):
89       c=simplestyle.parseColor(col)
90       col='#'+self.colmod(c[0],c[1],c[2])
91       #debug('made:'+col)
92     if col.startswith('url(#'):
93       id = col[len('url(#'):col.find(')')]
94       newid = '%s-%d' % (id, int(random.random() * 1000))
95       #inkex.debug('ID:' + id )
96       path = '//*[@id="%s"]' % id
97       for node in self.document.xpath(path, namespaces=inkex.NSS):
98         self.process_gradient(node, newid)
99       col = 'url(#%s)' % newid
100     return col
102   def process_gradient(self, node, newid):
103     #if node.hasAttributes():
104        #this_id=node.getAttribute('id')
105        #if this_id in self.visited:
106          ## prevent multiple processing of the same gradient if it is used by more than one selected object
107          ##inkex.debug("already had: " + this_id)
108          #return
109        #self.visited.append(this_id)
110        #inkex.debug("visited: " + str(self.visited))
111     newnode = copy.deepcopy(node)
112     newnode.set('id', newid)
113     node.getparent().append(newnode)
114     self.changeStyle(newnode)
115     for child in newnode:
116       self.changeStyle(child)
117     xlink = inkex.addNS('href','xlink')
118     if newnode.attrib.has_key(xlink):
119       href=newnode.get(xlink)
120       if href.startswith('#'):
121         id = href[len('#'):len(href)]
122         #inkex.debug('ID:' + id )
123         newhref = '%s-%d' % (id, int(random.random() * 1000))
124         newnode.set(xlink, '#%s' % newhref)
125         path = '//*[@id="%s"]' % id
126         for node in self.document.xpath(path, namespaces=inkex.NSS):
127           self.process_gradient(node, newhref)
128  
129   def colmod(self,r,g,b):
130     pass
132   def rgb_to_hsl(self,r, g, b):
133     rgb_max = max (max (r, g), b)
134     rgb_min = min (min (r, g), b)
135     delta = rgb_max - rgb_min
136     hsl = [0.0, 0.0, 0.0]
137     hsl[2] = (rgb_max + rgb_min)/2.0
138     if delta == 0:
139         hsl[0] = 0.0
140         hsl[1] = 0.0
141     else:
142         if hsl[2] <= 0.5:
143             hsl[1] = delta / (rgb_max + rgb_min)
144         else:
145             hsl[1] = delta / (2 - rgb_max - rgb_min)
146         if r == rgb_max:
147             hsl[0] = (g - b) / delta
148         else:
149             if g == rgb_max:
150                 hsl[0] = 2.0 + (b - r) / delta
151             else:
152                 if b == rgb_max:
153                     hsl[0] = 4.0 + (r - g) / delta
154         hsl[0] = hsl[0] / 6.0
155         if hsl[0] < 0:
156             hsl[0] = hsl[0] + 1
157         if hsl[0] > 1:
158             hsl[0] = hsl[0] - 1
159     return hsl
161   def hue_2_rgb (self, v1, v2, h):
162     if h < 0:
163         h += 6.0
164     if h > 6:
165         h -= 6.0
166     if h < 1:
167         return v1 + (v2 - v1) * h
168     if h < 3:
169         return v2
170     if h < 4:
171         return v1 + (v2 - v1) * (4 - h)
172     return v1
174   def hsl_to_rgb (self,h, s, l):
175     rgb = [0, 0, 0]
176     if s == 0:
177         rgb[0] = l
178         rgb[1] = l
179         rgb[2] = l
180     else:
181         if l < 0.5:
182             v2 = l * (1 + s)
183         else:
184             v2 = l + s - l*s
185         v1 = 2*l - v2
186         rgb[0] = self.hue_2_rgb (v1, v2, h*6 + 2.0)
187         rgb[1] = self.hue_2_rgb (v1, v2, h*6)
188         rgb[2] = self.hue_2_rgb (v1, v2, h*6 - 2.0)
189     return rgb
191 # vi: set autoindent shiftwidth=2 tabstop=8 expandtab softtabstop=2 :