rsyntaxtree 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ #==========================
5
+ # error_message.rb
6
+ #==========================
7
+ #
8
+ # Takes an error message and drow an image file of the very message
9
+ #
10
+ # This file is part of RSyntaxTree, which is a ruby port of Andre Eisenbach's
11
+ # excellent program phpSyntaxTree.
12
+ #
13
+ # Copyright (c) 2007-2009 Yoichiro Hasebe <yohasebe@gmail.com>
14
+ # Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
15
+ #
16
+ # This program is free software; you can redistribute it and/or modify
17
+ # it under the terms of the GNU General Public License as published by
18
+ # the Free Software Foundation; either version 2 of the License, or
19
+ # (at your option) any later version.
20
+ #
21
+ # This program is distributed in the hope that it will be useful,
22
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
23
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
+ # GNU General Public License for more details.
25
+ #
26
+ # You should have received a copy of the GNU General Public License
27
+ # along with this program; if not, write to the Free Software
28
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29
+
30
+ require 'imgutils'
31
+
32
+ class ErrorMessage
33
+
34
+ def initialize(text, font, font_size, filename, format)
35
+
36
+ @text = text
37
+ @font = font
38
+ @font_size = font_size
39
+ @filename = filename
40
+ @format = format
41
+
42
+ metrics = img_get_txt_metrics(text, font, font_size, true)
43
+
44
+ @im = Image.new(metrics.width, metrics.height)
45
+ @gc = Draw.new
46
+ @gc.font = font
47
+ @gc.pointsize = font_size
48
+ @gc.stroke("transparent")
49
+ @gc.fill("black")
50
+ @gc.gravity(CenterGravity)
51
+ @gc.text(0, 0, text)
52
+ end
53
+
54
+ def draw
55
+ @gc.draw(@im)
56
+ end
57
+
58
+ def save
59
+ @gc.draw(@im)
60
+ @im.write(@filename + "." + @format)
61
+ end
62
+
63
+ end
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ #==========================
5
+ # imgutils.rb
6
+ #==========================
7
+ #
8
+ # Image utility functions to inspect text font metrics
9
+ #
10
+ # This file is part of RSyntaxTree, which is a ruby port of Andre Eisenbach's
11
+ # excellent program phpSyntaxTree.
12
+ #
13
+ # Copyright (c) 2007-2009 Yoichiro Hasebe <yohasebe@gmail.com>
14
+ # Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
15
+ #
16
+ # This program is free software; you can redistribute it and/or modify
17
+ # it under the terms of the GNU General Public License as published by
18
+ # the Free Software Foundation; either version 2 of the License, or
19
+ # (at your option) any later version.
20
+ #
21
+ # This program is distributed in the hope that it will be useful,
22
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
23
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
+ # GNU General Public License for more details.
25
+ #
26
+ # You should have received a copy of the GNU General Public License
27
+ # along with this program; if not, write to the Free Software
28
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29
+
30
+ require 'rubygems'
31
+ require 'RMagick'
32
+ include Magick
33
+
34
+ def img_get_txt_metrics(text, font, font_size, multiline)
35
+
36
+ background = Image.new(500, 250)
37
+
38
+ gc = Draw.new
39
+ gc.annotate(background, 0, 0, 0, 0, text) do |gc|
40
+ gc.font = font
41
+ gc.pointsize = font_size
42
+ gc.gravity = CenterGravity
43
+ gc.stroke = 'none'
44
+ end
45
+
46
+ if multiline
47
+ metrics = gc.get_multiline_type_metrics(background, text)
48
+ else
49
+ metrics = gc.get_type_metrics(background, text)
50
+ end
51
+
52
+ return metrics
53
+ end
54
+
55
+ def img_get_txt_width(text, font = "Verdana", font_size = 10, multibyte = false)
56
+
57
+ metrics = img_get_txt_metrics(text, font, font_size, multibyte)
58
+ x = metrics.width
59
+ return x
60
+
61
+ end
62
+
63
+ def img_get_txt_height(text, font = "Verdana", font_size = 10, multibyte = false)
64
+
65
+ metrics = img_get_txt_metrics(text, font, font_size, multibyte)
66
+ y = metrics.height
67
+ return y
68
+
69
+ end
70
+
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ #==========================
5
+ # string_parser.rb
6
+ #==========================
7
+ #
8
+ # Parses a phrase into leafs and nodes and store the result in an element list
9
+ # (see element_list.rb)
10
+ #
11
+ # This file is part of RSyntaxTree, which is a ruby port of Andre Eisenbach's
12
+ # excellent program phpSyntaxTree.
13
+ #
14
+ # Copyright (c) 2007-2009 Yoichiro Hasebe <yohasebe@gmail.com>
15
+ # Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
16
+ #
17
+ # This program is free software; you can redistribute it and/or modify
18
+ # it under the terms of the GNU General Public License as published by
19
+ # the Free Software Foundation; either version 2 of the License, or
20
+ # (at your option) any later version.
21
+ #
22
+ # This program is distributed in the hope that it will be useful,
23
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
24
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
+ # GNU General Public License for more details.
26
+ #
27
+ # You should have received a copy of the GNU General Public License
28
+ # along with this program; if not, write to the Free Software
29
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30
+
31
+ require 'elementlist'
32
+ require 'element'
33
+
34
+ def escape_high_ascii(string)
35
+ html = ""
36
+ string.length.times do |i|
37
+ ch = string[i]
38
+ if(ch < 127)
39
+ html += ch.chr
40
+ else
41
+ html += sprintf("&#%d;", ch)
42
+ end
43
+ end
44
+ html
45
+ end
46
+
47
+ class StringParser
48
+
49
+ attr_accessor :data, :elist, :pos, :id, :level, :tncnt
50
+ def initialize(str)
51
+ # Clean up the data a little to make processing easier
52
+ string = str.gsub(/\t/, "")
53
+ string.gsub!(/\s+/, " ")
54
+ string.gsub!(/\] \[/, "][")
55
+ string.gsub!(/ \[/, "[")
56
+
57
+ @data = string # Store it for later...
58
+ @elist = ElementList.new # Initialize internal element list
59
+ @pos = 0 # Position in the sentence
60
+ @id = 1 # ID for the next element
61
+ @level = 0 # Level in the diagram
62
+ @tncnt = Hash.new # Node type counts
63
+ end
64
+
65
+ # caution: quick and dirty solution
66
+ def valid?
67
+ if(@data.length < 1)
68
+ return false
69
+ end
70
+ if /\A\s*\[.+ .+\]\s*\z/ !~ @data
71
+ return false
72
+ end
73
+
74
+ text = @data.strip
75
+ text_r = text.split(//)
76
+ open_br, close_br = [], []
77
+ text_r.each do |chr|
78
+ if chr == '['
79
+ open_br.push(chr)
80
+ elsif chr == ']'
81
+ close_br.push(chr)
82
+ if open_br.length < close_br.length
83
+ break
84
+ end
85
+ end
86
+ end
87
+
88
+ return false unless open_br.length == close_br.length
89
+ make_tree(0)
90
+ return false if @tncnt.empty?
91
+ @tncnt.each do |key, value|
92
+ return false if key == ""
93
+ end
94
+ return true
95
+ end
96
+
97
+
98
+ def parse
99
+ make_tree(0);
100
+ end
101
+
102
+ def get_elementlist
103
+ @elist;
104
+ end
105
+
106
+ def auto_subscript
107
+ elements = @elist.get_elements
108
+ tmpcnt = Hash.new
109
+ elements.each do |element|
110
+ if(element.type == ETYPE_NODE)
111
+ count = 1
112
+ content = element.content
113
+
114
+ if @tncnt[content]
115
+ count = @tncnt[content]
116
+ end
117
+
118
+ if(count > 1)
119
+ if tmpcnt[content]
120
+ tmpcnt[content] += 1
121
+ else
122
+ tmpcnt[content] = 1
123
+ end
124
+
125
+ element.content += ("_" + tmpcnt[content].to_s)
126
+ end
127
+
128
+ end
129
+ end
130
+ @tncnt
131
+ end
132
+
133
+ def count_node(name)
134
+ name = name.strip
135
+ if @tncnt[name]
136
+ @tncnt[name] += 1
137
+ else
138
+ @tncnt[name] = 1
139
+ end
140
+ end
141
+
142
+ def get_next_token
143
+ data = @data.split(//)
144
+ gottoken = false
145
+ token = ""
146
+ i = 0
147
+
148
+ if((@pos + 1) >= data.length)
149
+ return ""
150
+ end
151
+
152
+ while(((@pos + i) < data.length) && !gottoken)
153
+ ch = data[@pos + i];
154
+ case ch
155
+ when "["
156
+ if(i > 0)
157
+ gottoken = true
158
+ else
159
+ token += ch
160
+ end
161
+ when "]"
162
+ if(i == 0 )
163
+ token += ch
164
+ end
165
+ gottoken = true
166
+ when /[\n\r]/
167
+ gottoken = false # same as do nothing
168
+ else
169
+ token += ch
170
+ end
171
+ i += 1
172
+ end
173
+
174
+ if(i > 1)
175
+ @pos += (i - 1)
176
+ else
177
+ @pos += 1
178
+ end
179
+ return token
180
+ end
181
+
182
+ def make_tree(parent)
183
+ token = get_next_token.strip
184
+ parts = Array.new
185
+
186
+ while(token != "" && token != "]" )
187
+ token_r = token.split(//)
188
+ case token_r[0]
189
+ when "["
190
+ tl = token_r.length
191
+ token_r = token_r[1, tl - 1]
192
+ spaceat = token_r.index(" ")
193
+ newparent = -1
194
+
195
+ if spaceat
196
+ parts[0] = token_r[0, spaceat].join
197
+ tl =token_r.length
198
+ parts[1] = token_r[spaceat, tl - spaceat].join
199
+ element = Element.new(@id, parent, parts[0], @level)
200
+ @id += 1
201
+ @elist.add(element)
202
+ newparent = element.id
203
+ count_node(parts[0])
204
+
205
+ element = Element.new(@id, @id - 1, parts[1], @level + 1 )
206
+ @id += 1
207
+ @elist.add(element)
208
+ else
209
+ element = Element.new(@id, parent, token_r.join, @level)
210
+ @id += 1
211
+ newparent = element.id
212
+ @elist.add(element)
213
+ count_node(token_r.join)
214
+ end
215
+
216
+ @level += 1
217
+ make_tree(newparent)
218
+
219
+ else
220
+ if token.strip != ""
221
+ element = Element.new(@id, parent, token, @level)
222
+ @id += 1
223
+ @elist.add(element)
224
+ count_node(token)
225
+ end
226
+ end
227
+
228
+ token = get_next_token
229
+ end
230
+ @level -= 1
231
+ end
232
+ end
233
+
@@ -0,0 +1,447 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ #==========================
5
+ # svg_graph.rb
6
+ #==========================
7
+ #
8
+ # Parses an element list into an SVG tree.
9
+ #
10
+ # This file is part of RSyntaxTree, which is a ruby port of Andre Eisenbach's
11
+ # excellent program phpSyntaxTree.
12
+ #
13
+ # Copyright (c) 2007-2009 Yoichiro Hasebe <yohasebe@gmail.com>
14
+ # Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
15
+ #
16
+ # This program is free software; you can redistribute it and/or modify
17
+ # it under the terms of the GNU General Public License as published by
18
+ # the Free Software Foundation; either version 2 of the License, or
19
+ # (at your option) any later version.
20
+ #
21
+ # This program is distributed in the hope that it will be useful,
22
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
23
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
+ # GNU General Public License for more details.
25
+ #
26
+ # You should have received a copy of the GNU General Public License
27
+ # along with this program; if not, write to the Free Software
28
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29
+
30
+ require 'tmpdir'
31
+ require 'rvg/rvg'
32
+ include Magick
33
+
34
+ # constant variables are already set in tree_graph.rb
35
+
36
+ class SVGGraph
37
+
38
+ def initialize(e_list, symmetrize = true, color = true, leafstyle = "triangle",
39
+ font = "Helvetica", font_size = 10, simple = false)
40
+
41
+ # Store parameters
42
+ @e_list = e_list
43
+ @font = font
44
+ @font_size = font_size
45
+ @leafstyle = leafstyle
46
+ @symmetrize = symmetrize
47
+ # Element dimensions
48
+ @e_width = E_WIDTH
49
+
50
+
51
+ # Calculate image dimensions
52
+ @e_height = @font_size + E_PADD * 2
53
+ h = @e_list.get_level_height
54
+ w = calc_level_width(0)
55
+ w_px = w + B_SIDE * 2
56
+ h_px = h * @e_height + (h-1) * (V_SPACE + @font_size) + B_TOPBOT * 2
57
+ @height = h_px
58
+ @width = w_px
59
+
60
+
61
+ # Initialize the image and colors
62
+ @col_bg = "none"
63
+ @col_fg = "black"
64
+ @col_line = "black"
65
+
66
+ if color
67
+ @col_node = "blue"
68
+ @col_leaf = "green"
69
+ @col_trace = "red"
70
+ else
71
+ @col_node = "black"
72
+ @col_leaf = "black"
73
+ @col_trace = "black"
74
+ end
75
+
76
+ @line_styles = "<line style='stroke:black; stroke-width:1;' x1='X1' y1='Y1' x2='X2' y2='Y2' />\n"
77
+ @polygon_styles = "<polygon style='fill: white; stroke: black; stroke-width:1;' points='X1 Y1 X2 Y2 X3 Y3' />\n"
78
+
79
+ @text_styles = "<text style='fill: COLOR; font-size: FONT_SIZEpx;' x='X_VALUE' y='Y_VALUE'>CONTENT</text>\n"
80
+
81
+ @tree_data = String.new
82
+ end
83
+
84
+ def svg_data
85
+ parse_list
86
+ header =<<EOD
87
+ <?xml version="1.0" standalone="no"?>
88
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
89
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
90
+ <svg width="#{@width}" height="#{@height}" version="1.1" xmlns="http://www.w3.org/2000/svg">
91
+ EOD
92
+
93
+ footer = "</svg>"
94
+ # File.open(filename, "w") do |f|
95
+ # f.write header
96
+ # f.write @tree_data
97
+ # f.write footer
98
+ # end
99
+ header + @tree_data + footer
100
+ end
101
+
102
+ # Create a temporary file and returns only its filename
103
+ def create_tempf(basename, ext, num = 10)
104
+ flags = File::RDWR | File::CREAT | File::EXCL
105
+ tfname = ""
106
+ num.times do |i|
107
+ begin
108
+ tfname = "#{basename}.#{$$}.#{i}.#{ext}"
109
+ tfile = File.open(tfname, flags, 0600)
110
+ rescue Errno::EEXIST
111
+ next
112
+ end
113
+ tfile.close
114
+ return tfname
115
+ end
116
+ end
117
+
118
+ :private
119
+
120
+ # Add the element into the tree (draw it)
121
+ def draw_element(x, y, w, string, type)
122
+
123
+ # Calculate element dimensions and position
124
+ if (type == ETYPE_LEAF) and @leafstyle == "nothing"
125
+ top = row2px(y - 1) + (@font_size * 1.5)
126
+ else
127
+ top = row2px(y)
128
+ end
129
+ left = x + B_SIDE
130
+ bottom = top + @e_height
131
+ right = left + w
132
+
133
+ # Split the string into the main part and the
134
+ # subscript part of the element (if any)
135
+ main = string
136
+ sub = ""
137
+
138
+ sub_size = (@font_size * 0.7 )
139
+ parts = string.split("_", 2)
140
+
141
+ if(parts.length > 1 )
142
+ main = parts[0]
143
+ sub = parts[1].gsub(/_/, " ")
144
+ end
145
+
146
+ # Calculate text size for the main and the
147
+ # subscript part of the element
148
+ main_width = img_get_txt_width(main, @font, @font_size)
149
+
150
+ if sub != ""
151
+ sub_width = img_get_txt_width(sub.to_s, @font, sub_size)
152
+ else
153
+ sub_width = 0
154
+ end
155
+
156
+ # Center text in the element
157
+ txt_width = main_width + sub_width
158
+ txt_pos = left + (right - left) / 2 - txt_width / 2
159
+
160
+ # Select apropriate color
161
+ if(type == ETYPE_LEAF)
162
+ col = @col_leaf
163
+ else
164
+ col = @col_node
165
+ end
166
+
167
+ if(main[0].chr == "<" && main[-1].chr == ">")
168
+ col = @col_trace
169
+ end
170
+
171
+ # Draw main text
172
+ main_data = @text_styles.sub(/COLOR/, col)
173
+ main_data = main_data.sub(/FONT_SIZE/, @font_size.to_s)
174
+ main_x = txt_pos
175
+ main_y = top + @e_height - E_PADD
176
+ main_data = main_data.sub(/X_VALUE/, main_x.to_s)
177
+ main_data = main_data.sub(/Y_VALUE/, main_y.to_s)
178
+ @tree_data += main_data.sub(/CONTENT/, main)
179
+
180
+ # Draw subscript text
181
+ sub_data = @text_styles.sub(/COLOR/, col)
182
+ sub_data = sub_data.sub(/FONT_SIZE/, @font_size.to_s)
183
+ sub_x = main_x + main_width + (sub_size/8)
184
+ sub_y = top + (@e_height - E_PADD + sub_size / 2).ceil
185
+ if (sub.length > 0 )
186
+ sub_data = sub_data.sub(/X_VALUE/, sub_x.ceil.to_s)
187
+ sub_data = sub_data.sub(/Y_VALUE/, sub_y.ceil.to_s)
188
+ @tree_data += sub_data.sub(/CONTENT/, sub)
189
+ end
190
+
191
+ end
192
+
193
+ # Draw a line between child/parent elements
194
+ def line_to_parent(fromX, fromY, fromW, toX, toW)
195
+
196
+ if (fromY == 0 )
197
+ return
198
+ end
199
+
200
+ fromTop = row2px(fromY)
201
+ fromLeft = (fromX + fromW / 2 + B_SIDE)
202
+ toBot = (row2px(fromY - 1 ) + @e_height)
203
+ toLeft = (toX + toW / 2 + B_SIDE)
204
+
205
+ line_data = @line_styles.sub(/X1/, fromLeft.ceil.to_s.to_s)
206
+ line_data = line_data.sub(/Y1/, fromTop.ceil.to_s.to_s)
207
+ line_data = line_data.sub(/X2/, toLeft.ceil.to_s.to_s)
208
+ @tree_data += line_data.sub(/Y2/, toBot.ceil.to_s.to_s)
209
+
210
+ end
211
+
212
+ # Draw a triangle between child/parent elements
213
+ def triangle_to_parent(fromX, fromY, fromW, toW, textW)
214
+ if (fromY == 0)
215
+ return
216
+ end
217
+
218
+ toX = fromX
219
+
220
+ fromTop = row2px(fromY)
221
+
222
+ fromCenter = (fromX + fromW / 2 + B_SIDE)
223
+ fromLeft1 = (fromCenter + textW / 2)
224
+ fromLeft2 = (fromCenter - textW / 2)
225
+
226
+ toBot = (row2px(fromY - 1) + @e_height)
227
+ toLeft = (toX + toW / 2 + B_SIDE)
228
+ polygon_data = @polygon_styles.sub(/X1/, fromLeft1.ceil.to_s)
229
+ polygon_data = polygon_data.sub(/Y1/, fromTop.ceil.to_s)
230
+ polygon_data = polygon_data.sub(/X2/, fromLeft2.ceil.to_s)
231
+ polygon_data = polygon_data.sub(/Y2/, fromTop.ceil.to_s)
232
+ polygon_data = polygon_data.sub(/X3/, toLeft.ceil.to_s)
233
+ @tree_data += polygon_data.sub(/Y3/, toBot.ceil.to_s)
234
+ end
235
+
236
+ # If a node element text is wider than the sum of it's
237
+ # child elements, then the child elements need to
238
+ # be resized to even out the space. This function
239
+ # recurses down the a child tree and sizes the
240
+ # children appropriately.
241
+ def fix_child_size(id, current, target)
242
+ children = @e_list.get_children(id)
243
+ @e_list.set_element_width(id, target)
244
+
245
+ if(children.length > 0 )
246
+ delta = target - current
247
+ target_delta = delta / children.length
248
+
249
+ children.each do |child|
250
+ child_width = @e_list.get_element_width(child)
251
+ fix_child_size(child, child_width, child_width + target_delta)
252
+ end
253
+ end
254
+ end
255
+
256
+ # Calculate the width of the element. If the element is
257
+ # a node, the calculation will be performed recursively
258
+ # for all child elements.
259
+ def calc_element_width(e)
260
+ w = 0
261
+
262
+ children = @e_list.get_children(e.id)
263
+
264
+ if(children.length == 0)
265
+ w = img_get_txt_width(e.content, @font, @font_size) + @font_size
266
+ else
267
+ children.each do |child|
268
+ child_e = @e_list.get_id(child)
269
+ w += calc_element_width(child_e)
270
+ end
271
+
272
+ tw = img_get_txt_width(e.content, @font, @font_size) + @font_size
273
+ if(tw > w)
274
+ fix_child_size(e.id, w, tw)
275
+ w = tw
276
+ end
277
+ end
278
+
279
+ @e_list.set_element_width(e.id, w)
280
+ return w
281
+ end
282
+
283
+ # Calculate the width of all elements in a certain level
284
+ def calc_level_width(level)
285
+ w = 0
286
+ e = @e_list.get_first
287
+ while e
288
+ if(e.level == level)
289
+ w += calc_element_width(e)
290
+ end
291
+ e = @e_list.get_next
292
+ end
293
+
294
+ return w
295
+ end
296
+
297
+ def calc_children_width(id)
298
+ left = 0
299
+ right = 0
300
+ c_list = @e_list.get_children(id)
301
+ return nil if c_list.empty?
302
+
303
+ c_list.each do |c|
304
+ left = c.indent if indent == 0 or left > c.indent
305
+ end
306
+ c_list.each do |c|
307
+ right = c.indent + e.width if c.indent + c.width > right
308
+ end
309
+ return [left, right]
310
+ end
311
+
312
+ def get_children_indent(id)
313
+ calc_children_width(id)[0]
314
+ end
315
+
316
+ def get_children_width(id)
317
+ calc_children_width(id)[1] - get_children_indent(id)
318
+ end
319
+
320
+ # Parse the elements in the list top to bottom and
321
+ # draw the elements into the image.
322
+ # As we it iterate through the levels, the element
323
+ # indentation is calculated.
324
+ def parse_list
325
+
326
+ # Calc element list recursively....
327
+ e_arr = @e_list.get_elements
328
+
329
+ h = @e_list.get_level_height
330
+
331
+ h.times do |i|
332
+ x = 0
333
+ e_arr.each do |j|
334
+
335
+ if (j.level == i)
336
+ cw = @e_list.get_element_width(j.id)
337
+ parent_indent = @e_list.get_indent(j.parent)
338
+
339
+ if (x < parent_indent)
340
+ x = parent_indent
341
+ end
342
+
343
+ @e_list.set_indent(j.id, x)
344
+ if !@symmetrize
345
+ draw_element(x, i, cw, j.content, j.type)
346
+ if(j.parent != 0 )
347
+ words = j.content.split(" ")
348
+ unless @leafstyle == "nothing" && ETYPE_LEAF == j.type
349
+ if (@leafstyle == "triangle" && ETYPE_LEAF == j.type && x == parent_indent && words.length > 1)
350
+ txt_width = img_get_txt_width(j.content, @font, @font_size)
351
+ triangle_to_parent(x, i, cw, @e_list.get_element_width(j.parent), txt_width)
352
+ else
353
+ line_to_parent(x, i, cw, @e_list.get_indent(j.parent), @e_list.get_element_width(j.parent))
354
+ end
355
+ end
356
+ end
357
+ end
358
+ x += cw
359
+ end
360
+ end
361
+ end
362
+ return true if !@symmetrize
363
+ h.times do |i|
364
+ curlevel = h - i - 1
365
+ indent = 0
366
+ e_arr.each_with_index do |j, idx|
367
+ if (j.level == curlevel)
368
+ # Draw a line to the parent element
369
+ children = @e_list.get_children(j.id)
370
+
371
+ tw = img_get_txt_width(j.content, @font, @font_size)
372
+ if children.length > 1
373
+ left, right = -1, -1
374
+ children.each do |child|
375
+ k = @e_list.get_id(child)
376
+ kw = img_get_txt_width(k.content, @font, @font_size)
377
+ left = k.indent + kw / 2 if k.indent + kw / 2 < left or left == -1
378
+ right = k.indent + kw / 2 if k.indent + kw / 2 > right
379
+ end
380
+ draw_element(left, curlevel, right - left, j.content, j.type)
381
+ @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
382
+
383
+ children.each do |child|
384
+ k = @e_list.get_id(child)
385
+ words = k.content.split(" ")
386
+ dw = img_get_txt_width(k.content, @font, @font_size)
387
+ unless @leafstyle == "nothing" && ETYPE_LEAF == k.type
388
+ if (@leafstyle == "triangle" && ETYPE_LEAF == k.type && k.indent == j.indent && words.length > 1)
389
+ txt_width = img_get_txt_width(k.content, @font, @font_size)
390
+ triangle_to_parent(k.indent, curlevel + 1, dw, tw, txt_width)
391
+ else
392
+ line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
393
+ end
394
+ end
395
+ end
396
+
397
+ else
398
+ unless children.empty?
399
+ k = @e_list.get_id(children[0])
400
+ kw = img_get_txt_width(k.content, @font, @font_size)
401
+ left = k.indent
402
+ right = k.indent + kw
403
+ draw_element(left, curlevel, right - left, j.content, j.type)
404
+ @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
405
+ else
406
+ parent = @e_list.get_id(j.parent)
407
+ pw = img_get_txt_width(parent.content, @font, @font_size)
408
+ pleft = parent.indent
409
+ pright = pleft + pw
410
+ left = j.indent
411
+ right = left + tw
412
+ if pw > tw
413
+ left = pleft
414
+ right = pright
415
+ end
416
+ draw_element(left, curlevel, right - left, j.content, j.type)
417
+ @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
418
+ end
419
+
420
+ unless children.empty?
421
+ k = @e_list.get_id(children[0])
422
+ words = k.content.split(" ")
423
+ dw = img_get_txt_width(k.content, @font, @font_size)
424
+ unless @leafstyle == "nothing" && ETYPE_LEAF == k.type
425
+ if (@leafstyle == "triangle" && ETYPE_LEAF == k.type && words.length > 1)
426
+ txt_width = img_get_txt_width(k.content, @font, @font_size)
427
+ triangle_to_parent(k.indent, curlevel + 1, dw,
428
+ @e_list.get_element_width(k.parent), txt_width)
429
+ else
430
+ line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
431
+ end
432
+ end
433
+ end
434
+ end
435
+ end
436
+ end
437
+ end
438
+ end
439
+
440
+
441
+ # Calculate top position from row (level)
442
+ def row2px(row)
443
+ B_TOPBOT + @e_height * row + (V_SPACE + @font_size) * row
444
+ end
445
+
446
+ end
447
+