rsyntaxtree 0.7.1 → 0.7.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,24 +10,8 @@
10
10
  # This file is part of RSyntaxTree, which is a ruby port of Andre Eisenbach's
11
11
  # excellent program phpSyntaxTree.
12
12
  #
13
- # Copyright (c) 2007-2018 Yoichiro Hasebe <yohasebe@gmail.com>
13
+ # Copyright (c) 2007-2021 Yoichiro Hasebe <yohasebe@gmail.com>
14
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
15
 
32
16
  class ErrorMessage
33
17
 
@@ -38,7 +22,7 @@ class ErrorMessage
38
22
  @font_size = font_size
39
23
  @filename = filename
40
24
  @format = format
41
-
25
+
42
26
  metrics = img_get_txt_metrics(text, font, font_size, true)
43
27
 
44
28
  @im = Image.new(metrics.width, metrics.height)
@@ -51,6 +35,27 @@ class ErrorMessage
51
35
  @gc.text(0, 0, text)
52
36
  end
53
37
 
38
+ def img_get_txt_metrics(text, font, font_size, multiline)
39
+
40
+ background = Image.new(500, 250)
41
+
42
+ gc = Draw.new
43
+ gc.annotate(background, 0, 0, 0, 0, text) do |gc|
44
+ gc.font = font
45
+ gc.pointsize = font_size
46
+ gc.gravity = CenterGravity
47
+ gc.stroke = 'none'
48
+ end
49
+
50
+ if multiline
51
+ metrics = gc.get_multiline_type_metrics(background, text)
52
+ else
53
+ metrics = gc.get_type_metrics(background, text)
54
+ end
55
+
56
+ return metrics
57
+ end
58
+
54
59
  def draw
55
60
  @gc.draw(@im)
56
61
  end
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ #==========================
5
+ # graph.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-2021 Yoichiro Hasebe <yohasebe@gmail.com>
14
+ # Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
15
+
16
+ require 'rmagick'
17
+ include Magick
18
+
19
+ class Graph
20
+
21
+ def initialize(e_list, metrics, symmetrize, color, leafstyle, multibyte, font, font_size)
22
+
23
+ # Set class-specific parameters beforehand in subclass
24
+
25
+ # Store parameters
26
+ @e_list = e_list
27
+ @m = metrics
28
+ @multibyte = multibyte
29
+ @leafstyle = leafstyle
30
+ @symmetrize = symmetrize
31
+
32
+ # Calculate image dimensions
33
+ @e_height = font_size + @m[:e_padd] * 2
34
+ h = @e_list.get_level_height
35
+ w = calc_level_width(0)
36
+ @width = w + @m[:b_side] * 2
37
+ @height = h * @e_height + (h-1) * (@m[:v_space] + font_size) + @m[:b_topbot] * 2
38
+
39
+ # Initialize the image and colors
40
+ @col_bg = "none"
41
+ @col_fg = "black"
42
+ @col_line = "black"
43
+
44
+ if color
45
+ @col_node = "blue"
46
+ @col_leaf = "green"
47
+ @col_trace = "red"
48
+ else
49
+ @col_node = "black"
50
+ @col_leaf = "black"
51
+ @col_trace = "black"
52
+ end
53
+
54
+ @main_height = img_get_txt_height("l", font, font_size)
55
+ @sub_size = (font_size * SUBSCRIPT_CONST)
56
+ @sub_space_width = img_get_txt_width("l", font, @sub_size)
57
+ end
58
+
59
+ def img_get_txt_metrics(text, font, font_size, multiline)
60
+
61
+ background = Image.new(500, 250)
62
+
63
+ gc = Draw.new
64
+ gc.annotate(background, 0, 0, 0, 0, text) do |gc|
65
+ gc.font = font
66
+ gc.pointsize = font_size
67
+ gc.gravity = CenterGravity
68
+ gc.stroke = 'none'
69
+ end
70
+
71
+ if multiline
72
+ metrics = gc.get_multiline_type_metrics(background, text)
73
+ else
74
+ metrics = gc.get_type_metrics(background, text)
75
+ end
76
+
77
+ return metrics
78
+ end
79
+
80
+ # Calculate the width of the element. If the element is
81
+ # a node, the calculation will be performed recursively
82
+ # for all child elements.
83
+ def calc_element_width(e)
84
+ w = 0
85
+
86
+ children = @e_list.get_children(e.id)
87
+
88
+ if(children.length == 0)
89
+ w = img_get_txt_width(e.content, @font, @font_size) + @font_size
90
+ else
91
+ children.each do |child|
92
+ child_e = @e_list.get_id(child)
93
+ w += calc_element_width(child_e)
94
+ end
95
+
96
+ tw = img_get_txt_width(e.content, @font, @font_size) + @font_size
97
+ if(tw > w)
98
+ fix_child_size(e.id, w, tw)
99
+ w = tw
100
+ end
101
+ end
102
+
103
+ @e_list.set_element_width(e.id, w)
104
+ return w
105
+ end
106
+
107
+ # Calculate the width of all elements in a certain level
108
+ def calc_level_width(level)
109
+ w = 0
110
+ e = @e_list.get_first
111
+ while e
112
+ if(e.level == level)
113
+ w += calc_element_width(e)
114
+ end
115
+ e = @e_list.get_next
116
+ end
117
+ return w
118
+ end
119
+
120
+ def calc_children_width(id)
121
+ left = 0
122
+ right = 0
123
+ c_list = @e_list.get_children(id)
124
+ return nil if c_list.empty?
125
+
126
+ c_list.each do |c|
127
+ left = c.indent if indent == 0 or left > c.indent
128
+ end
129
+ c_list.each do |c|
130
+ right = c.indent + e.width if c.indent + c.width > right
131
+ end
132
+ return [left, right]
133
+ end
134
+
135
+ def get_children_indent(id)
136
+ calc_children_width(id)[0]
137
+ end
138
+
139
+ def get_children_width(id)
140
+ calc_children_width(id)[1] - get_children_indent(id)
141
+ end
142
+
143
+ # Parse the elements in the list top to bottom and
144
+ # draw the elements into the image.
145
+ # As we it iterate through the levels, the element
146
+ # indentation is calculated.
147
+ def parse_list
148
+
149
+ # Calc element list recursively....
150
+ e_arr = @e_list.get_elements
151
+
152
+ h = @e_list.get_level_height
153
+ h.times do |i|
154
+ x = 0
155
+ e_arr.each do |j|
156
+
157
+ if (j.level == i)
158
+ cw = @e_list.get_element_width(j.id)
159
+ parent_indent = @e_list.get_indent(j.parent)
160
+ if (x < parent_indent)
161
+ x = parent_indent
162
+ end
163
+ @e_list.set_indent(j.id, x)
164
+
165
+ if !@symmetrize
166
+ draw_element(x, i, cw, j.content, j.type)
167
+ if(j.parent != 0 )
168
+ words = j.content.split(" ")
169
+ unless @leafstyle == "nothing" && ETYPE_LEAF == j.type
170
+ if (@leafstyle == "triangle" && ETYPE_LEAF == j.type && x == parent_indent && words.length > 0)
171
+ txt_width = img_get_txt_width(j.content, @font, @font_size)
172
+ triangle_to_parent(x, i, cw, txt_width, @symmetrize)
173
+ elsif (@leafstyle == "auto" && ETYPE_LEAF == j.type && x == parent_indent)
174
+ if words.length > 1 || j.triangle
175
+ txt_width = img_get_txt_width(j.content, @font, @font_size)
176
+ triangle_to_parent(x, i, cw, txt_width, @symmetrize)
177
+ else
178
+ line_to_parent(x, i, cw, @e_list.get_indent(j.parent), @e_list.get_element_width(j.parent))
179
+ end
180
+ else
181
+ line_to_parent(x, i, cw, @e_list.get_indent(j.parent), @e_list.get_element_width(j.parent))
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ x += cw
188
+ end
189
+ end
190
+ end
191
+ return true if !@symmetrize
192
+ h.times do |i|
193
+ curlevel = h - i - 1
194
+ indent = 0
195
+ e_arr.each_with_index do |j, idx|
196
+ if (j.level == curlevel)
197
+ # Draw a line to the parent element
198
+ children = @e_list.get_children(j.id)
199
+
200
+ tw = img_get_txt_width(j.content, @font, @font_size)
201
+ if children.length > 1
202
+ left, right = -1, -1
203
+ children.each do |child|
204
+ k = @e_list.get_id(child)
205
+ kw = img_get_txt_width(k.content, @font, @font_size)
206
+ left = k.indent + kw / 2 if k.indent + kw / 2 < left or left == -1
207
+ right = k.indent + kw / 2 if k.indent + kw / 2 > right
208
+ end
209
+ draw_element(left, curlevel, right - left, j.content, j.type)
210
+ @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
211
+
212
+ children.each do |child|
213
+ k = @e_list.get_id(child)
214
+ words = k.content.split(" ")
215
+ dw = img_get_txt_width(k.content, @font, @font_size)
216
+ unless @leafstyle == "nothing" && ETYPE_LEAF == k.type
217
+ if (@leafstyle == "triangle" && ETYPE_LEAF == k.type && k.indent == j.indent && words.length > 0)
218
+ txt_width = img_get_txt_width(k.content, @font, @font_size)
219
+ triangle_to_parent(k.indent, curlevel + 1, dw, txt_width)
220
+ elsif (@leafstyle == "auto" && ETYPE_LEAF == k.type && k.indent == j.indent)
221
+ if words.length > 1 || k.triangle
222
+ txt_width = img_get_txt_width(k.content, @font, @font_size)
223
+ triangle_to_parent(k.indent, curlevel + 1, dw, txt_width)
224
+ else
225
+ line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
226
+ end
227
+ else
228
+ line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
229
+ end
230
+ end
231
+ end
232
+
233
+ else
234
+ unless children.empty?
235
+ k = @e_list.get_id(children[0])
236
+ kw = img_get_txt_width(k.content, @font, @font_size)
237
+ left = k.indent
238
+ right = k.indent + kw
239
+ draw_element(left, curlevel, right - left, j.content, j.type)
240
+ @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
241
+ else
242
+ parent = @e_list.get_id(j.parent)
243
+ pw = img_get_txt_width(parent.content, @font, @font_size)
244
+ pleft = parent.indent
245
+ pright = pleft + pw
246
+ left = j.indent
247
+ right = left + tw
248
+ if pw > tw
249
+ left = pleft
250
+ right = pright
251
+ end
252
+ draw_element(left, curlevel, right - left, j.content, j.type)
253
+ @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
254
+ end
255
+
256
+ unless children.empty?
257
+ k = @e_list.get_id(children[0])
258
+ words = k.content.split(" ")
259
+ dw = img_get_txt_width(k.content, @font, @font_size)
260
+ unless @leafstyle == "nothing" && ETYPE_LEAF == k.type
261
+ if (@leafstyle == "triangle" && ETYPE_LEAF == k.type && words.length > 0)
262
+ txt_width = img_get_txt_width(k.content, @font, @font_size)
263
+ triangle_to_parent(k.indent, curlevel + 1, dw, txt_width)
264
+ elsif (@leafstyle == "auto" && ETYPE_LEAF == k.type)
265
+ if words.length > 1 || k.triangle
266
+ txt_width = img_get_txt_width(k.content, @font, @font_size)
267
+ triangle_to_parent(k.indent, curlevel + 1, dw, txt_width)
268
+ else
269
+ line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
270
+ end
271
+ else
272
+ line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
278
+ end
279
+ end
280
+ end
281
+
282
+ # Calculate top position from row (level)
283
+ def row2px(row)
284
+ @m[:b_topbot] + @e_height * row + (@m[:v_space] + @font_size) * row
285
+ end
286
+
287
+ def get_txt_only(text)
288
+ text = text.strip
289
+ if /\A([\+\-\=\*]+).+/ =~ text
290
+ prefix = $1
291
+ prefix_l = Regexp.escape(prefix)
292
+ prefix_r = Regexp.escape(prefix.reverse)
293
+ if /\A#{prefix_l}(.+)#{prefix_r}\z/ =~ text
294
+ return $1
295
+ end
296
+ end
297
+ return text
298
+ end
299
+
300
+ def img_get_txt_height(text, font, font_size, multiline = false)
301
+ metrics = img_get_txt_metrics(text, font, font_size, multiline)
302
+ y = metrics.height
303
+ return y
304
+ end
305
+ end
@@ -11,22 +11,8 @@
11
11
  # This file is part of RSyntaxTree, which is a ruby port of Andre Eisenbach's
12
12
  # excellent program phpSyntaxTree.
13
13
  #
14
- # Copyright (c) 2007-2018 Yoichiro Hasebe <yohasebe@gmail.com>
14
+ # Copyright (c) 2007-2021 Yoichiro Hasebe <yohasebe@gmail.com>
15
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
16
 
31
17
  require 'elementlist'
32
18
  require 'element'
@@ -53,7 +39,7 @@ class StringParser
53
39
  string.gsub!(/\s+/, " ")
54
40
  string.gsub!(/\] \[/, "][")
55
41
  string.gsub!(/ \[/, "[")
56
-
42
+
57
43
  @data = string # Store it for later...
58
44
  @elist = ElementList.new # Initialize internal element list
59
45
  @pos = 0 # Position in the sentence
@@ -61,18 +47,18 @@ class StringParser
61
47
  @level = 0 # Level in the diagram
62
48
  @tncnt = Hash.new # Node type counts
63
49
  end
64
-
50
+
65
51
  # caution: quick and dirty solution
66
52
  def valid?
67
53
  if(@data.length < 1)
68
54
  return false
69
55
  end
70
56
 
71
- if /\A\s*\[.+ .+\]\s*\z/ !~ @data
57
+ if /\[\s*\]/m =~ @data
72
58
  return false
73
59
  end
74
60
 
75
- if /\[\_ / =~ @data
61
+ if /\[\_/ =~ @data
76
62
  return false
77
63
  end
78
64
 
@@ -96,15 +82,14 @@ class StringParser
96
82
  end
97
83
 
98
84
  return false unless open_br.length == close_br.length
99
- make_tree(0)
100
- return false if @tncnt.empty?
101
- @tncnt.each do |key, value|
102
- return false if key == ""
103
- end
85
+ # make_tree(0)
86
+ # return false if @tncnt.empty?
87
+ # @tncnt.each do |key, value|
88
+ # return false if key == ""
89
+ # end
104
90
  return true
105
91
  end
106
-
107
-
92
+
108
93
  def parse
109
94
  make_tree(0);
110
95
  end
@@ -112,7 +97,7 @@ class StringParser
112
97
  def get_elementlist
113
98
  @elist;
114
99
  end
115
-
100
+
116
101
  def auto_subscript
117
102
  elements = @elist.get_elements
118
103
  tmpcnt = Hash.new
@@ -120,18 +105,18 @@ class StringParser
120
105
  if(element.type == ETYPE_NODE)
121
106
  count = 1
122
107
  content = element.content
123
-
108
+
124
109
  if @tncnt[content]
125
110
  count = @tncnt[content]
126
111
  end
127
-
112
+
128
113
  if(count > 1)
129
114
  if tmpcnt[content]
130
115
  tmpcnt[content] += 1
131
116
  else
132
117
  tmpcnt[content] = 1
133
118
  end
134
-
119
+
135
120
  element.content += ("_" + tmpcnt[content].to_s)
136
121
  end
137
122
 
@@ -148,7 +133,7 @@ class StringParser
148
133
  @tncnt[name] = 1
149
134
  end
150
135
  end
151
-
136
+
152
137
  def get_next_token
153
138
  data = @data.split(//)
154
139
  gottoken = false
@@ -158,7 +143,7 @@ class StringParser
158
143
  if((@pos + 1) >= data.length)
159
144
  return ""
160
145
  end
161
-
146
+
162
147
  escape = false
163
148
  while(((@pos + i) < data.length) && !gottoken)
164
149
  ch = data[@pos + i];
@@ -202,11 +187,11 @@ class StringParser
202
187
  end
203
188
  return token
204
189
  end
205
-
190
+
206
191
  def make_tree(parent)
207
192
  token = get_next_token.strip
208
193
  parts = Array.new
209
-
194
+
210
195
  while(token != "" && token != "]" )
211
196
  token_r = token.split(//)
212
197
  case token_r[0]
@@ -218,23 +203,28 @@ class StringParser
218
203
 
219
204
  if spaceat
220
205
  parts[0] = token_r[0, spaceat].join
206
+ parts[0] = parts[0].gsub("<>", " ")
207
+
221
208
  tl =token_r.length
222
209
  parts[1] = token_r[spaceat, tl - spaceat].join
210
+ parts[1] = parts[1].gsub("<>", " ")
211
+
223
212
  element = Element.new(@id, parent, parts[0], @level)
224
213
  @id += 1
225
214
  @elist.add(element)
226
215
  newparent = element.id
227
216
  count_node(parts[0])
228
-
217
+
229
218
  element = Element.new(@id, @id - 1, parts[1], @level + 1 )
230
219
  @id += 1
231
220
  @elist.add(element)
232
221
  else
233
- element = Element.new(@id, parent, token_r.join, @level)
222
+ joined = token_r.join.gsub("<>", " ")
223
+ element = Element.new(@id, parent, joined, @level)
234
224
  @id += 1
235
225
  newparent = element.id
236
226
  @elist.add(element)
237
- count_node(token_r.join)
227
+ count_node(joined)
238
228
  end
239
229
 
240
230
  @level += 1
@@ -248,7 +238,7 @@ class StringParser
248
238
  count_node(token)
249
239
  end
250
240
  end
251
-
241
+
252
242
  token = get_next_token
253
243
  end
254
244
  @level -= 1