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