HEX
Server: Apache
System: Linux opal14.opalstack.com 3.10.0-1160.108.1.el7.x86_64 #1 SMP Thu Jan 25 16:17:31 UTC 2024 x86_64
User: curbgloabal_opal (1234)
PHP: 8.1.29
Disabled: exec,passthru,shell_exec,system
Upload Files
File: //usr/share/inkscape/extensions/nicechart.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#  nicechart.py
#
#  Copyright 2011-2016
#  
#  Christoph Sterz 
#  Florian Weber
#  Maren Hachmann
#  
#  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 3 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.
#  

# TODO / Ideas: 
# allow negative values for bar charts
# show values for stacked bar charts
# don't create a new layer for each chart, but a normal group
# correct bar height for stacked bars (it's only half as high as it should be, double)
# adjust position of heading
# use aliasing workaround for stacked bars (e.g. let the rectangles overlap)

# Example CSV file contents:
'''
Month;1978;1979;1980;1981
January;2;1,3;0.1;2.3
February;6.5;2.4;1.2;6.1
March;7.4;6.7;7.9;4.7
April;7.7;6.4;8.2;8.9
May;10.9;11.7;18.7;11.1
June;12.6;14.2;14.7;14.7
July;16.5;15.5;17.5;15.1
August;15.9;15.4;14.6;16.6
September;14;14.5;13.2;15.3
October;11.9;13.9;11.5;9.2
November;6.7;8.5;7;6.6
December;6.4;2.2;6.3;3.5
'''
# The extension creates one chart for a single value column in one go,
# e.g. chart all temperatures for all months of the year 1978 into one chart.
# (for this, select column 0 for labels and column 1 for values).
# "1978" etc. can be used as heading (Need not be numeric. If not used delete the heading line.)
# Month names can be used as labels
# Values can be shown, in addition to labels (doesn't work with stacked bar charts)
# Values can contain commas as decimal separator, as long as delimiter isn't comma
# Negative values are not yet supported.


import re
import sys
import math
import inkex

from simplestyle import *

#www.sapdesignguild.org/goodies/diagram_guidelines/color_palettes.html#mss
COLOUR_TABLE = {
  "red": ["#460101", "#980101", "#d40000", "#f44800", "#fb8b00", "#eec73e", "#d9bb7a", "#fdd99b"],
  "blue": ["#000442", "#0F1781", "#252FB7", "#3A45E1", "#656DDE", "#8A91EC"],
  "gray": ["#222222", "#444444", "#666666", "#888888", "#aaaaaa", "#cccccc", "#eeeeee"],
  "contrast": ["#0000FF", "#FF0000", "#00FF00", "#CF9100", "#FF00FF", "#00FFFF"],
  "sap": ["#f8d753", "#5c9746", "#3e75a7", "#7a653e", "#e1662a", "#74796f", "#c4384f",
          "#fff8a3", "#a9cc8f", "#b2c8d9", "#bea37a", "#f3aa79", "#b5b5a9", "#e6a5a5"]
}

def get_color_scheme(name="default"):
    return COLOUR_TABLE.get(name.lower(), COLOUR_TABLE['red'])


class NiceChart(inkex.Effect):
    """
    Inkscape extension that can draw pie charts and bar charts 
    (stacked, single, horizontally or vertically) 
    with optional drop shadow, from a csv file or from pasted text
    """
    
    def __init__(self):
        """
        Constructor.
        Defines the "--what" option of a script.
        """
        # Call the base class constructor.
        inkex.Effect.__init__(self)
        
        # Define string option "--what" with "-w" shortcut and default chart values.
        self.OptionParser.add_option('-w', '--what', action='store',
              type='string', dest='what', default='22,11,67',
              help='Chart Values')
            
        # Define string option "--type" with "-t" shortcut.
        self.OptionParser.add_option("-t", "--type", action="store",
              type="string", dest="type", default='',
              help="Chart Type")
        
        # Define bool option "--blur" with "-b" shortcut.
        self.OptionParser.add_option("-b", "--blur", action="store",
              type="inkbool", dest="blur", default='True',
              help="Blur Type")
        
        # Define string option "--file" with "-f" shortcut.
        self.OptionParser.add_option("-f", "--filename", action="store",
              type="string", dest="filename", default='',
              help="Name of File")
        
        # Define string option "--input_type" with "-i" shortcut.
        self.OptionParser.add_option("-i", "--input_type", action="store",
              type="string", dest="input_type", default='file',
              help="Chart Type")
        
        # Define string option "--delimiter" with "-d" shortcut.
        self.OptionParser.add_option("-d", "--delimiter", action="store",
              type="string", dest="csv_delimiter", default=';',
              help="delimiter")
              
        # Define string option "--colors" with "-c" shortcut.
        self.OptionParser.add_option("-c", "--colors", action="store",
              type="string", dest="colors", default='default',
              help="color-scheme")

        # Define string option "--colors_override"
        self.OptionParser.add_option("", "--colors_override", action="store",
              type="string", dest="colors_override", default='',
              help="color-scheme-override")
        

        self.OptionParser.add_option("", "--reverse_colors", action="store",
              type="inkbool", dest="reverse_colors", default='False',
              help="reverse color-scheme")
        
        self.OptionParser.add_option("-k", "--col_key", action="store",
              type="int", dest="col_key", default='0',
              help="column that contains the keys")
        
        
        self.OptionParser.add_option("-v", "--col_val", action="store",
              type="int", dest="col_val", default='1',
              help="column that contains the values")
              
        self.OptionParser.add_option("", "--encoding", action="store",
              type="string", dest="encoding", default='utf-8',
              help="encoding of the CSV file, e.g. utf-8")
        
        self.OptionParser.add_option("", "--headings", action="store",
              type="inkbool", dest="headings", default='False',
              help="the first line of the CSV file consists of headings for the columns")
              
        self.OptionParser.add_option("-r", "--rotate", action="store",
              type="inkbool", dest="rotate", default='False',
              help="Draw barchart horizontally")
            
        self.OptionParser.add_option("-W", "--bar-width", action="store",
            type="int", dest="bar_width", default='10',
            help="width of bars")
        
        self.OptionParser.add_option("-p", "--pie-radius", action="store",
            type="int", dest="pie_radius", default='100',
            help="radius of pie-charts")
            
        self.OptionParser.add_option("-H", "--bar-height", action="store",
            type="int", dest="bar_height", default='100',
            help="height of bars")
            
        self.OptionParser.add_option("-O", "--bar-offset", action="store",
            type="int", dest="bar_offset", default='5',
            help="distance between bars")
            
        self.OptionParser.add_option("", "--stroke-width", action="store",
            type="float", dest="stroke_width", default='1')
            
        self.OptionParser.add_option("-o", "--text-offset", action="store",
            type="int", dest="text_offset", default='5',
            help="distance between bar and descriptions")
        
        self.OptionParser.add_option("", "--heading-offset", action="store",
            type="int", dest="heading_offset", default='50',
            help="distance between chart and chart title")
        
        self.OptionParser.add_option("", "--segment-overlap", action="store",
            type="inkbool", dest="segment_overlap", default='False',
            help="work around aliasing effects by letting pie chart segments overlap")
            
        self.OptionParser.add_option("-F", "--font", action="store",
            type="string", dest="font", default='sans-serif',
            help="font of description")
            
        self.OptionParser.add_option("-S", "--font-size", action="store",
            type="int", dest="font_size", default='10',
            help="font size of description")
        
        self.OptionParser.add_option("-C", "--font-color", action="store",
            type="string", dest="font_color", default='black',
            help="font color of description")
        #Dummy:
        self.OptionParser.add_option("","--input_sections")

        self.OptionParser.add_option("-V", "--show_values", action="store",
            type="inkbool", dest="show_values", default='False',
            help="Show values in chart")
    
    def effect(self):
        """
        Effect behaviour.
        Overrides base class' method and inserts a nice looking chart into SVG document.
        """
        # Get script's "--what" option value and process the data type --- i concess the if term is a little bit of magic
        what = self.options.what
        keys = []
        values = []
        orig_values = []
        keys_present = True
        pie_abs = False
        cnt = 0
        csv_file_name = self.options.filename
        csv_delimiter = self.options.csv_delimiter
        input_type = self.options.input_type
        col_key = self.options.col_key
        col_val = self.options.col_val
        show_values = self.options.show_values
        encoding = self.options.encoding.strip() or 'utf-8'
        headings = self.options.headings
        heading_offset = self.options.heading_offset
        
        if input_type == "\"file\"":
            csv_file = open(csv_file_name, "r")
            
            for linenum, line in enumerate(csv_file):
                value = line.decode(encoding).split(csv_delimiter)
                #make sure that there is at least one value (someone may want to use it as description)
                if len(value) >= 1:
                    # allow to parse headings as strings
                    if linenum == 0 and headings:
                        heading = value[col_val]
                    else:
                        keys.append(value[col_key])
                        # replace comma decimal separator from file by colon, 
                        # to avoid file editing for people whose programs output
                        # values with comma
                        values.append(float(value[col_val].replace(",",".")))
            csv_file.close()
            
        elif input_type == "\"direct_input\"":
            what = re.findall("([A-Z|a-z|0-9]+:[0-9]+\.?[0-9]*)", what)
            for value in what:
                value = value.split(":")
                keys.append(value[0])
                values.append(float(value[1]))

        # warn about negative values (not yet supported)
        for value in values:
            if value < 0:
              inkex.errormsg("Negative values are currently not supported!")
              return

        # Get script's "--type" option value.
        charttype = self.options.type

        if charttype == "pie_abs":
            pie_abs = True
            charttype = "pie"

        # Get access to main SVG document element and get its dimensions.
        svg = self.document.getroot()
        
        # Get the page attibutes:
        width  = self.getUnittouu(svg.get('width'))
        height = self.getUnittouu(svg.attrib['height'])
        
        # Create a new layer.
        layer = inkex.etree.SubElement(svg, 'g')
        layer.set(inkex.addNS('label', 'inkscape'), 'Chart-Layer: %s' % (what))
        layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
        
        # Check if a drop shadow should be drawn:
        draw_blur = self.options.blur
        
        if draw_blur:
            # Get defs of Document
            defs = self.xpathSingle('/svg:svg//svg:defs')
            if defs == None:
                defs = inkex.etree.SubElement(self.document.getroot(), inkex.addNS('defs', 'svg'))
            
            # Create new Filter
            filt = inkex.etree.SubElement(defs,inkex.addNS('filter', 'svg'))
            filtId = self.uniqueId('filter')
            self.filtId = 'filter:url(#%s);' % filtId
            for k, v in [('id', filtId), ('height', "3"),
                         ('width', "3"),
                         ('x', '-0.5'), ('y', '-0.5')]:
                filt.set(k, v)
            
            # Append Gaussian Blur to that Filter
            fe = inkex.etree.SubElement(filt, inkex.addNS('feGaussianBlur', 'svg'))
            fe.set('stdDeviation', "1.1")
        
        # Set Default Colors
        self.options.colors_override.strip()
        if len(self.options.colors_override) > 0:
            colors = self.options.colors_override
        else:
            colors = self.options.colors

        if colors[0].isalpha():
            colors = get_color_scheme(colors)
        else:
            colors = re.findall("(#[0-9a-fA-F]{6})", colors)
            #to be sure we create a fallback:
            if len(colors) == 0:
                colors = get_color_scheme()
        
        color_count = len(colors)
        
        if self.options.reverse_colors:
            colors.reverse()
        
        # Those values should be self-explanatory:
        bar_height = self.options.bar_height
        bar_width = self.options.bar_width
        bar_offset = self.options.bar_offset
        # offset of the description in stacked-bar-charts:
        # stacked_bar_text_offset=self.options.stacked_bar_text_offset
        text_offset = self.options.text_offset
        # prevents ugly aliasing effects between pie chart segments by overlapping
        segment_overlap = self.options.segment_overlap
        
        # get font
        font = self.options.font
        font_size = self.options.font_size
        font_color = self.options.font_color
        
        # get rotation
        rotate = self.options.rotate
        
        pie_radius = self.options.pie_radius
        stroke_width = self.options.stroke_width

        if charttype == "bar":
        #########
        ###BAR###
        #########
            
            # iterate all values, use offset to draw the bars in different places
            offset = 0
            color = 0
            
            # Normalize the bars to the largest value
            try:
                value_max = max(values)
            except ValueError:
                value_max = 0.0

            for x in range(len(values)):
                orig_values.append(values[x])
                values[x] = (values[x]/value_max) * bar_height

            # Draw Single bars with their shadows
            for value in values:
                
                # draw drop shadow, if necessary
                if draw_blur:
                    # Create shadow element
                    shadow = inkex.etree.Element(inkex.addNS("rect", "svg"))
                    # Set chart position to center of document. Make it horizontal or vertical
                    if not rotate:
                        shadow.set('x', str(width/2 + offset + 1))
                        shadow.set('y', str(height/2 - int(value) + 1))
                        shadow.set("width", str(bar_width))
                        shadow.set("height", str(int(value)))
                    else:
                        shadow.set('y', str(width/2 + offset + 1))
                        shadow.set('x', str(height/2 + 1))
                        shadow.set("height", str(bar_width))
                        shadow.set("width", str(int(value)))
                        
                    # Set shadow blur (connect to filter object in xml path)
                    shadow.set("style", "filter:url(#filter)")
                
                # Create rectangle element
                rect = inkex.etree.Element(inkex.addNS('rect', 'svg'))
                
                # Set chart position to center of document.
                if not rotate:
                    rect.set('x', str(width/2 + offset))
                    rect.set('y', str(height/2 - int(value)))
                    rect.set("width", str(bar_width))
                    rect.set("height", str(int(value)))
                else:
                    rect.set('y', str(width/2 + offset))
                    rect.set('x', str(height/2))
                    rect.set("height", str(bar_width))
                    rect.set("width", str(int(value)))
                    
                rect.set("style", "fill:" + colors[color % color_count])
                
                # If keys are given, create text elements
                if keys_present:
                    text = inkex.etree.Element(inkex.addNS('text', 'svg'))
                    if not rotate: #=vertical
                        text.set("transform", "matrix(0,-1,1,0,0,0)")
                        #y after rotation:
                        text.set("x", "-" + str(height/2 + text_offset)) 
                        #x after rotation:
                        text.set("y", str(width/2 + offset + bar_width/2 + font_size/3))
                    else: #=horizontal
                        text.set("y", str(width/2 + offset + bar_width/2 + font_size/3))
                        text.set("x", str(height/2 - text_offset))
                    
                    text.set("style", "font-size:" + str(font_size)\
                           + "px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:"\
                           + font + ";-inkscape-font-specification:Bitstream Charter;text-align:end;text-anchor:end;fill:"\
                           + font_color)

                    text.text = keys[cnt]

                # Increase Offset and Color
                #offset=offset+bar_width+bar_offset
                color = (color + 1) % 8
                # Connect elements together.
                if draw_blur:
                    layer.append(shadow)
                layer.append(rect)
                if keys_present:
                    layer.append(text)
                    
                if show_values:
                    vtext = inkex.etree.Element(inkex.addNS('text', 'svg'))
                    if not rotate: #=vertical
                        vtext.set("transform", "matrix(0,-1,1,0,0,0)")
                        #y after rotation:
                        vtext.set("x", "-"+str(height/2+text_offset-value-text_offset-text_offset)) 
                        #x after rotation:
                        vtext.set("y", str(width/2+offset+bar_width/2+font_size/3))
                    else: #=horizontal
                        vtext.set("y", str(width/2+offset+bar_width/2+font_size/3))
                        vtext.set("x", str(height/2-text_offset+value+text_offset+text_offset))

                    vtext.set("style", "font-size:"+str(font_size)\
                            + "px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:"\
                            + font + ";-inkscape-font-specification:Bitstream Charter;text-align:start;text-anchor:start;fill:"\
                            + font_color)

                    vtext.text = str(int(orig_values[cnt]))
                    layer.append(vtext)
                
                cnt = cnt+1
                offset = offset + bar_width + bar_offset
            
            # set x position for heading line
            if not rotate:
                heading_x = width/2 # TODO: adjust
            else:
                heading_x = width/2 # TODO: adjust

                
        elif charttype == "pie":
        #########
        ###PIE###
        #########
            # Iterate all values to draw the different slices
            color = 0
            
            # Create the shadow first (if it should be created):
            if draw_blur:
                shadow = inkex.etree.Element(inkex.addNS("circle", "svg"))
                shadow.set('cx', str(width/2))
                shadow.set('cy', str(height/2))
                shadow.set('r', str(pie_radius))
                shadow.set("style", "filter:url(#filter);fill:#000000")
                layer.append(shadow)
            
            
            # Add a grey background circle with a light stroke
            background = inkex.etree.Element(inkex.addNS("circle", "svg"))
            background.set("cx", str(width/2))
            background.set("cy", str(height/2))
            background.set("r", str(pie_radius))
            background.set("style", "stroke:#ececec;fill:#f9f9f9")
            layer.append(background)
            
            #create value sum in order to divide the slices
            try:
                valuesum = sum(values)
                
            except ValueError:
                valuesum = 0

            if pie_abs:
                valuesum = 100

            num_values = len(values)

            # Set an offsetangle
            offset = 0
            
            # Draw single slices
            for i in range(num_values):
                value = values[i]
                # Calculate the PI-angles for start and end
                angle = (2*3.141592) / valuesum * float(value)
                start = offset
                end = offset + angle
                
                # proper overlapping
                if segment_overlap:
                    if i != num_values-1:
                        end += 0.09 # add a 5° overlap
                    if i == 0:
                        start -= 0.09 # let the first element overlap into the other direction

                #then add the slice
                pieslice = inkex.etree.Element(inkex.addNS("path", "svg"))
                pieslice.set(inkex.addNS('type', 'sodipodi'), 'arc')
                pieslice.set(inkex.addNS('cx', 'sodipodi'), str(width/2))
                pieslice.set(inkex.addNS('cy', 'sodipodi'), str(height/2))
                pieslice.set(inkex.addNS('rx', 'sodipodi'), str(pie_radius))
                pieslice.set(inkex.addNS('ry', 'sodipodi'), str(pie_radius))
                pieslice.set(inkex.addNS('start', 'sodipodi'), str(start))
                pieslice.set(inkex.addNS('end', 'sodipodi'), str(end))
                pieslice.set("style", "fill:"+ colors[color % color_count] + ";stroke:none;fill-opacity:1")
                
                #If text is given, draw short paths and add the text
                if keys_present:
                    path = inkex.etree.Element(inkex.addNS("path", "svg"))
                    path.set("d", "m " 
                                + str((width/2) + pie_radius * math.cos(angle/2 + offset)) + "," 
                                + str((height/2) + pie_radius * math.sin(angle/2 + offset)) + " " 
                                + str((text_offset - 2) * math.cos(angle/2 + offset)) + "," 
                                + str((text_offset - 2) * math.sin(angle/2 + offset)))
                    
                    path.set("style", "fill:none;stroke:" 
                                    + font_color + ";stroke-width:" + str(stroke_width) 
                                    + "px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1")
                    layer.append(path)
                    text = inkex.etree.Element(inkex.addNS('text', 'svg'))
                    text.set("x", str((width/2) + (pie_radius + text_offset) * math.cos(angle/2 + offset)))
                    text.set("y", str((height/2) + (pie_radius + text_offset) * math.sin(angle/2 + offset) + font_size/3))
                    textstyle = "font-size:" + str(font_size) \
                                + "px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:" \
                                + font + ";-inkscape-font-specification:Bitstream Charter;fill:" + font_color 
                    # check if it is right or left of the Pie
                    if math.cos(angle/2 + offset) > 0:
                        text.set("style", textstyle)
                    else:
                        text.set("style", textstyle + ";text-align:end;text-anchor:end")
                    text.text = keys[cnt]
                    if show_values:
                        text.text = text.text + "(" + str(values[cnt])

                        if pie_abs:
                            text.text = text.text + " %"
                        
                        text.text = text.text + ")"
                    
                    cnt = cnt + 1
                    layer.append(text)
                
                # increase the rotation-offset and the colorcycle-position
                offset = offset + angle
                color = (color + 1) % 8
                
                # append the objects to the extension-layer
                layer.append(pieslice)
                
            # set x position for heading line
            heading_x = width/2 - pie_radius # TODO: adjust
                
        elif charttype == "stbar":
        #################
        ###STACKED BAR###
        #################
            # Iterate over all values to draw the different slices
            color = 0
            
            #create value sum in order to divide the bars
            try:
                valuesum = sum(values)
            except ValueError:
                valuesum = 0.0

            for value in values:
                valuesum = valuesum + float(value)
            
            # Init offset
            offset = 0
            
            if draw_blur:
                # Create rectangle element
                shadow = inkex.etree.Element(inkex.addNS("rect", "svg"))
                # Set chart position to center of document.
                if not rotate:
                    shadow.set('x', str(width/2))
                    shadow.set('y', str(height/2 - bar_height/2)) 
                else:
                    shadow.set('x', str(width/2))
                    shadow.set('y', str(height/2))
                # Set rectangle properties
                if not rotate:
                    shadow.set("width", str(bar_width))
                    shadow.set("height", str(bar_height/2))
                else:
                    shadow.set("width",str(bar_height/2))
                    shadow.set("height", str(bar_width))
                # Set shadow blur (connect to filter object in xml path)
                shadow.set("style", "filter:url(#filter)")
                layer.append(shadow)
            
            i = 0
            # Draw Single bars
            for value in values:
                
                # Calculate the individual heights normalized on 100units
                normedvalue = (bar_height / valuesum) * float(value)
                
                # Create rectangle element
                rect = inkex.etree.Element(inkex.addNS('rect', 'svg'))
                
                # Set chart position to center of document.
                if not rotate:
                    rect.set('x', str(width / 2 ))
                    rect.set('y', str(height / 2 - offset - normedvalue))
                else:
                    rect.set('x', str(width / 2 + offset ))
                    rect.set('y', str(height / 2 ))
                # Set rectangle properties
                if not rotate:
                    rect.set("width", str(bar_width))
                    rect.set("height", str(normedvalue))
                else:
                    rect.set("height", str(bar_width))
                    rect.set("width", str(normedvalue))
                rect.set("style", "fill:" + colors[color % color_count])
                
                #If text is given, draw short paths and add the text
                # TODO: apply overlap workaround for visible gaps in between
                if keys_present:
                    if not rotate:
                        path = inkex.etree.Element(inkex.addNS("path", "svg"))
                        path.set("d","m " + str((width + bar_width)/2) + ","
                                    + str(height/2 - offset - (normedvalue / 2)) + " "
                                    + str(bar_width/2 + text_offset) + ",0")
                        path.set("style", "fill:none;stroke:" + font_color
                                        + ";stroke-width:" + str(stroke_width)
                                        + "px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1")
                        layer.append(path)
                        text = inkex.etree.Element(inkex.addNS('text', 'svg'))
                        text.set("x", str(width/2 + bar_width + text_offset + 1))
                        text.set("y", str(height/ 2 - offset + font_size/3 - (normedvalue/2)))
                        text.set("style", "font-size:" + str(font_size)
                                 + "px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:"
                                 + font + ";-inkscape-font-specification:Bitstream Charter;fill:" + font_color)
                        text.text = keys[cnt]
                        cnt = cnt + 1
                        layer.append(text)
                    else:
                        path = inkex.etree.Element(inkex.addNS("path", "svg"))
                        path.set("d","m " + str((width)/2 + offset + normedvalue/2) + ","
                                    + str(height / 2 + bar_width/2) + " 0," 
                                    + str(bar_width/2 + (font_size * i) + text_offset)) #line
                        path.set("style", "fill:none;stroke:" + font_color
                                 + ";stroke-width:" + str(stroke_width)
                                 + "px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1")
                        layer.append(path)
                        text = inkex.etree.Element(inkex.addNS('text', 'svg'))
                        text.set("x", str((width)/2 + offset + normedvalue/2 - font_size/3))
                        text.set("y", str((height/2) + bar_width + (font_size * (i + 1)) + text_offset))
                        text.set("style", "font-size:" + str(font_size) 
                                        + "px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:"
                                        + font + ";-inkscape-font-specification:Bitstream Charter;fill:" + font_color)
                        text.text = keys[color]
                        layer.append(text)
                
                # Increase Offset and Color
                offset = offset + normedvalue
                color = (color + 1) % 8
                
                # Draw rectangle
                layer.append(rect)
                i += 1
            
            # set x position for heading line
            if not rotate:
                heading_x = width/2 + offset + normedvalue # TODO: adjust
            else:
                heading_x = width/2 + offset + normedvalue # TODO: adjust
                
        if headings and input_type == "\"file\"":
            headingtext = inkex.etree.Element(inkex.addNS('text', 'svg'))
            headingtext.set("y", str(height/2 + heading_offset))
            headingtext.set("x", str(heading_x))
            headingtext.set("style", "font-size:" + str(font_size + 4)\
                    + "px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:"\
                    + font + ";-inkscape-font-specification:Bitstream Charter;text-align:end;text-anchor:end;fill:"\
                    + font_color)

            headingtext.text = heading
            layer.append(headingtext)
    
    def getUnittouu(self, param):
        try:
            return inkex.unittouu(param)
        except AttributeError:
            return self.unittouu(param)

if __name__ == '__main__':
    # Create effect instance and apply it.
    effect = NiceChart()
    effect.affect()