rsyntaxtree 0.7.1 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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