209 lines
7.1 KiB
Python
209 lines
7.1 KiB
Python
|
#!/usr/bin/env python3
|
||
|
'''
|
||
|
Copyright (C) 2006 Jos Hirth, kaioa.com
|
||
|
Copyright (C) 2007 Aaron C. Spike
|
||
|
Copyright (C) 2009 Monash University
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 2 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, write to the Free Software
|
||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
'''
|
||
|
import sys, copy, inkex
|
||
|
import random
|
||
|
from inkex import colors
|
||
|
|
||
|
color_props_fill = ('fill', 'stop-color', 'flood-color', 'lighting-color')
|
||
|
color_props_stroke = ('stroke',)
|
||
|
opacity_props = ('opacity',) #'stop-opacity', 'fill-opacity', 'stroke-opacity' don't work with clones
|
||
|
color_props = color_props_fill + color_props_stroke
|
||
|
|
||
|
|
||
|
class ColorEffect(inkex.Effect):
|
||
|
def __init__(self):
|
||
|
inkex.Effect.__init__(self)
|
||
|
self.visited = []
|
||
|
|
||
|
def effect(self):
|
||
|
if len(self.selected)==0:
|
||
|
self.getAttribs(self.document.getroot())
|
||
|
else:
|
||
|
for id,node in self.selected.items():
|
||
|
self.getAttribs(node)
|
||
|
|
||
|
def getAttribs(self,node):
|
||
|
self.changeStyle(node)
|
||
|
for child in node:
|
||
|
self.getAttribs(child)
|
||
|
|
||
|
def changeStyle(self,node):
|
||
|
for attr in color_props:
|
||
|
val = node.get(attr)
|
||
|
if val:
|
||
|
new_val = self.process_prop(val)
|
||
|
if new_val != val:
|
||
|
node.set(attr, new_val)
|
||
|
|
||
|
if 'style' in node.attrib:
|
||
|
# References for style attribute:
|
||
|
# http://www.w3.org/TR/SVG11/styling.html#StyleAttribute,
|
||
|
# http://www.w3.org/TR/CSS21/syndata.html
|
||
|
#
|
||
|
# The SVG spec is ambiguous as to how style attributes should be parsed.
|
||
|
# For example, it isn't clear whether semicolons are allowed to appear
|
||
|
# within strings or comments, or indeed whether comments are allowed to
|
||
|
# appear at all.
|
||
|
#
|
||
|
# The processing here is just something simple that should usually work,
|
||
|
# without trying too hard to get everything right.
|
||
|
# (Won't work for the pathological case that someone escapes a property
|
||
|
# name, probably does the wrong thing if colon or semicolon is used inside
|
||
|
# a comment or string value.)
|
||
|
style = node.get('style') # fixme: this will break for presentation attributes!
|
||
|
if style:
|
||
|
#inkex.utils.debug('old style:'+style)
|
||
|
declarations = style.split(';')
|
||
|
opacity_in_style = False
|
||
|
for i,decl in enumerate(declarations):
|
||
|
parts = decl.split(':', 2)
|
||
|
if len(parts) == 2:
|
||
|
(prop, val) = parts
|
||
|
prop = prop.strip().lower()
|
||
|
if prop in color_props:
|
||
|
val = val.strip()
|
||
|
new_val = self.process_prop(val)
|
||
|
if new_val != val:
|
||
|
declarations[i] = prop + ':' + new_val
|
||
|
elif prop in opacity_props:
|
||
|
opacity_in_style = True
|
||
|
val = val.strip()
|
||
|
new_val = self.process_prop(val)
|
||
|
if new_val != val:
|
||
|
declarations[i] = prop + ':' + new_val
|
||
|
if not opacity_in_style:
|
||
|
new_val = self.process_prop("1")
|
||
|
declarations.append('opacity' + ':' + new_val)
|
||
|
#inkex.utils.debug('new style:'+';'.join(declarations))
|
||
|
node.set('style', ';'.join(declarations))
|
||
|
|
||
|
def process_prop(self, col):
|
||
|
#inkex.utils.debug('got:'+col+str(type(col)))
|
||
|
if colors.isColor(col):
|
||
|
c=colors.parseColor(col)
|
||
|
col='#'+self.colmod(c[0], c[1], c[2])
|
||
|
#inkex.utils.debug('made:'+col)
|
||
|
elif col.startswith('url(#'):
|
||
|
id = col[len('url(#'):col.find(')')]
|
||
|
newid = '%s-%d' % (id, int(random.random() * 1000))
|
||
|
#inkex.utils.debug('ID:' + id )
|
||
|
path = '//*[@id="%s"]' % id
|
||
|
for node in self.document.xpath(path, namespaces=inkex.NSS):
|
||
|
self.process_gradient(node, newid)
|
||
|
col = 'url(#%s)' % newid
|
||
|
# what remains should be opacity
|
||
|
else:
|
||
|
col = self.opacmod(col)
|
||
|
|
||
|
#inkex.utils.debug('col:'+str(col))
|
||
|
return col
|
||
|
|
||
|
def process_gradient(self, node, newid):
|
||
|
#if node.hasAttributes():
|
||
|
#this_id=node.getAttribute('id')
|
||
|
#if this_id in self.visited:
|
||
|
## prevent multiple processing of the same gradient if it is used by more than one selected object
|
||
|
##inkex.utils.debug("already had: " + this_id)
|
||
|
#return
|
||
|
#self.visited.append(this_id)
|
||
|
#inkex.utils.debug("visited: " + str(self.visited))
|
||
|
newnode = copy.deepcopy(node)
|
||
|
newnode.set('id', newid)
|
||
|
node.getparent().append(newnode)
|
||
|
self.changeStyle(newnode)
|
||
|
for child in newnode:
|
||
|
self.changeStyle(child)
|
||
|
xlink = inkex.addNS('href','xlink')
|
||
|
if xlink in newnode.attrib:
|
||
|
href=newnode.get(xlink)
|
||
|
if href.startswith('#'):
|
||
|
id = href[len('#'):len(href)]
|
||
|
#inkex.utils.debug('ID:' + id )
|
||
|
newhref = '%s-%d' % (id, int(random.random() * 1000))
|
||
|
newnode.set(xlink, '#%s' % newhref)
|
||
|
path = '//*[@id="%s"]' % id
|
||
|
for node in self.document.xpath(path, namespaces=inkex.NSS):
|
||
|
self.process_gradient(node, newhref)
|
||
|
|
||
|
def colmod(self,r,g,b):
|
||
|
pass
|
||
|
|
||
|
def opacmod(self, opacity):
|
||
|
return opacity
|
||
|
|
||
|
def rgb_to_hsl(self,r, g, b):
|
||
|
rgb_max = max (max (r, g), b)
|
||
|
rgb_min = min (min (r, g), b)
|
||
|
delta = rgb_max - rgb_min
|
||
|
hsl = [0.0, 0.0, 0.0]
|
||
|
hsl[2] = (rgb_max + rgb_min)/2.0
|
||
|
if delta == 0:
|
||
|
hsl[0] = 0.0
|
||
|
hsl[1] = 0.0
|
||
|
else:
|
||
|
if hsl[2] <= 0.5:
|
||
|
hsl[1] = delta / (rgb_max + rgb_min)
|
||
|
else:
|
||
|
hsl[1] = delta / (2 - rgb_max - rgb_min)
|
||
|
if r == rgb_max:
|
||
|
hsl[0] = (g - b) / delta
|
||
|
else:
|
||
|
if g == rgb_max:
|
||
|
hsl[0] = 2.0 + (b - r) / delta
|
||
|
else:
|
||
|
if b == rgb_max:
|
||
|
hsl[0] = 4.0 + (r - g) / delta
|
||
|
hsl[0] = hsl[0] / 6.0
|
||
|
if hsl[0] < 0:
|
||
|
hsl[0] = hsl[0] + 1
|
||
|
if hsl[0] > 1:
|
||
|
hsl[0] = hsl[0] - 1
|
||
|
return hsl
|
||
|
|
||
|
def hue_2_rgb (self, v1, v2, h):
|
||
|
if h < 0:
|
||
|
h += 6.0
|
||
|
if h > 6:
|
||
|
h -= 6.0
|
||
|
if h < 1:
|
||
|
return v1 + (v2 - v1) * h
|
||
|
if h < 3:
|
||
|
return v2
|
||
|
if h < 4:
|
||
|
return v1 + (v2 - v1) * (4 - h)
|
||
|
return v1
|
||
|
|
||
|
def hsl_to_rgb (self,h, s, l):
|
||
|
rgb = [0, 0, 0]
|
||
|
if s == 0:
|
||
|
rgb[0] = l
|
||
|
rgb[1] = l
|
||
|
rgb[2] = l
|
||
|
else:
|
||
|
if l < 0.5:
|
||
|
v2 = l * (1 + s)
|
||
|
else:
|
||
|
v2 = l + s - l*s
|
||
|
v1 = 2*l - v2
|
||
|
rgb[0] = self.hue_2_rgb (v1, v2, h*6 + 2.0)
|
||
|
rgb[1] = self.hue_2_rgb (v1, v2, h*6)
|
||
|
rgb[2] = self.hue_2_rgb (v1, v2, h*6 - 2.0)
|
||
|
return rgb
|