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