1 #!/usr/bin/env python
2 '''
3 Copyright (C) 2009 Karlisson Bezerra, contato@nerdson.com
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 '''
20 import inkex
22 class Split(inkex.Effect):
23 def __init__(self):
24 inkex.Effect.__init__(self)
25 self.OptionParser.add_option("-s", "--splittype",
26 action="store", type="string",
27 dest="split_type", default="word",
28 help="type of split")
29 self.OptionParser.add_option("-p", "--preserve",
30 action="store", type="inkbool",
31 dest="preserve", default="True",
32 help="Preserve original")
33 self.OptionParser.add_option("--tab",
34 action="store", type="string",
35 dest="tab",
36 help="The selected UI-tab when OK was pressed")
38 def split_lines(self, node):
39 """Returns a list of lines"""
41 lines = []
42 count = 1
44 for n in node:
45 if not (n.tag == inkex.addNS("flowPara", "svg") or n.tag == inkex.addNS("tspan", "svg")):
46 if n.tag == inkex.addNS("textPath", "svg"):
47 inkex.debug("This type of text element isn't supported. First remove text from path.")
48 break
49 else:
50 continue
52 text = inkex.etree.Element(inkex.addNS("text", "svg"), node.attrib)
54 #handling flowed text nodes
55 if node.tag == inkex.addNS("flowRoot", "svg"):
56 try:
57 from simplestyle import parseStyle
58 fontsize = parseStyle(node.get("style"))["font-size"]
59 except:
60 fontsize = "12px"
61 fs = inkex.unittouu(fontsize)
63 #selects the flowRegion's child (svg:rect) to get @X and @Y
64 id = node.get("id")
65 flowref = self.xpathSingle('/svg:svg//*[@id="%s"]/svg:flowRegion[1]' % id)[0]
67 if flowref.tag == inkex.addNS("rect", "svg"):
68 text.set("x", flowref.get("x"))
69 text.set("y", str(float(flowref.get("y")) + fs * count))
70 count += 1
71 else:
72 inkex.debug("This type of text element isn't supported. First unflow text.")
73 break
75 #now let's convert flowPara into tspan
76 tspan = inkex.etree.Element(inkex.addNS("tspan", "svg"))
77 tspan.set(inkex.addNS("role","sodipodi"), "line")
78 tspan.text = n.text
79 text.append(tspan)
81 else:
82 from copy import copy
83 x = n.get("x") or node.get("x")
84 y = n.get("y") or node.get("y")
86 text.set("x", x)
87 text.set("y", y)
88 text.append(copy(n))
90 lines.append(text)
92 return lines
95 def split_words(self, node):
96 """Returns a list of words"""
98 words = []
100 #Function to recursively extract text
101 def plain_str(elem):
102 words = []
103 if elem.text:
104 words.append(elem.text)
105 for n in elem:
106 words.extend(plain_str(n))
107 if n.tail:
108 words.append(n.tail)
109 return words
111 #if text has more than one line, iterates through elements
112 lines = self.split_lines(node)
113 if not lines:
114 return words
116 for line in lines:
117 #gets the position of text node
118 x = float(line.get("x"))
119 y = line.get("y")
121 #gets the font size. if element doesn't have a style attribute, it assumes font-size = 12px
122 try:
123 from simplestyle import parseStyle
124 fontsize = parseStyle(line.get("style"))["font-size"]
125 except:
126 fontsize = "12px"
127 fs = inkex.unittouu(fontsize)
129 #extract and returns a list of words
130 words_list = "".join(plain_str(line)).split()
131 prev_len = 0
133 #creates new text nodes for each string in words_list
134 for word in words_list:
135 tspan = inkex.etree.Element(inkex.addNS("tspan", "svg"))
136 tspan.text = word
138 text = inkex.etree.Element(inkex.addNS("text", "svg"), line.attrib)
139 tspan.set(inkex.addNS("role","sodipodi"), "line")
141 #positioning new text elements
142 x = x + prev_len * fs
143 prev_len = len(word)
144 text.set("x", str(x))
145 text.set("y", str(y))
147 text.append(tspan)
148 words.append(text)
150 return words
153 def split_letters(self, node):
154 """Returns a list of letters"""
156 letters = []
158 words = self.split_words(node)
159 if not words:
160 return letters
162 for word in words:
164 x = float(word.get("x"))
165 y = word.get("y")
167 #gets the font size. If element doesn't have a style attribute, it assumes font-size = 12px
168 try:
169 import simplestyle
170 fontsize = simplestyle.parseStyle(word.get("style"))["font-size"]
171 except:
172 fontsize = "12px"
173 fs = inkex.unittouu(fontsize)
175 #for each letter in element string
176 for letter in word[0].text:
177 tspan = inkex.etree.Element(inkex.addNS("tspan", "svg"))
178 tspan.text = letter
180 text = inkex.etree.Element(inkex.addNS("text", "svg"), node.attrib)
181 text.set("x", str(x))
182 text.set("y", str(y))
183 x += fs
185 text.append(tspan)
186 letters.append(text)
187 return letters
190 def effect(self):
191 """Applies the effect"""
193 split_type = self.options.split_type
194 preserve = self.options.preserve
196 #checks if the selected elements are text nodes
197 for id, node in self.selected.iteritems():
198 if not (node.tag == inkex.addNS("text", "svg") or node.tag == inkex.addNS("flowRoot", "svg")):
199 inkex.debug("Please select only text elements.")
200 break
201 else:
202 if split_type == "line":
203 nodes = self.split_lines(node)
204 elif split_type == "word":
205 nodes = self.split_words(node)
206 elif split_type == "letter":
207 nodes = self.split_letters(node)
209 for n in nodes:
210 node.getparent().append(n)
212 #preserve original element
213 if not preserve and nodes:
214 parent = node.getparent()
215 parent.remove(node)
217 b = Split()
218 b.affect()