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")
35 def split_lines(self, node):
36 """Returns a list of lines"""
38 lines = []
39 count = 1
41 for n in node:
42 if not (n.tag == inkex.addNS("flowPara", "svg") or n.tag == inkex.addNS("tspan", "svg")):
43 if n.tag == inkex.addNS("textPath", "svg"):
44 inkex.debug("This type of text element isn't supported. First remove text from path.")
45 break
46 else:
47 continue
49 text = inkex.etree.Element(inkex.addNS("text", "svg"), node.attrib)
51 #handling flowed text nodes
52 if node.tag == inkex.addNS("flowRoot", "svg"):
53 try:
54 from simplestyle import parseStyle
55 fontsize = parseStyle(node.get("style"))["font-size"]
56 except:
57 fontsize = "12px"
58 fs = inkex.unittouu(fontsize)
60 #selects the flowRegion's child (svg:rect) to get @X and @Y
61 id = node.get("id")
62 flowref = self.xpathSingle('/svg:svg//*[@id="%s"]/svg:flowRegion[1]' % id)[0]
64 if flowref.tag == inkex.addNS("rect", "svg"):
65 text.set("x", flowref.get("x"))
66 text.set("y", str(float(flowref.get("y")) + fs * count))
67 count += 1
68 else:
69 inkex.debug("This type of text element isn't supported. First unflow text.")
70 break
72 #now let's convert flowPara into tspan
73 tspan = inkex.etree.Element(inkex.addNS("tspan", "svg"))
74 tspan.set(inkex.addNS("role","sodipodi"), "line")
75 tspan.text = n.text
76 text.append(tspan)
78 else:
79 from copy import copy
80 x = n.get("x") or node.get("x")
81 y = n.get("y") or node.get("y")
83 text.set("x", x)
84 text.set("y", y)
85 text.append(copy(n))
87 lines.append(text)
89 return lines
92 def split_words(self, node):
93 """Returns a list of words"""
95 words = []
97 #Function to recursively extract text
98 def plain_str(elem):
99 words = []
100 if elem.text:
101 words.append(elem.text)
102 for n in elem:
103 words.extend(plain_str(n))
104 if n.tail:
105 words.append(n.tail)
106 return words
108 #if text has more than one line, iterates through elements
109 lines = self.split_lines(node)
110 if not lines:
111 return words
113 for line in lines:
114 #gets the position of text node
115 x = float(line.get("x"))
116 y = line.get("y")
118 #gets the font size. if element doesn't have a style attribute, it assumes font-size = 12px
119 try:
120 from simplestyle import parseStyle
121 fontsize = parseStyle(line.get("style"))["font-size"]
122 except:
123 fontsize = "12px"
124 fs = inkex.unittouu(fontsize)
126 #extract and returns a list of words
127 words_list = "".join(plain_str(line)).split()
128 prev_len = 0
130 #creates new text nodes for each string in words_list
131 for word in words_list:
132 tspan = inkex.etree.Element(inkex.addNS("tspan", "svg"))
133 tspan.text = word
135 text = inkex.etree.Element(inkex.addNS("text", "svg"), line.attrib)
136 tspan.set(inkex.addNS("role","sodipodi"), "line")
138 #positioning new text elements
139 x = x + prev_len * fs
140 prev_len = len(word)
141 text.set("x", str(x))
142 text.set("y", str(y))
144 text.append(tspan)
145 words.append(text)
147 return words
150 def split_letters(self, node):
151 """Returns a list of letters"""
153 letters = []
155 words = self.split_words(node)
156 if not words:
157 return letters
159 for word in words:
161 x = float(word.get("x"))
162 y = word.get("y")
164 #gets the font size. If element doesn't have a style attribute, it assumes font-size = 12px
165 try:
166 import simplestyle
167 fontsize = simplestyle.parseStyle(word.get("style"))["font-size"]
168 except:
169 fontsize = "12px"
170 fs = inkex.unittouu(fontsize)
172 #for each letter in element string
173 for letter in word[0].text:
174 tspan = inkex.etree.Element(inkex.addNS("tspan", "svg"))
175 tspan.text = letter
177 text = inkex.etree.Element(inkex.addNS("text", "svg"), node.attrib)
178 text.set("x", str(x))
179 text.set("y", str(y))
180 x += fs
182 text.append(tspan)
183 letters.append(text)
184 return letters
187 def effect(self):
188 """Applies the effect"""
190 split_type = self.options.split_type
191 preserve = self.options.preserve
193 #checks if the selected elements are text nodes
194 for id, node in self.selected.iteritems():
195 if not (node.tag == inkex.addNS("text", "svg") or node.tag == inkex.addNS("flowRoot", "svg")):
196 inkex.debug("Please select only text elements.")
197 break
198 else:
199 if split_type == "line":
200 nodes = self.split_lines(node)
201 elif split_type == "word":
202 nodes = self.split_words(node)
203 elif split_type == "letter":
204 nodes = self.split_letters(node)
206 for n in nodes:
207 node.getparent().append(n)
209 #preserve original element
210 if not preserve and nodes:
211 parent = node.getparent()
212 parent.remove(node)
214 b = Split()
215 b.affect()