#!/usr/bin/env python 
'''
Copyright (C) 2009 Karlisson Bezerra, contato@nerdson.com

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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
'''

import inkex

class Split(inkex.Effect):
    def __init__(self):
        inkex.Effect.__init__(self)
        self.OptionParser.add_option("-s", "--splittype", 
                        action="store", type="string", 
                        dest="split_type", default="word", 
                        help="type of split")
        self.OptionParser.add_option("-p", "--preserve", 
                        action="store", type="inkbool", 
                        dest="preserve", default="True", 
                        help="Preserve original")


    def split_lines(self, node):
        """Returns a list of lines"""

        lines = []
        count = 1
        
        for n in node:
            if not (n.tag == inkex.addNS("flowPara", "svg") or n.tag == inkex.addNS("tspan", "svg")):
                if n.tag == inkex.addNS("textPath", "svg"):
                    inkex.debug("This type of text element isn't supported. First remove text from path.")
                    break
                else:
                    continue
           
            text = inkex.etree.Element(inkex.addNS("text", "svg"), node.attrib)
            
            #handling flowed text nodes
            if node.tag == inkex.addNS("flowRoot", "svg"):
                try:
                    from simplestyle import parseStyle
                    fontsize = parseStyle(node.get("style"))["font-size"]
                except:
                    fontsize = "12px"
                fs = inkex.unittouu(fontsize)
                
                #selects the flowRegion's child (svg:rect) to get @X and @Y
                id = node.get("id")
                flowref = self.xpathSingle('/svg:svg//*[@id="%s"]/svg:flowRegion[1]' % id)[0]
                
                if flowref.tag == inkex.addNS("rect", "svg"):
                    text.set("x", flowref.get("x"))
                    text.set("y", str(float(flowref.get("y")) + fs * count))
                    count += 1
                else:
                    inkex.debug("This type of text element isn't supported. First unflow text.")
                    break
                
                #now let's convert flowPara into tspan
                tspan = inkex.etree.Element(inkex.addNS("tspan", "svg"))
                tspan.set(inkex.addNS("role","sodipodi"), "line")
                tspan.text = n.text
                text.append(tspan)
            
            else:
                from copy import copy
                x = n.get("x") or node.get("x")
                y = n.get("y") or node.get("y")

                text.set("x", x)
                text.set("y", y)
                text.append(copy(n))
            
            lines.append(text)

        return lines


    def split_words(self, node):
        """Returns a list of words"""
        
        words = []

        #Function to recursively extract text
        def plain_str(elem):
            words = []
            if elem.text:
                words.append(elem.text)
            for n in elem:
                words.extend(plain_str(n))
                if n.tail:
                    words.append(n.tail)
            return words
        
        #if text has more than one line, iterates through elements
        lines = self.split_lines(node)
        if not lines:
            return words

        for line in lines:
            #gets the position of text node
            x = float(line.get("x"))
            y = line.get("y")
            
            #gets the font size. if element doesn't have a style attribute, it assumes font-size = 12px
            try:
                from simplestyle import parseStyle
                fontsize = parseStyle(line.get("style"))["font-size"]
            except:
                fontsize = "12px"
            fs = inkex.unittouu(fontsize)

            #extract and returns a list of words
            words_list = "".join(plain_str(line)).split()
            prev_len = 0
            
            #creates new text nodes for each string in words_list
            for word in words_list:
                tspan = inkex.etree.Element(inkex.addNS("tspan", "svg"))
                tspan.text = word
                
                text = inkex.etree.Element(inkex.addNS("text", "svg"), line.attrib)
                tspan.set(inkex.addNS("role","sodipodi"), "line")
                
                #positioning new text elements
                x = x + prev_len * fs
                prev_len = len(word)
                text.set("x", str(x))
                text.set("y", str(y))
                
                text.append(tspan)
                words.append(text)
        
        return words


    def split_letters(self, node):
        """Returns a list of letters"""

        letters = []
        
        words = self.split_words(node)
        if not words:
            return letters

        for word in words:
            
            x = float(word.get("x"))
            y = word.get("y")
           
            #gets the font size. If element doesn't have a style attribute, it assumes font-size = 12px
            try:
                import simplestyle
                fontsize = simplestyle.parseStyle(word.get("style"))["font-size"]
            except:
                fontsize = "12px"
            fs = inkex.unittouu(fontsize)

            #for each letter in element string
            for letter in word[0].text:
                tspan = inkex.etree.Element(inkex.addNS("tspan", "svg"))
                tspan.text = letter

                text = inkex.etree.Element(inkex.addNS("text", "svg"), node.attrib)
                text.set("x", str(x))
                text.set("y", str(y))
                x += fs
                
                text.append(tspan)
                letters.append(text)
        return letters


    def effect(self):
        """Applies the effect"""

        split_type = self.options.split_type
        preserve = self.options.preserve
       
        #checks if the selected elements are text nodes
        for id, node in self.selected.iteritems():
            if not (node.tag == inkex.addNS("text", "svg") or node.tag == inkex.addNS("flowRoot", "svg")):
                inkex.debug("Please select only text elements.")
                break
            else:
                if split_type == "line":
                    nodes = self.split_lines(node)
                elif split_type == "word":
                    nodes = self.split_words(node)
                elif split_type == "letter":
                    nodes = self.split_letters(node)
                
                for n in nodes:
                    node.getparent().append(n)
                        
                #preserve original element
                if not preserve and nodes:
                    parent = node.getparent()
                    parent.remove(node)

b = Split()
b.affect()
