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,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