rsyntaxtree 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/Gemfile +4 -0
- data/README.md +51 -0
- data/Rakefile +1 -0
- data/app.rb +55 -0
- data/bin/rsyntaxtree +73 -0
- data/config.ru +7 -0
- data/fonts/DroidSans.ttf +0 -0
- data/fonts/DroidSerif-Regular.ttf +0 -0
- data/fonts/ipagp.ttf +0 -0
- data/fonts/ipamp.ttf +0 -0
- data/fonts/wqy-zenhei.ttf +0 -0
- data/helpers/helpers.rb +22 -0
- data/lib/rsyntaxtree.rb +134 -0
- data/lib/rsyntaxtree/element.rb +58 -0
- data/lib/rsyntaxtree/elementlist.rb +137 -0
- data/lib/rsyntaxtree/error_message.rb +63 -0
- data/lib/rsyntaxtree/imgutils.rb +70 -0
- data/lib/rsyntaxtree/string_parser.rb +233 -0
- data/lib/rsyntaxtree/svg_graph.rb +447 -0
- data/lib/rsyntaxtree/tree_graph.rb +433 -0
- data/lib/rsyntaxtree/version.rb +4 -0
- data/public/bootstrap/css/bootstrap-responsive.css +581 -0
- data/public/bootstrap/css/bootstrap-responsive.min.css +4 -0
- data/public/bootstrap/css/bootstrap.css +3496 -0
- data/public/bootstrap/css/bootstrap.min.css +632 -0
- data/public/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/public/bootstrap/img/glyphicons-halflings.png +0 -0
- data/public/bootstrap/js/bootstrap.js +1731 -0
- data/public/bootstrap/js/bootstrap.min.js +7 -0
- data/public/bootstrap/js/jquery.js +4 -0
- data/public/css/rsyntaxtree.css +5 -0
- data/public/js/rsyntaxtree.js +137 -0
- data/rsyntaxtree.gemspec +29 -0
- data/views/footer.haml +3 -0
- data/views/index.haml +132 -0
- data/views/layout.haml +42 -0
- data/views/navbar.haml +8 -0
- metadata +139 -0
@@ -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
|
+
|