Chapter 12.  Colors and Styles

Posted on

SVG Styles

The SVG specs has a chapter on styling. The SVG and Cascading Style Sheets (CSS) share many styling properties. There are two ways to style an element in SVG. We can style an element using individual attributes or combining them together as the value of style attribute. For example, we can create a blue rectangle with red border like this.

<rect
    fill="blue"
    stroke="red"
    stroke-width="2"
    x="10" y="10" width="30" height="20" />

Or we can specify a single style attribute like this.

<rect
    style="fill:blue; stroke:red; stroke-width:3"
    x="50" y="50" width="30" height="20" />

SVG elements generated by Inkscape itself use the style attribute.

Style Module

The style.py module in inkex directory defines several classes. The most notable one is the Style class which derives from OrderedDict class. The __init__ method of the class is written in a way that it accepts a dictionary, a string, key value pairs, or another Style object as the initial value. The Python interpreter session below shows how to initialize a Style object in different ways.

george@Inspiron-5515:~$ /usr/bin/python3

>>> import sys
>>> sys.path.append('/usr/share/inkscape/extensions')

>>> from inkex import Style
>>> s = Style({'fill':'red', 'stroke':'green'})
>>> s
Style([('fill', 'red'), ('stroke', 'green')])
>>> str(s)
'fill:red;stroke:green'

>>> st1 = Style('fill:red; stroke:green')
>>> str(st1)
'fill:red;stroke:green'

>>> st2 = Style(fill='red', stroke='green')
>>> str(st2)
'fill:red;stroke:green'

>>> st3 = Style(st2)
>>> str(st3)
'fill:red;stroke:green'

The static method parse_str parses a string and yields key value pair as a tuple from which we can create a new Style object. The to_str method converts a Style object to a string.

The update method is similar to Python dictionary update method. The Style class also defines the __add__ and __sub__ methods so we can add or subtract Style class objects. The operators + or - create new copies of Style object.

>>> st4 = Style({'stroke-width':'3'})
>>> st5 = st3 + st4
>>> print(st5)
fill:red;stroke:green;stroke-width:3
>>> st6 = st5 - {'fill':'red'}
>>> print(st6)
stroke:green;stroke-width:3

The Style class also defines a get_color and a set_color method. We can use them like the examples shown below.

>>> st6.get_color('stroke')
[0, 128, 0, 1.0]

>>> st6.set_color('red', 'fill')
>>> print(st6)
stroke:green;stroke-width:3;fill:#ff0000

When we are working on element style in an Inkscape extension, we change the element style in Inkscape itself and watch the style value in XML editor. Once we find a value to use, we can set the same style attribute and value in extension code. As shown in previous chapters, we set style property of an element object with a Style object.

element.style = st2 # st2 is a Style object

Color Module

The colors.py module in inkex directory defines an SVG_COLOR dictionary with 148 color names and values. The is_color function determines if a value is a color that we can use.

>>> from inkex.colors import SVG_COLOR
>>> len(SVG_COLOR) # include a none
149
>>> SVG_COLOR['none']
>>> from inkex.colors import is_color
>>> is_color('#334')
True
>>> is_color('#334srd')  # wrong value above F
False
>>> is_color('#33AABB')
True
>>> is_color('red')
True
>>> is_color(345)
True
>>> is_color('#334ABD00') # this is in rgba format
True

We usually use the color name as a string value such as red or an RGB string value such as #FF00CC when we specify a color in extension code.

ColorExtension Class

The Inkscape has a whole class of extensions dealing with colors. They are listed under the Extensions > Color menu. The Randomize and Negative are interesting, and Grayscale and Replace color are sometimes useful. Those extensions have classes derived from ColorExtension class which is defined in the extensions.py module.

Let’s take a look at the Grayscale extension. The content of the color_grayscale.py module is listed below. It only overrides a modify_color method. The color argument is the color of the element to be modified. The name argument is one of the color attribute names stroke, fill, stop-color, flood-color or lighting-color defined in the Style class. The return value is the new modified color.

#!/usr/bin/env python
"""Convert to grey"""

import inkex

class Grayscale(inkex.ColorExtension):
    """Make all colours grayscale"""
    def modify_color(self, name, color):
        # ITU-R Recommendation BT.709 (NTSC and PAL)
        # l = 0.2125 * r + 0.7154 * g + 0.0721 * b
        lum = 0.299 * color.red + 0.587 * color.green + 0.114 * color.blue
        return inkex.Color((int(round(lum)), 
                    int(round(lum)), int(round(lum))))

if __name__ == '__main__':
    Grayscale().run()