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,84 +10,39 @@
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
- require 'elementlist'
32
- # require 'rubygems'
15
+
16
+ require 'graph'
33
17
  require 'rmagick'
34
18
  include Magick
35
19
 
36
- # Double to make image retina ready
37
- E_WIDTH = 60 * 2 # Element width
38
- E_PADD = 7 * 2 # Element height padding
39
- V_SPACE = 20 * 2
40
- H_SPACE = 10 * 2
41
- B_SIDE = 5 * 2
42
- B_TOPBOT = 5 * 2
43
-
44
- class TreeGraph
45
-
46
- def initialize(e_list, symmetrize = true, color = true, terminal = "auto",
47
- font = "Helvetica", font_size = 10, simple = false, margin = 0)
48
-
49
- # Store parameters (double font size and margin to make image retina ready)
50
- @e_list = e_list
51
- @font = font
52
- @terminal = terminal
53
- @symmetrize = symmetrize
54
- @simple = simple
55
- @font_size = font_size * 2
56
- @margin = margin * 2
57
-
58
- # Element dimensions
59
- @e_width = E_WIDTH
60
-
61
- # Calculate image dimensions
62
- @e_height = @font_size + E_PADD * 2
63
- h = @e_list.get_level_height
64
- w = calc_level_width(0)
65
- w_px = w + B_SIDE * 2
66
- h_px = h * @e_height + (h-1) * (V_SPACE + @font_size) + B_TOPBOT * 2
67
- @height = h_px
68
- @width = w_px
20
+ class TreeGraph < Graph
21
+
22
+ def initialize(e_list, metrics, symmetrize, color, leafstyle, multibyte,
23
+ fontstyle, font, font_it, font_bd, font_itbd, font_cjk, font_size,
24
+ margin)
25
+
26
+ # Store class-specific parameters
27
+ @fontstyle = fontstyle
28
+ @font = multibyte ? font_cjk : font
29
+ @font_size = font_size
30
+ @font_it = font_it
31
+ @font_bd = font_bd
32
+ @font_itbd = font_itbd
33
+ @font_cjk = font_cjk
34
+ @margin = margin
35
+
36
+ super(e_list, metrics, symmetrize, color, leafstyle, multibyte, @font, @font_size)
69
37
 
70
38
  # Initialize the image and colors
71
- @im = Image.new(w_px, h_px)
72
- @gc = Draw.new
73
- @gc.font = @font
39
+ @im = Image.new(@width, @height)
40
+ @im.interlace = PlaneInterlace
41
+ @gc = Draw.new
42
+ @gc.font = @font
74
43
  @gc.pointsize(@font_size)
75
-
76
- @col_bg = "none"
77
- @col_fg = "black"
78
- @col_line = "black"
79
-
80
- if color
81
- @col_node = "blue"
82
- @col_leaf = "green"
83
- @col_trace = "red"
84
- else
85
- @col_node = "black"
86
- @col_leaf = "black"
87
- @col_trace = "black"
88
- end
89
44
  end
90
-
45
+
91
46
  def destroy
92
47
  @im.destroy!
93
48
  end
@@ -108,44 +63,112 @@ class TreeGraph
108
63
  draw
109
64
  @im.border!(@margin, @margin, "white")
110
65
  return @im.to_blob do
111
- self.format = fileformat
66
+ self.format = fileformat
67
+ self.interlace = PlaneInterlace
112
68
  end
113
- end
114
-
69
+ end
70
+
115
71
  :private
116
-
72
+
117
73
  # Add the element into the tree (draw it)
118
74
  def draw_element(x, y, w, string, type)
119
- string = string.sub(/\^\z/){""}
75
+ string = string.sub(/\^\z/){""}
120
76
  # Calculate element dimensions and position
121
- if (type == ETYPE_LEAF) and @terminal == "nothing"
77
+ if (type == ETYPE_LEAF) and @leafstyle == "nothing"
122
78
  top = row2px(y - 1) + (@font_size * 1.5)
123
- else
79
+ else
124
80
  top = row2px(y)
125
81
  end
126
- left = x + B_SIDE
82
+ left = x + @m[:b_side]
127
83
  bottom = top + @e_height
128
84
  right = left + w
129
85
 
130
- # Split the string into the main part and the
86
+ # Split the string into the main part and the
131
87
  # subscript part of the element (if any)
132
- main = string
133
- sub = ""
134
-
135
- sub_size = (@font_size * 0.7 )
136
88
  parts = string.split("_", 2)
137
-
138
89
  if(parts.length > 1 )
139
- main = parts[0]
140
- sub = parts[1].gsub(/_/, " ")
90
+ main = parts[0].strip
91
+ sub = parts[1].gsub(/_/, " ").strip
92
+ else
93
+ main = parts[0].strip
94
+ sub = ""
141
95
  end
142
-
143
- # Calculate text size for the main and the
96
+
97
+ if /\A\+(.+)\+\z/ =~ main
98
+ main = $1
99
+ main_decoration = OverlineDecoration
100
+ elsif /\A\-(.+)\-\z/ =~ main
101
+ main = $1
102
+ main_decoration = UnderlineDecoration
103
+ elsif /\A\=(.+)\=\z/ =~ main
104
+ main = $1
105
+ main_decoration = LineThroughDecoration
106
+ else
107
+ main_decoration = NoDecoration
108
+ end
109
+
110
+ if /\A\*\*\*(.+)\*\*\*\z/ =~ main
111
+ main = $1
112
+ if !@multibyte
113
+ main_font = @font_itbd
114
+ end
115
+ elsif /\A\*\*(.+)\*\*\z/ =~ main
116
+ main = $1
117
+ if !@multibyte
118
+ main_font = @font_bd
119
+ end
120
+ elsif /\A\*(.+)\*\z/ =~ main
121
+ main = $1
122
+ if !@multibyte
123
+ main_font = @font_it
124
+ end
125
+ else
126
+ main_font = @font
127
+ end
128
+
129
+ # Calculate text size for the main and the
144
130
  # subscript part of the element
145
- main_width = img_get_txt_width(main, @font, @font_size)
131
+ # symbols for underline/overline removed temporarily
132
+
133
+ main_width = img_get_txt_width(main, main_font, @font_size)
134
+
135
+ if /\A\+(.+)\+\z/ =~ sub
136
+ sub = $1
137
+ sub_decoration = OverlineDecoration
138
+ elsif /\A\-(.+)\-\z/ =~ sub
139
+ sub = $1
140
+ @gc.decorate(UnderlineDecoration)
141
+ sub_decoration = UnderlineDecoration
142
+ elsif /\A\=(.+)\=\z/ =~ sub
143
+ sub = $1
144
+ sub_decoration = LineThroughDecoration
145
+ else
146
+ sub_decoration = NoDecoration
147
+ end
148
+
149
+ sub_font = @font
150
+
151
+ if /\A\*\*\*(.+)\*\*\*\z/ =~ sub
152
+ sub = $1
153
+ if !@multibyte
154
+ sub_font = @font_itbd
155
+ end
156
+ elsif /\A\*\*(.+)\*\*\z/ =~ sub
157
+ sub = $1
158
+ if !@multibyte
159
+ sub_font = @font_bd
160
+ end
161
+ elsif /\A\*(.+)\*\z/ =~ sub
162
+ sub = $1
163
+ if !@multibyte
164
+ sub_font = @font_it
165
+ end
166
+ else
167
+ sub_font = @font
168
+ end
146
169
 
147
170
  if sub != ""
148
- sub_width = img_get_txt_width(sub.to_s, @font, sub_size)
171
+ sub_width = img_get_txt_width(sub.to_s, sub_font, @sub_size)
149
172
  else
150
173
  sub_width = 0
151
174
  end
@@ -154,14 +177,14 @@ class TreeGraph
154
177
  txt_width = main_width + sub_width
155
178
 
156
179
  txt_pos = left + (right - left) / 2 - txt_width / 2
157
-
180
+
158
181
  # Select apropriate color
159
182
  if(type == ETYPE_LEAF)
160
183
  col = @col_leaf
161
184
  else
162
- col = @col_node
185
+ col = @col_node
163
186
  end
164
-
187
+
165
188
  if(main[0].chr == "<" && main[-1].chr == ">")
166
189
  col = @col_trace
167
190
  end
@@ -172,17 +195,22 @@ class TreeGraph
172
195
  # Draw main text
173
196
  @gc.pointsize(@font_size)
174
197
  main_x = txt_pos
175
- main_y = top + @e_height - E_PADD
198
+ main_y = top + @e_height - @m[:e_padd]
199
+
200
+ @gc.interline_spacing = -(@main_height / 3)
201
+ @gc.font(main_font)
202
+ @gc.decorate(main_decoration)
176
203
  @gc.text(main_x.ceil, main_y.ceil, main)
177
-
204
+
178
205
  # Draw subscript text
179
206
  if (sub.length > 0 )
180
- @gc.pointsize(sub_size)
181
- sub_x = txt_pos + main_width + (sub_size/8)
182
- sub_y = top + (@e_height - E_PADD + sub_size / 2)
207
+ @gc.pointsize(@sub_size)
208
+ sub_x = txt_pos + main_width + @sub_space_width
209
+ sub_y = top + (@e_height - @m[:e_padd] + @sub_size / 2)
210
+ @gc.font(sub_font)
211
+ @gc.decorate(sub_decoration)
183
212
  @gc.text(sub_x.ceil, sub_y.ceil, sub)
184
213
  end
185
-
186
214
  end
187
215
 
188
216
  # Draw a line between child/parent elements
@@ -191,11 +219,11 @@ class TreeGraph
191
219
  if (fromY == 0 )
192
220
  return
193
221
  end
194
-
222
+
195
223
  fromTop = row2px(fromY)
196
- fromLeft = (fromX + fromW / 2 + B_SIDE)
224
+ fromLeft = (fromX + fromW / 2 + @m[:b_side])
197
225
  toBot = (row2px(fromY - 1 ) + @e_height)
198
- toLeft = (toX + toW / 2 + B_SIDE)
226
+ toLeft = (toX + toW / 2 + @m[:b_side])
199
227
 
200
228
  @gc.fill("none")
201
229
  @gc.stroke @col_line
@@ -204,27 +232,27 @@ class TreeGraph
204
232
  end
205
233
 
206
234
  # Draw a triangle between child/parent elements
207
- def triangle_to_parent(fromX, fromY, fromW, toX, textW, symmetrize = true)
235
+ def triangle_to_parent(fromX, fromY, fromW, textW, symmetrize = true)
208
236
  if (fromY == 0)
209
237
  return
210
238
  end
211
-
239
+
212
240
  toX = fromX
213
- fromCenter = (fromX + fromW / 2 + B_SIDE)
214
-
241
+ fromCenter = (fromX + fromW / 2 + @m[:b_side])
242
+
215
243
  fromTop = row2px(fromY).ceil
216
244
  fromLeft1 = (fromCenter + textW / 2).ceil
217
245
  fromLeft2 = (fromCenter - textW / 2).ceil
218
246
  toBot = (row2px(fromY - 1) + @e_height)
219
247
  if symmetrize
220
- toLeft = (toX + textW / 2 + B_SIDE)
248
+ toLeft = (toX + textW / 2 + @m[:b_side])
221
249
  else
222
- toLeft = (toX + textW / 2 + B_SIDE * 3)
250
+ toLeft = (toX + textW / 2 + @m[:b_side] * 3)
223
251
  end
224
-
252
+
225
253
  @gc.fill("none")
226
254
  @gc.stroke @col_line
227
- @gc.stroke_width 1
255
+ @gc.stroke_width 1
228
256
  @gc.line(fromLeft1, fromTop, toLeft, toBot)
229
257
  @gc.line(fromLeft2, fromTop, toLeft, toBot)
230
258
  @gc.line(fromLeft1, fromTop, fromLeft2, fromTop)
@@ -239,9 +267,9 @@ class TreeGraph
239
267
  children = @e_list.get_children(id)
240
268
  @e_list.set_element_width(id, target)
241
269
 
242
- if(children.length > 0 )
270
+ if(children.length > 0 )
243
271
  delta = target - current
244
- target_delta = delta / children.length
272
+ target_delta = delta / children.length
245
273
 
246
274
  children.each do |child|
247
275
  child_width = @e_list.get_element_width(child)
@@ -250,212 +278,17 @@ class TreeGraph
250
278
  end
251
279
  end
252
280
 
253
- # Calculate the width of the element. If the element is
254
- # a node, the calculation will be performed recursively
255
- # for all child elements.
256
- def calc_element_width(e)
257
- w = 0
258
-
259
- children = @e_list.get_children(e.id)
260
-
261
- if(children.length == 0)
262
- w = img_get_txt_width(e.content, @font, @font_size) + @font_size
263
- else
264
- children.each do |child|
265
- child_e = @e_list.get_id(child)
266
- w += calc_element_width(child_e)
267
- end
268
-
269
- tw = img_get_txt_width(e.content, @font, @font_size) + @font_size
270
- if(tw > w)
271
- fix_child_size(e.id, w, tw)
272
- w = tw
273
- end
274
- end
275
-
276
- @e_list.set_element_width(e.id, w)
277
- return w
278
- end
279
-
280
- # Calculate the width of all elements in a certain level
281
- def calc_level_width(l)
282
- w = 0
283
- e = @e_list.get_first
284
- while e
285
- if(e.level == l)
286
- w += calc_element_width(e)
287
- end
288
- e = @e_list.get_next
289
- end
290
-
291
- return w
292
- end
293
-
294
- def calc_children_width(id)
295
- left = 0
296
- right = 0
297
- c_list = @e_list.get_children(id)
298
- return nil if c_list.empty?
299
-
300
- c_list.each do |c|
301
- left = c.indent if indent == 0 or left > c.indent
281
+ def img_get_txt_width(text, font, font_size, multiline = false)
282
+ parts = text.split("_", 2)
283
+ main_before = parts[0].strip
284
+ sub = parts[1]
285
+ main = get_txt_only(main_before)
286
+ main_metrics = img_get_txt_metrics(main, font, font_size, multiline)
287
+ width = main_metrics.width
288
+ if sub
289
+ sub_metrics = img_get_txt_metrics(sub.strip, font, font_size * SUBSCRIPT_CONST, multiline)
290
+ width += sub_metrics.width
302
291
  end
303
- c_list.each do |c|
304
- right = c.indent + e.width if c.indent + c.width > right
305
- end
306
- return [left, right]
307
- end
308
-
309
- def get_children_indent(id)
310
- calc_children_width(id)[0]
311
- end
312
-
313
- def get_children_width(id)
314
- calc_children_width(id)[1] - get_children_indent(id)
315
- end
316
-
317
- # Parse the elements in the list top to bottom and
318
- # draw the elements into the image.
319
- # As we it iterate through the levels, the element
320
- # indentation is calculated.
321
- def parse_list
322
-
323
- # Calc element list recursively....
324
- e_arr = @e_list.get_elements
325
-
326
- h = @e_list.get_level_height
327
- h.times do |i|
328
- x = 0
329
- e_arr.each do |j|
330
-
331
- if (j.level == i)
332
- cw = @e_list.get_element_width(j.id)
333
- parent_indent = @e_list.get_indent(j.parent)
334
- if (x < parent_indent)
335
- x = parent_indent
336
- end
337
- @e_list.set_indent(j.id, x)
338
-
339
- if !@symmetrize
340
- draw_element(x, i, cw, j.content, j.type)
341
- if(j.parent != 0 )
342
- words = j.content.split(" ")
343
- unless @terminal == "nothing" && ETYPE_LEAF == j.type
344
- if (@terminal == "triangle" && ETYPE_LEAF == j.type && x == parent_indent && words.length > 0)
345
- txt_width = img_get_txt_width(j.content, @font, @font_size)
346
- triangle_to_parent(x, i, cw, @e_list.get_element_width(j.parent), txt_width)
347
- elsif (@terminal == "auto" && ETYPE_LEAF == j.type && x == parent_indent)
348
- if words.length > 1 || j.triangle
349
- txt_width = img_get_txt_width(j.content, @font, @font_size)
350
- triangle_to_parent(x, i, cw, @e_list.get_element_width(j.parent), txt_width, @symmetrize)
351
- else
352
- line_to_parent(x, i, cw, @e_list.get_indent(j.parent), @e_list.get_element_width(j.parent))
353
- end
354
- else
355
- line_to_parent(x, i, cw, @e_list.get_indent(j.parent), @e_list.get_element_width(j.parent))
356
- end
357
- end
358
- end
359
- end
360
-
361
- x += cw
362
- end
363
- end
364
- end
365
- return true if !@symmetrize
366
- h.times do |i|
367
- curlevel = h - i - 1
368
- indent = 0
369
- e_arr.each_with_index do |j, idx|
370
- if (j.level == curlevel)
371
- # Draw a line to the parent element
372
- children = @e_list.get_children(j.id)
373
-
374
- tw = img_get_txt_width(j.content, @font, @font_size)
375
- if children.length > 1
376
- left, right = -1, -1
377
- children.each do |child|
378
- k = @e_list.get_id(child)
379
- kw = img_get_txt_width(k.content, @font, @font_size)
380
- left = k.indent + kw / 2 if k.indent + kw / 2 < left or left == -1
381
- right = k.indent + kw / 2 if k.indent + kw / 2 > right
382
- end
383
- draw_element(left, curlevel, right - left, j.content, j.type)
384
- @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
385
-
386
- children.each do |child|
387
- k = @e_list.get_id(child)
388
- words = k.content.split(" ")
389
- dw = img_get_txt_width(k.content, @font, @font_size)
390
- unless @terminal == "nothing" && ETYPE_LEAF == k.type
391
- if (@terminal == "triangle" && ETYPE_LEAF == k.type && k.indent == j.indent && words.length > 0)
392
- txt_width = img_get_txt_width(k.content, @font, @font_size)
393
- triangle_to_parent(k.indent, curlevel + 1, dw, tw, txt_width)
394
- elsif (@terminal == "auto" && ETYPE_LEAF == k.type && k.indent == j.indent)
395
- if words.length > 1 || k.triangle
396
- txt_width = img_get_txt_width(k.content, @font, @font_size)
397
- triangle_to_parent(k.indent, curlevel + 1, dw, tw, txt_width)
398
- else
399
- line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
400
- end
401
- else
402
- line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
403
- end
404
- end
405
- end
406
-
407
- else
408
- unless children.empty?
409
- k = @e_list.get_id(children[0])
410
- kw = img_get_txt_width(k.content, @font, @font_size)
411
- left = k.indent
412
- right = k.indent + kw
413
- draw_element(left, curlevel, right - left, j.content, j.type)
414
- @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
415
- else
416
- parent = @e_list.get_id(j.parent)
417
- pw = img_get_txt_width(parent.content, @font, @font_size)
418
- pleft = parent.indent
419
- pright = pleft + pw
420
- left = j.indent
421
- right = left + tw
422
- if pw > tw
423
- left = pleft
424
- right = pright
425
- end
426
- draw_element(left, curlevel, right - left, j.content, j.type)
427
- @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
428
- end
429
-
430
- unless children.empty?
431
- k = @e_list.get_id(children[0])
432
- words = k.content.split(" ")
433
- dw = img_get_txt_width(k.content, @font, @font_size)
434
- unless @terminal == "nothing" && ETYPE_LEAF == k.type
435
- if (@terminal == "triangle" && ETYPE_LEAF == k.type && words.length > 0)
436
- txt_width = img_get_txt_width(k.content, @font, @font_size)
437
- triangle_to_parent(k.indent, curlevel + 1, dw,
438
- @e_list.get_element_width(k.parent), txt_width)
439
- elsif (@terminal == "auto" && ETYPE_LEAF == k.type)
440
- if words.length > 1 || k.triangle
441
- txt_width = img_get_txt_width(k.content, @font, @font_size)
442
- triangle_to_parent(k.indent, curlevel + 1, dw, @e_list.get_element_width(k.parent), txt_width)
443
- else
444
- line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
445
- end
446
- else
447
- line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
448
- end
449
- end
450
- end
451
- end
452
- end
453
- end
454
- end
455
- end
456
-
457
- def row2px(row)
458
- B_TOPBOT + @e_height * row + (V_SPACE + @font_size) * row
292
+ return width
459
293
  end
460
-
461
294
  end