rsyntaxtree 0.8.4 → 0.9.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 46e87b5eb3718ca486b7db30381bf64d2c276f6e60173bbf686c150c3a40a54e
4
- data.tar.gz: 24c796def12932596bd4a4df5b29a758e4a8f633b986cae1d33663bf19e2836c
3
+ metadata.gz: 69c3a36921d2441f35c9729197aaea3ded204780b7033361a3434605e17e70cb
4
+ data.tar.gz: 342c7b8e779e2fca098d441f11ea7a458e050e42b49957369a7af3ac9ffd5ce8
5
5
  SHA512:
6
- metadata.gz: cf98fca2b5ce268499bfdea7a6d745de7f5bf6c0b26bacff1bea79d42df360dad1ec013d9f96b54f9111c951839d74c84a9725d12a81c364d504d39cec2432c2
7
- data.tar.gz: 8efcf521ecb186456be69b8060c9a3b6c7724805897fb2aef6716aa5420f0da38fa4639c9798528d32b845498c2484cff56b95bb4e4a1df401972131910a315e
6
+ metadata.gz: ddffb304231af332a8de21e29a97943570b8297749c7368c536b0f15103a392c4f10f1b6c77c79c0ab8b48a3fbe05ccfcc09da69b20b53c0f5a41692245d5137
7
+ data.tar.gz: 0c6a91490bdf0a26bcfed5cdad68bc25fdd9252577b86a638a8a6ce72a4c5c297324dda81e9e0f1852bee545d437d16f625ec312141fd8a56868b691d9bd05b6
data/bin/rsyntaxtree CHANGED
@@ -37,6 +37,8 @@ EOS
37
37
  :default => "on"
38
38
  opt :autosub, "Put subscript numbers to nodes: on or off",
39
39
  :default => "off"
40
+ opt :transparent, "Make background transparent: on or off",
41
+ :default => "off"
40
42
  end
41
43
 
42
44
  Optimist::die :outdir, "must be an exsting directory path" unless FileTest::directory?(opts[:outdir])
@@ -48,8 +50,9 @@ Optimist::die :fontsize, "must be in the range of 8-26" unless opts[:fontsize] >
48
50
  Optimist::die :color, "must be either on or off" unless /\A(on|off)\z/ =~ opts[:color]
49
51
  Optimist::die :symmetrize, "must be either on or off" unless /\A(on|off)\z/ =~ opts[:symmetrize]
50
52
  Optimist::die :autosub, "must be either on or off" unless /\A(on|off)\z/ =~ opts[:autosub]
51
- Optimist::die :margin, "must be in the range of 1-5" if opts[:margin] < 1 || opts[:margin] > 5
53
+ Optimist::die :margin, "must be in the range of 0-5" if opts[:margin] < 1 || opts[:margin] > 5
52
54
  Optimist::die :vheight, "must be in the range of 0.5-5.0" if opts[:vheight] < 0.5 || opts[:vheight] > 5.0
55
+ Optimist::die :transparent, "must be either on or off" unless /\A(on|off)\z/ =~ opts[:transparent]
53
56
 
54
57
  string_opts = {}
55
58
  opts.each do |key, value|
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -23,12 +23,12 @@ class Element
23
23
  @level = level # Element level in the tree (0=top etc...)
24
24
  @width = 0 # Width of the element in pixels
25
25
  @indent = 0 # Drawing offset
26
- # content = content.strip
27
- if /\A.+\^\z/ =~ content.strip
26
+ content = content.strip
27
+ if /\A.+\^\z/m =~ content
28
28
  @content = content.gsub("^"){""} # The actual element content
29
29
  @triangle = true # draw triangle instead of stright bar when in auto mode
30
30
  else
31
- @content = content.gsub("^"){""} # The actual element content
31
+ @content = content.gsub("^"){""}.strip # The actual element content
32
32
  @triangle = false # draw triangle instead of stright bar when in auto mode
33
33
  end
34
34
  # workaround to save "[A [B [C] [D] ] [E [F] [G [H] [J] ] ] ]"
@@ -23,7 +23,7 @@ class ErrorMessage
23
23
  @filename = filename
24
24
  @format = format
25
25
 
26
- metrics = img_get_txt_metrics(text, font, font_size, true)
26
+ metrics = img_get_txt_metrics(text, font, font_size, NoDecoration, true)
27
27
 
28
28
  @im = Image.new(metrics.width, metrics.height)
29
29
  @gc = Draw.new
@@ -32,8 +32,8 @@ class Graph
32
32
  # Calculate image dimensions
33
33
  @e_height = font_size + @m[:e_padd] * 2
34
34
  h = @e_list.get_level_height
35
- w = calc_level_width(0)
36
- @width = w + @m[:b_side] * 2
35
+ w = calc_level_width(0)
36
+ @width = w
37
37
  @height = h * @e_height + (h-1) * (@m[:v_space] + font_size) + @m[:b_topbot] * 2
38
38
 
39
39
  # Initialize the image and colors
@@ -50,7 +50,7 @@ class Graph
50
50
  @col_leaf = "black"
51
51
  @col_trace = "black"
52
52
  end
53
-
53
+
54
54
  @main_height = img_get_txt_height("l", font, font_size)
55
55
  @sub_size = (font_size * SUBSCRIPT_CONST)
56
56
  @sub_space_width = img_get_txt_width("l", font, @sub_size)
@@ -58,15 +58,17 @@ class Graph
58
58
 
59
59
  def img_get_txt_metrics(text, font, font_size, multiline)
60
60
 
61
- # background = Image.new(500, 250)
62
61
  background = Image.new(1, 1)
63
62
 
64
63
  gc = Draw.new
65
64
  gc.annotate(background, 0, 0, 0, 0, text) do |gc|
66
- gc.font = font
65
+ gc.font = font
67
66
  gc.pointsize = font_size
68
67
  gc.gravity = CenterGravity
69
68
  gc.stroke = 'none'
69
+ gc.kerning = 0
70
+ gc.interline_spacing = 0
71
+ gc.interword_spacing = 0
70
72
  end
71
73
 
72
74
  if multiline
@@ -82,18 +84,19 @@ class Graph
82
84
  # for all child elements.
83
85
  def calc_element_width(e)
84
86
  w = 0
87
+ content = e.content.gsub("<>", " ")
85
88
 
86
89
  children = @e_list.get_children(e.id)
87
90
 
88
91
  if(children.length == 0)
89
- w = img_get_txt_width(e.content, @font, @font_size) + @font_size
92
+ w = img_get_txt_width(content, @font, @font_size) + @font_size
90
93
  else
91
94
  children.each do |child|
92
95
  child_e = @e_list.get_id(child)
93
96
  w += calc_element_width(child_e)
94
97
  end
95
98
 
96
- tw = img_get_txt_width(e.content, @font, @font_size) + @font_size
99
+ tw = img_get_txt_width(content, @font, @font_size) + @font_size
97
100
  if(tw > w)
98
101
  fix_child_size(e.id, w, tw)
99
102
  w = tw
@@ -110,9 +113,10 @@ class Graph
110
113
  e = @e_list.get_first
111
114
  while e
112
115
  if(e.level == level)
113
- w += calc_element_width(e)
116
+ x = calc_element_width(e)
117
+ w += x
114
118
  end
115
- e = @e_list.get_next
119
+ e = @e_list.get_next
116
120
  end
117
121
  return w
118
122
  end
@@ -168,127 +172,116 @@ class Graph
168
172
  if(j.parent != 0 )
169
173
  words = j.content.split(" ")
170
174
  unless @leafstyle == "nothing" && ETYPE_LEAF == j.type
171
- if (@leafstyle == "triangle" && ETYPE_LEAF == j.type && x == parent_indent && words.length > 0)
172
- txt_width = img_get_txt_width(j.content, @font, @font_size)
173
- triangle_to_parent(x, i, cw, txt_width, @symmetrize)
174
- elsif (@leafstyle == "auto" && ETYPE_LEAF == j.type && x == parent_indent)
175
- if words.length > 1 || j.triangle
176
- txt_width = img_get_txt_width(j.content, @font, @font_size)
177
- triangle_to_parent(x, i, cw, txt_width, @symmetrize)
178
- else
179
- line_to_parent(x, i, cw, @e_list.get_indent(j.parent), @e_list.get_element_width(j.parent))
180
- end
175
+ if (@leafstyle == "auto" && ETYPE_LEAF == j.type && x == parent_indent)
176
+ if words.length > 1 || j.triangle
177
+ txt_width = img_get_txt_width(j.content, @font, @font_size)
178
+ triangle_to_parent(x, i, cw, txt_width, @symmetrize)
179
+ else
180
+ line_to_parent(x, i, cw, @e_list.get_indent(j.parent), @e_list.get_element_width(j.parent))
181
+ end
181
182
  else
182
183
  line_to_parent(x, i, cw, @e_list.get_indent(j.parent), @e_list.get_element_width(j.parent))
183
184
  end
184
185
  end
185
186
  end
186
187
  end
187
-
188
188
  x += cw
189
189
  end
190
190
  end
191
191
  end
192
192
  return true if !@symmetrize
193
193
 
194
+ elements_to_draw = {}
195
+ triangles_to_draw = []
196
+ lines_to_draw = []
197
+
198
+ lmost = {:level => nil, :value => nil, :type => nil}
199
+ rmost = nil
194
200
  h.times do |i|
195
201
  curlevel = h - i - 1
196
- indent = 0
197
202
  e_arr.each_with_index do |j, idx|
198
203
  if (j.level == curlevel)
199
204
  # Draw a line to the parent element
200
205
  children = @e_list.get_children(j.id)
201
206
 
202
207
  tw = img_get_txt_width(j.content, @font, @font_size)
203
- if children.length > 1
208
+ if children.length > 0
204
209
  left, right = -1, -1
205
- children.each do |child|
210
+ children.each do |child|
206
211
  k = @e_list.get_id(child)
207
- kw = img_get_txt_width(k.content, @font, @font_size)
212
+ kw = img_get_txt_width(k.content, @font, @font_size)
208
213
  left = k.indent + kw / 2 if k.indent + kw / 2 < left or left == -1
209
214
  right = k.indent + kw / 2 if k.indent + kw / 2 > right
210
215
  end
211
- draw_element(left, curlevel, right - left, j.content, j.type)
216
+
217
+ elements_to_draw[j.id] = {:left => left, :curlevel => curlevel, :width => right - left, :content => j.content, :type => j.type}
212
218
  @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
213
219
 
214
220
  children.each do |child|
215
221
  k = @e_list.get_id(child)
216
222
  words = k.content.split(" ")
217
223
  dw = img_get_txt_width(k.content, @font, @font_size)
224
+
225
+ children2 = @e_list.get_children(k.id)
226
+
218
227
  unless @leafstyle == "nothing" && ETYPE_LEAF == k.type
219
- if (@leafstyle == "triangle" && ETYPE_LEAF == k.type && k.indent == j.indent && words.length > 0)
220
- txt_width = img_get_txt_width(k.content, @font, @font_size)
221
- triangle_to_parent(k.indent, curlevel + 1, dw, txt_width)
222
- elsif (@leafstyle == "auto" && ETYPE_LEAF == k.type && k.indent == j.indent)
228
+ if (@leafstyle == "auto" && ETYPE_LEAF == k.type)
223
229
  if words.length > 1 || k.triangle
224
230
  txt_width = img_get_txt_width(k.content, @font, @font_size)
225
- triangle_to_parent(k.indent, curlevel + 1, dw, txt_width)
231
+ triangles_to_draw << {:indent => k.indent, :curlevel => curlevel + 1, :width1 => dw, :width2 => txt_width}
226
232
  else
227
- line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
233
+ lines_to_draw << {:indent1 => k.indent, :curlevel => curlevel + 1, :width1 => dw, :indent2 => j.indent, :width2 => tw}
228
234
  end
229
235
  else
230
- line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
236
+ lines_to_draw << {:indent1 => k.indent, :curlevel => curlevel + 1, :width1 => dw, :indent2 => j.indent, :width2 => tw}
231
237
  end
232
238
  end
233
239
  end
234
240
 
235
- else
236
- unless children.empty?
237
- k = @e_list.get_id(children[0])
238
- kw = img_get_txt_width(k.content, @font, @font_size)
239
- left = k.indent
240
- right = k.indent + kw
241
- draw_element(left, curlevel, right - left, j.content, j.type)
242
- @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
243
-
244
- k = @e_list.get_id(children[0])
245
- words = k.content.split(" ")
246
- dw = img_get_txt_width(k.content, @font, @font_size)
247
- unless (@leafstyle == "nothing" && ETYPE_LEAF == k.type)
248
- if (@leafstyle == "triangle" && ETYPE_LEAF == k.type && words.length > 0)
249
- txt_width = img_get_txt_width(k.content, @font, @font_size)
250
- triangle_to_parent(k.indent, curlevel + 1, dw, txt_width)
251
- elsif (@leafstyle == "auto" && ETYPE_LEAF == k.type)
252
- if words.length > 1 || k.triangle
253
- txt_width = img_get_txt_width(k.content, @font, @font_size)
254
- triangle_to_parent(k.indent, curlevel + 1, dw, txt_width)
255
- else
256
- line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
257
- end
258
- else
259
- line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
260
- end
261
- end
241
+ end
242
+ elements = e_arr.select do |l|
243
+ l.level == curlevel && @e_list.get_children(l.id).empty?
244
+ end
262
245
 
263
- else
264
- parent = @e_list.get_id(j.parent)
265
- pw = img_get_txt_width(parent.content, @font, @font_size)
266
- pleft = parent.indent
267
- pright = pleft + pw
268
- if curlevel == (h - 1) && e_arr.size == idx + 1
269
- e_arr.select{|l|l.level == curlevel}.each do |l|
270
- lw = img_get_txt_width(l.content, @font, @font_size)
271
- left = l.indent
272
- right = left + lw
273
- @e_list.set_indent(l.id, left + (right - left) / 2 - tw / 2)
274
- draw_element(left, curlevel, right - left, l.content, l.type)
275
- end
276
- break
277
- else
278
- left = j.indent
279
- right = left + tw
280
- if pw > tw
281
- left = pleft
282
- right = pright
283
- end
284
- draw_element(left, curlevel, right - left, j.content, j.type)
285
- @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
286
- end
246
+ elements.each.with_index do |l, idx|
247
+ lw = img_get_txt_width(l.content, @font, @font_size)
248
+ left = l.indent
249
+ right = left + lw
250
+ unless elements_to_draw.include? l.id
251
+ elements_to_draw[l.id] = {:left => left, :curlevel => curlevel, :width => right - left, :content => l.content, :type => l.type}
287
252
  end
288
-
289
253
  end
290
254
  end
291
255
  end
256
+
257
+ e_arr.each do |e|
258
+ lpos = e.indent - img_get_txt_width(e.content, @font, @font_size) / 2
259
+ next if lpos > 0
260
+ if !lmost[:value] || lmost[:value] > lpos
261
+ lmost[:level] = e.level
262
+ lmost[:value] = lpos
263
+ lmost[:type] = e
264
+ end
265
+ rpos = e.indent + e.width
266
+ rmost = rpos if !rmost || rmost < rpos
267
+ end
268
+ end
269
+
270
+ offset = 0
271
+ if lmost[:level] != h - 1
272
+ offset = lmost[:value] / -2
273
+ new_width = rmost
274
+ @width = new_width + offset
275
+ end
276
+
277
+ elements_to_draw.each do |k, v|
278
+ draw_element(v[:left] + offset, v[:curlevel], v[:width], v[:content], v[:type])
279
+ end
280
+ triangles_to_draw.each do |v|
281
+ triangle_to_parent(v[:indent] + offset, v[:curlevel], v[:width1], v[:width2])
282
+ end
283
+ lines_to_draw.each do |v|
284
+ line_to_parent(v[:indent1] + offset, v[:curlevel], v[:width1], v[:indent2] + offset, v[:width2])
292
285
  end
293
286
  end
294
287
 
@@ -310,8 +303,9 @@ class Graph
310
303
  return text
311
304
  end
312
305
 
313
- def img_get_txt_height(text, font, font_size, multiline = false)
306
+ def img_get_txt_height(text, font, font_size, multiline = true)
314
307
  metrics = img_get_txt_metrics(text, font, font_size, multiline)
308
+ # y = (metrics.bounds.y2 - metrics.bounds.y1).round
315
309
  y = metrics.height
316
310
  return y
317
311
  end
@@ -171,8 +171,15 @@ class StringParser
171
171
  end
172
172
  when "\\"
173
173
  escape = true
174
- when /[\n\r]/
175
- gottoken = false # same as do nothing
174
+ when "n", " ", "+", "-", "=", "~", "#", "*"
175
+ if escape
176
+ token += "\\#{ch}"
177
+ escape = false
178
+ else
179
+ token += ch
180
+ end
181
+ # when /[\n\r]/
182
+ # gottoken = false # same as do nothing
176
183
  else
177
184
  token += ch
178
185
  escape = false if escape
@@ -18,37 +18,70 @@ require 'graph'
18
18
 
19
19
  class SVGGraph < Graph
20
20
 
21
- def initialize(e_list, metrics, symmetrize, color, leafstyle, multibyte, fontstyle, font, font_cjk, font_size)
21
+ def initialize(e_list, metrics, symmetrize, color, leafstyle, multibyte, fontstyle, font, font_cjk, font_size, margin, transparent)
22
22
 
23
23
  # Store class-specific parameters
24
24
  @font = multibyte ? font_cjk : font
25
25
  @font_size = font_size
26
+ @transparent = transparent
27
+
26
28
  case fontstyle
27
29
  when /(?:sans|cjk)/
28
- @fontstyle = "sans-serif"
29
- when /(?:serif|math)/
30
- @fontstyle = "serif"
30
+ @fontstyle = "\"'Noto Sans JP', 'Noto Sans', sans-serif\""
31
+ @fontcss = "http://fonts.googleapis.com/earlyaccess/notosansjp.css"
32
+ when /(?:serif)/
33
+ @fontstyle = "\"'Noto Serif JP', 'Noto Serif', serif\""
34
+ @fontcss = "https://fonts.googleapis.com/css?family=Noto+Serif+JP"
35
+ when /(?:math)/
36
+ @fontstyle = "\"Latin Modern Roman', sans-serif\""
37
+ @fontcss = "https://cdn.jsdelivr.net/gh/sugina-dev/latin-modern-web@1.0.1/style/latinmodern-roman.css"
31
38
  end
32
39
 
33
- super(e_list, metrics, symmetrize, color, leafstyle, multibyte, @font, @font_size)
40
+ @margin = margin.to_i
41
+
42
+ super(e_list, metrics, symmetrize, color, leafstyle, multibyte, @fontstyle, @font_size)
34
43
 
35
44
  @line_styles = "<line style='stroke:black; stroke-width:#{FONT_SCALING};' x1='X1' y1='Y1' x2='X2' y2='Y2' />\n"
36
45
  @polygon_styles = "<polygon style='fill: none; stroke: black; stroke-width:#{FONT_SCALING};' points='X1 Y1 X2 Y2 X3 Y3' />\n"
37
- @text_styles = "<text style='fill: COLOR; font-size: FONT_SIZEpx; ST; WA;' x='X_VALUE' y='Y_VALUE' TD font-family='#{@fontstyle}'>CONTENT</text>\n"
46
+ @text_styles = "<text letter-spacing='0' word-spacing='0' kerning='0' style='fill: COLOR; font-size: FONT_SIZE ST WA' x='X_VALUE' y='Y_VALUE' TD font-family=#{@fontstyle}>CONTENT</text>\n"
38
47
  @tree_data = String.new
39
48
  end
40
49
 
50
+ def get_left_most(tree_data)
51
+ xs = @tree_data.scan(/x1?=['"]([^'"]+)['"]/).map{|m| m.first.to_i}
52
+ xs.min
53
+ end
54
+
41
55
  def svg_data
42
56
  parse_list
57
+ lm = get_left_most(@tree_data)
58
+ width = @width - lm + @margin * 2
59
+ height = @height + @margin * 2
60
+
43
61
  header =<<EOD
44
62
  <?xml version="1.0" standalone="no"?>
45
- <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
63
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
46
64
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
47
- <svg width="#{@width}" height="#{@height}" version="1.1" xmlns="http://www.w3.org/2000/svg">
65
+ <svg width="#{width}" height="#{height}" viewBox="#{-@margin + lm}, -#{@margin}, #{@width - lm + @margin * 2}, #{@height + @margin * 2}" version="1.1" xmlns="http://www.w3.org/2000/svg">
66
+ <defs>
67
+ <style>
68
+ @import url(#{@fontcss});
69
+ </style>
70
+ </defs>
71
+ EOD
72
+
73
+
74
+ rect =<<EOD
75
+ <rect x="#{-@margin + lm}" y="-#{@margin}" width="#{@width - lm + @margin * 2}" height="#{@height + @margin * 2}" stroke="none" fill="white" />"
48
76
  EOD
49
77
 
50
78
  footer = "</svg>"
51
- header + @tree_data + footer
79
+
80
+ if @transparent
81
+ header + @tree_data + footer
82
+ else
83
+ header + rect + @tree_data + footer
84
+ end
52
85
  end
53
86
 
54
87
  # Create a temporary file and returns only its filename
@@ -68,21 +101,21 @@ EOD
68
101
  end
69
102
 
70
103
  :private
71
-
104
+
72
105
  # Add the element into the tree (draw it)
73
106
  def draw_element(x, y, w, string, type)
74
- string = string.sub(/\^\z/){""}
107
+ string = string.sub(/\^\z/){""}
75
108
  # Calculate element dimensions and position
76
109
  if (type == ETYPE_LEAF) and @leafstyle == "nothing"
77
110
  top = row2px(y - 1) + (@font_size * 1.5)
78
- else
111
+ else
79
112
  top = row2px(y)
80
113
  end
81
114
  left = x + @m[:b_side]
82
115
  bottom = top + @e_height
83
116
  right = left + w
84
117
 
85
- # Split the string into the main part and the
118
+ # Split the string into the main part and the
86
119
  # subscript part of the element (if any)
87
120
  parts = string.split("_", 2)
88
121
  if(parts.length > 1 )
@@ -127,11 +160,18 @@ EOD
127
160
  main = $1
128
161
  end
129
162
 
130
- # Calculate text size for the main and the
163
+ # Calculate text size for the main and the
131
164
  # subscript part of the element
132
165
  # symbols for underline/overline removed temporarily
133
166
 
134
- main_width = img_get_txt_width(main, @font, @font_size)
167
+ main_width = 0
168
+ main_height = 0
169
+ main.split(/\\n/).each do |l|
170
+ l_width = img_get_txt_width(l, @font, @font_size)
171
+ main_width = l_width if main_width < l_width
172
+ main_height += img_get_txt_height(l, @font, @font_size)
173
+ end
174
+
135
175
 
136
176
  if sub != ""
137
177
  if /\A\=(.+)\=\z/ =~ sub
@@ -163,9 +203,11 @@ EOD
163
203
  sub_style = ""
164
204
  sub_weight = ""
165
205
  end
206
+ sub_height = img_get_txt_height(sub, @font, @font_size)
166
207
  sub_width = img_get_txt_width(sub.to_s, @font, @sub_size)
167
208
  else
168
209
  sub_width = 0
210
+ sub_height = 0
169
211
  end
170
212
 
171
213
  if /\A#(.+)#\z/ =~ sub
@@ -173,14 +215,13 @@ EOD
173
215
  end
174
216
 
175
217
  # Center text in the element
176
- txt_width = main_width + sub_width
177
- txt_pos = left + (right - left) / 2 - txt_width / 2
218
+ txt_pos = left + (right - left) / 2
178
219
 
179
220
  # Select apropriate color
180
221
  if(type == ETYPE_LEAF)
181
222
  col = @col_leaf
182
223
  else
183
- col = @col_node
224
+ col = @col_node
184
225
  end
185
226
 
186
227
  if(main[0].chr == "<" && main[-1].chr == ">")
@@ -189,29 +230,47 @@ EOD
189
230
 
190
231
  # Draw main text
191
232
  main_data = @text_styles.sub(/COLOR/, col)
192
- main_data = main_data.sub(/FONT_SIZE/, @font_size.to_s)
193
- main_x = txt_pos
194
- main_y = top + @e_height - @m[:e_padd] * 1.5
233
+ main_data = main_data.sub(/FONT_SIZE/, @font_size.to_s + "px;")
234
+ main_x = txt_pos - (main_width + sub_width) / 2
235
+ main_y = top + @e_height - @m[:e_padd]
195
236
  main_data = main_data.sub(/X_VALUE/, main_x.to_s)
196
237
  main_data = main_data.sub(/Y_VALUE/, main_y.to_s)
197
-
238
+ if /\\n/ =~ main
239
+ lines = main.split(/\\n/)
240
+ new_main = ""
241
+ dy = 0
242
+ lines.each_with_index do |l, idx|
243
+ if idx == 0
244
+ dy = 0
245
+ else
246
+ dy = 1
247
+ main_y += img_get_txt_height(l, @font, @font_size)
248
+ end
249
+ this_width = img_get_txt_width(l, @font, @font_size)
250
+ this_x = txt_pos - (this_width + sub_width) / 2
251
+ new_main << "<tspan x='#{this_x}' y='#{main_y}'>#{l}</tspan>"
252
+ @height = main_y if main_y > @height
253
+ end
254
+ main = new_main
255
+ end
198
256
  @tree_data += main_data.sub(/TD/, "text-decoration='#{main_decoration}'")
199
- .sub(/ST/, main_style)
200
- .sub(/WA/, main_weight)
257
+ .sub(/ST/, main_style + ";")
258
+ .sub(/WA/, main_weight + ";")
201
259
  .sub(/CONTENT/, main)
202
260
 
203
261
  # Draw subscript text
204
- sub_data = @text_styles.sub(/COLOR/, col)
205
- sub_data = sub_data.sub(/FONT_SIZE/, @sub_size.to_s)
206
- sub_x = main_x + main_width
207
- sub_y = top + (@e_height - @m[:e_padd] + @sub_size / 10)
208
- if (sub.length > 0 )
209
- sub_data = sub_data.sub(/X_VALUE/, sub_x.ceil.to_s)
210
- sub_data = sub_data.sub(/Y_VALUE/, sub_y.ceil.to_s)
262
+ if sub && sub != ""
263
+ sub_data = @text_styles.sub(/COLOR/, col)
264
+ sub_data = sub_data.sub(/FONT_SIZE/, @sub_size.to_s)
265
+ sub_x = right - sub_width
266
+ sub_y = main_y + sub_height / 4
267
+ sub_data = sub_data.sub(/X_VALUE/, sub_x.to_s)
268
+ sub_data = sub_data.sub(/Y_VALUE/, sub_y.to_s)
211
269
  @tree_data += sub_data.sub(/TD/, "text-decoration='#{sub_decoration}'")
212
270
  .sub(/ST/, sub_style)
213
271
  .sub(/WA/, sub_weight)
214
- .sub(/CONTENT/, sub)
272
+ .sub(/CONTENT/, sub)
273
+ @height += sub_height / 4
215
274
  end
216
275
  end
217
276
 
@@ -227,10 +286,10 @@ EOD
227
286
  toBot = (row2px(fromY - 1 ) + @e_height)
228
287
  toLeft = (toX + toW / 2 + @m[:b_side])
229
288
 
230
- line_data = @line_styles.sub(/X1/, fromLeft.ceil.to_s)
231
- line_data = line_data.sub(/Y1/, fromTop.ceil.to_s)
232
- line_data = line_data.sub(/X2/, toLeft.ceil.to_s)
233
- @tree_data += line_data.sub(/Y2/, toBot.ceil.to_s)
289
+ line_data = @line_styles.sub(/X1/, fromLeft.to_s)
290
+ line_data = line_data.sub(/Y1/, fromTop.to_s)
291
+ line_data = line_data.sub(/X2/, toLeft.to_s)
292
+ @tree_data += line_data.sub(/Y2/, toBot.to_s)
234
293
 
235
294
  end
236
295
 
@@ -243,9 +302,9 @@ EOD
243
302
  toX = fromX
244
303
  fromCenter = (fromX + fromW / 2 + @m[:b_side])
245
304
 
246
- fromTop = row2px(fromY).ceil
247
- fromLeft1 = (fromCenter + textW / 2).ceil
248
- fromLeft2 = (fromCenter - textW / 2).ceil
305
+ fromTop = row2px(fromY)
306
+ fromLeft1 = (fromCenter + textW / 2)
307
+ fromLeft2 = (fromCenter - textW / 2)
249
308
  toBot = (row2px(fromY - 1) + @e_height)
250
309
 
251
310
  if symmetrize
@@ -254,12 +313,12 @@ EOD
254
313
  toLeft = (toX + textW / 2 + @m[:b_side] * 3)
255
314
  end
256
315
 
257
- polygon_data = @polygon_styles.sub(/X1/, fromLeft1.ceil.to_s)
258
- polygon_data = polygon_data.sub(/Y1/, fromTop.ceil.to_s)
259
- polygon_data = polygon_data.sub(/X2/, fromLeft2.ceil.to_s)
260
- polygon_data = polygon_data.sub(/Y2/, fromTop.ceil.to_s)
261
- polygon_data = polygon_data.sub(/X3/, toLeft.ceil.to_s)
262
- @tree_data += polygon_data.sub(/Y3/, toBot.ceil.to_s)
316
+ polygon_data = @polygon_styles.sub(/X1/, fromLeft1.to_s)
317
+ polygon_data = polygon_data.sub(/Y1/, fromTop.to_s)
318
+ polygon_data = polygon_data.sub(/X2/, fromLeft2.to_s)
319
+ polygon_data = polygon_data.sub(/Y2/, fromTop.to_s)
320
+ polygon_data = polygon_data.sub(/X3/, toLeft.to_s)
321
+ @tree_data += polygon_data.sub(/Y3/, toBot.to_s)
263
322
  end
264
323
 
265
324
  # If a node element text is wider than the sum of it's
@@ -271,9 +330,9 @@ EOD
271
330
  children = @e_list.get_children(id)
272
331
  @e_list.set_element_width(id, target)
273
332
 
274
- if(children.length > 0 )
333
+ if(children.length > 0 )
275
334
  delta = target - current
276
- target_delta = delta / children.length
335
+ target_delta = delta / children.length
277
336
 
278
337
  children.each do |child|
279
338
  child_width = @e_list.get_element_width(child)
@@ -282,27 +341,23 @@ EOD
282
341
  end
283
342
  end
284
343
 
285
- def img_get_txt_width(text, font, font_size, multiline = false)
344
+ def img_get_txt_width(text, font, font_size, multiline = true)
286
345
  parts = text.split("_", 2)
287
346
  main_before = parts[0].strip
288
347
  sub = parts[1]
289
348
  main = get_txt_only(main_before)
290
- if(main.contains_cjk?)
291
- main = 'n' * main.strip.size * 2
292
- else
293
- main
294
- end
295
349
  main_metrics = img_get_txt_metrics(main, font, font_size, multiline)
296
350
  width = main_metrics.width
297
351
  if sub
298
- if(sub.contains_cjk?)
299
- sub = 'n' * sub.strip.size * 2
300
- else
301
- sub
302
- end
303
- sub_metrics = img_get_txt_metrics(sub, font, font_size * SUBSCRIPT_CONST, multiline)
352
+ sub_metrics = img_get_txt_metrics(sub.strip, font, font_size * SUBSCRIPT_CONST, multiline)
304
353
  width += sub_metrics.width
305
354
  end
306
355
  return width
307
356
  end
357
+
358
+ def img_get_txt_height(text, font, font_size)
359
+ main_metrics = img_get_txt_metrics(text, font, font_size, false)
360
+ main_metrics.height
361
+ end
362
+
308
363
  end
@@ -21,24 +21,23 @@ class TreeGraph < Graph
21
21
 
22
22
  def initialize(e_list, metrics, symmetrize, color, leafstyle, multibyte,
23
23
  fontstyle, font, font_it, font_bd, font_itbd, font_math, font_cjk, font_size,
24
- margin)
24
+ margin, transparent)
25
25
 
26
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_math = font_math
34
- @font_cjk = font_cjk
35
- @margin = margin
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_math = font_math
34
+ @font_cjk = font_cjk
35
+ @margin = margin
36
+ @transparent = transparent
36
37
 
37
38
  super(e_list, metrics, symmetrize, color, leafstyle, multibyte, @font, @font_size)
38
39
 
39
40
  # Initialize the image and colors
40
- @im = Image.new(@width, @height)
41
- @im.interlace = PlaneInterlace
42
41
  @gc = Draw.new
43
42
  @gc.font = @font
44
43
  @gc.pointsize(@font_size)
@@ -50,6 +49,12 @@ class TreeGraph < Graph
50
49
 
51
50
  def draw
52
51
  parse_list
52
+ @im = Image.new(@width, @height)
53
+ if @transparent
54
+ @im.matte_reset!
55
+ end
56
+ # @im.interlace = PlaneInterlace
57
+ @im.interlace = LineInterlace
53
58
  @gc.draw(@im)
54
59
  end
55
60
 
@@ -62,7 +67,12 @@ class TreeGraph < Graph
62
67
  # by Geoffrey Grosenbach
63
68
  def to_blob(fileformat='PNG')
64
69
  draw
65
- @im.border!(@margin, @margin, "white")
70
+ @im.trim!
71
+ if @transparent
72
+ @im.border!(@margin, @margin, "transparent")
73
+ else
74
+ @im.border!(@margin, @margin, "white")
75
+ end
66
76
  @im.format = fileformat
67
77
  @im.interlace = PlaneInterlace
68
78
  return @im.to_blob
@@ -80,7 +90,6 @@ class TreeGraph < Graph
80
90
  top = row2px(y)
81
91
  end
82
92
  left = x + @m[:b_side]
83
- bottom = top + @e_height
84
93
  right = left + w
85
94
 
86
95
  # Split the string into the main part and the
@@ -134,7 +143,14 @@ class TreeGraph < Graph
134
143
  # Calculate text size for the main and the
135
144
  # subscript part of the element
136
145
 
137
- main_width = img_get_txt_width(main, main_font, @font_size)
146
+
147
+ main_width = 0
148
+ main_height = 0
149
+ main.split(/\\n/).each do |l|
150
+ l_width = img_get_txt_width(l, main_font, @font_size)
151
+ main_width = l_width if main_width < l_width
152
+ main_height += img_get_txt_height(l, @font, @font_size)
153
+ end
138
154
 
139
155
  if /\A\=(.+)\=\z/ =~ sub
140
156
  sub = $1
@@ -173,15 +189,16 @@ class TreeGraph < Graph
173
189
  end
174
190
 
175
191
  if sub != ""
192
+ # add a separation of the width of "M"
176
193
  sub_width = img_get_txt_width(sub.to_s, sub_font, @sub_size)
194
+ sub_height = img_get_txt_height(sub.to_s, sub_font, @sub_size)
177
195
  else
178
196
  sub_width = 0
197
+ sub_height = 0
179
198
  end
180
199
 
181
200
  # Center text in the element
182
- txt_width = main_width + sub_width
183
-
184
- txt_pos = left + (right - left) / 2 - txt_width / 2
201
+ txt_pos = left + (right - left) / 2
185
202
 
186
203
  # Select apropriate color
187
204
  if(type == ETYPE_LEAF)
@@ -199,22 +216,33 @@ class TreeGraph < Graph
199
216
 
200
217
  # Draw main text
201
218
  @gc.pointsize(@font_size)
202
- main_x = txt_pos
219
+ @gc.kerning = 0
220
+ @gc.interline_spacing = 0
221
+ @gc.interword_spacing = 0
222
+
223
+ main_x = txt_pos - sub_width / 2
203
224
  main_y = top + @e_height - @m[:e_padd]
204
225
 
205
- @gc.interline_spacing = -(@main_height / 3)
226
+ # @gc.interline_spacing = -(@main_height / 3)
206
227
  @gc.font(main_font)
207
228
  @gc.decorate(main_decoration)
208
- @gc.text(main_x.ceil, main_y.ceil, main)
229
+ numlines = main.count("\\n")
230
+ if numlines > 1
231
+ @height = @height + @main_height * numlines
232
+ end
233
+ @gc.text_align(CenterAlign)
234
+ @gc.text(main_x.ceil, main_y.ceil, main.gsub("\\n", "\n"))
209
235
 
210
236
  # Draw subscript text
211
- if (sub.length > 0 )
237
+ if (sub != "" )
212
238
  @gc.pointsize(@sub_size)
213
- sub_x = txt_pos + main_width + @sub_space_width
214
- sub_y = top + (@e_height - @m[:e_padd] + @sub_size / 2)
239
+ sub_x = main_x + (main_width / 2) + (sub_width / 2)
240
+ sub_y = top + main_height + sub_height / 4
241
+ @height += sub_height / 4
215
242
  @gc.font(sub_font)
216
243
  @gc.decorate(sub_decoration)
217
- @gc.text(sub_x.ceil, sub_y.ceil, sub)
244
+ @gc.text_align(CenterAlign)
245
+ @gc.text(sub_x.ceil, sub_y.ceil, " " + sub.gsub("\\n", "\n"))
218
246
  end
219
247
  end
220
248
 
@@ -249,6 +277,7 @@ class TreeGraph < Graph
249
277
  fromLeft1 = (fromCenter + textW / 2).ceil
250
278
  fromLeft2 = (fromCenter - textW / 2).ceil
251
279
  toBot = (row2px(fromY - 1) + @e_height)
280
+
252
281
  if symmetrize
253
282
  toLeft = (toX + textW / 2 + @m[:b_side])
254
283
  else
@@ -283,7 +312,7 @@ class TreeGraph < Graph
283
312
  end
284
313
  end
285
314
 
286
- def img_get_txt_width(text, font, font_size, multiline = false)
315
+ def img_get_txt_width(text, font, font_size, multiline = true)
287
316
  parts = text.split("_", 2)
288
317
  main_before = parts[0].strip
289
318
  sub = parts[1]
@@ -1,4 +1,4 @@
1
1
  module RSyntaxTree
2
- VERSION = "0.8.4"
2
+ VERSION = "0.9.1"
3
3
  end
4
4
 
data/lib/rsyntaxtree.rb CHANGED
@@ -40,7 +40,8 @@ class RSGenerator
40
40
  key = keystr.to_sym
41
41
  case key
42
42
  when :data
43
- data = CGI.unescape(value)
43
+ data = value
44
+ # data = CGI.unescape(value)
44
45
  data = data.gsub('-AMP-', '&')
45
46
  .gsub('-PERCENT-', "%")
46
47
  .gsub('-PRIME-', "'")
@@ -49,7 +50,7 @@ class RSGenerator
49
50
  .gsub('-CABRACKET-', '>')
50
51
  new_params[key] = data
51
52
  new_params[:multibyte] = data.contains_cjk?
52
- when :symmetrize, :color, :autosub
53
+ when :symmetrize, :color, :autosub, :transparent
53
54
  new_params[key] = value && value != "off" ? true : false
54
55
  when :fontsize
55
56
  new_params[key] = value.to_i * FONT_SCALING
@@ -64,7 +65,7 @@ class RSGenerator
64
65
  new_params[:font_bd] = FONT_DIR + "/NotoSans-Bold.ttf"
65
66
  new_params[:font_bdit] = FONT_DIR + "/NotoSans-BoldItalic.ttf"
66
67
  new_params[:font_math] = FONT_DIR + "/NotoSansMath-Regular.ttf"
67
- new_params[:font_cjk] = FONT_DIR + "/NotoSansCJKjp-Regular.otf"
68
+ new_params[:font_cjk] = FONT_DIR + "/NotoSansJP-Regular.otf"
68
69
  new_params[:fontstyle] = "sans"
69
70
  elsif value == "noto-serif" || value == "serif"
70
71
  new_params[:font] = FONT_DIR + "/NotoSerif-Regular.ttf"
@@ -72,7 +73,7 @@ class RSGenerator
72
73
  new_params[:font_bd] = FONT_DIR + "/NotoSerif-Bold.ttf"
73
74
  new_params[:font_bdit] = FONT_DIR + "/NotoSerif-BoldItalic.ttf"
74
75
  new_params[:font_math] = FONT_DIR + "/NotoSansMath-Regular.ttf"
75
- new_params[:font_cjk] = FONT_DIR + "/NotoSerifCJKjp-Regular.otf"
76
+ new_params[:font_cjk] = FONT_DIR + "/NotoSerifJP-Regular.otf"
76
77
  new_params[:fontstyle] = "serif"
77
78
  elsif value == "cjk zenhei" || value == "cjk"
78
79
  new_params[:font] = FONT_DIR + "/wqy-zenhei.ttf"
@@ -88,7 +89,7 @@ class RSGenerator
88
89
  new_params[:font_bd] = FONT_DIR + "/lmroman10-bold.otf"
89
90
  new_params[:font_bdit] = FONT_DIR + "/lmroman10-bolditalic.otf"
90
91
  new_params[:font_math] = FONT_DIR + "/latinmodern-math.otf"
91
- new_params[:font_cjk] = FONT_DIR + "/NotoSerifCJKjp-Regular.otf"
92
+ new_params[:font_cjk] = FONT_DIR + "/NotoSerifJP-Regular.otf"
92
93
  new_params[:fontstyle] = "math"
93
94
  end
94
95
  else
@@ -98,30 +99,37 @@ class RSGenerator
98
99
 
99
100
  # defaults to the following
100
101
  @params = {
101
- :symmetrize => true,
102
- :color => true,
103
- :autosub => false,
104
- :fontsize => 18,
105
- :format => "png",
106
- :leafstyle => "auto",
107
- :filename => "syntree",
108
- :data => "",
109
- :margin => 0,
110
- :vheight => 1.0,
111
- :fontstyle => "sans",
112
- :font => "/NotoSansCJKjp-Regular.otf",
113
- :font_it => "/NotoSans-Italic.ttf",
114
- :font_bd => "/NotoSans-Bold.ttf",
115
- :font_bdit => "/NotoSans-BoldItalic.ttf",
116
- :font_math => "/NotoSansMath-Regular.ttf"
102
+ :symmetrize => true,
103
+ :color => true,
104
+ :autosub => false,
105
+ :transparent => false,
106
+ :fontsize => 18,
107
+ :format => "png",
108
+ :leafstyle => "auto",
109
+ :filename => "syntree",
110
+ :data => "",
111
+ :margin => 0,
112
+ :vheight => 1.0,
113
+ :fontstyle => "sans",
114
+ :font => "/NotoSansJP-Regular.otf",
115
+ :font_it => "/NotoSans-Italic.ttf",
116
+ :font_bd => "/NotoSans-Bold.ttf",
117
+ :font_bdit => "/NotoSans-BoldItalic.ttf",
118
+ :font_math => "/NotoSansMath-Regular.ttf"
117
119
  }
118
120
  @metrics = {
119
- :e_width => 120,
120
- :e_padd => 14,
121
- :v_space => 20,
122
- :h_space => 20,
123
- :b_side => 10,
124
- :b_topbot => 10
121
+ :e_width => 0,
122
+ :e_padd => 20,
123
+ :v_space => 0,
124
+ :h_space => 0,
125
+ :b_side => 0,
126
+ :b_topbot => 0
127
+ # :e_width => 120,
128
+ # :e_padd => 14,
129
+ # :v_space => 20,
130
+ # :h_space => 20,
131
+ # :b_side => 10,
132
+ # :b_topbot => 10
125
133
  }
126
134
 
127
135
  @params.merge! new_params
@@ -163,7 +171,7 @@ class RSGenerator
163
171
  elist = sp.get_elementlist
164
172
  graph = SVGGraph.new(elist, @metrics,
165
173
  @params[:symmetrize], @params[:color], @params[:leafstyle], @params[:multibyte],
166
- @params[:fontstyle], @params[:font], @params[:font_cjk], @params[:fontsize],
174
+ @params[:fontstyle], @params[:font], @params[:font_cjk], @params[:fontsize], @params[:margin], @params[:transparent]
167
175
  )
168
176
  graph.svg_data
169
177
  end
@@ -176,7 +184,7 @@ class RSGenerator
176
184
  graph = TreeGraph.new(elist, @metrics,
177
185
  @params[:symmetrize], @params[:color], @params[:leafstyle], @params[:multibyte],
178
186
  @params[:fontstyle], @params[:font], @params[:font_it], @params[:font_bd], @params[:font_bdit], @params[:font_math],
179
- @params[:font_cjk], @params[:fontsize], @params[:margin],
187
+ @params[:font_cjk], @params[:fontsize], @params[:margin], @params[:transparent]
180
188
  )
181
189
  graph.to_blob(@params[:format])
182
190
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rsyntaxtree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.4
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yoichiro Hasebe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-01 00:00:00.000000000 Z
11
+ date: 2021-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rmagick
@@ -67,13 +67,15 @@ files:
67
67
  - fonts/NotoSans-BoldItalic.ttf
68
68
  - fonts/NotoSans-Italic.ttf
69
69
  - fonts/NotoSans-Regular.ttf
70
- - fonts/NotoSansCJKjp-Regular.otf
70
+ - fonts/NotoSansJP-Bold.otf
71
+ - fonts/NotoSansJP-Regular.otf
71
72
  - fonts/NotoSansMath-Regular.ttf
72
73
  - fonts/NotoSerif-Bold.ttf
73
74
  - fonts/NotoSerif-BoldItalic.ttf
74
75
  - fonts/NotoSerif-Italic.ttf
75
76
  - fonts/NotoSerif-Regular.ttf
76
- - fonts/NotoSerifCJKjp-Regular.otf
77
+ - fonts/NotoSerifJP-Bold.otf
78
+ - fonts/NotoSerifJP-Regular.otf
77
79
  - fonts/latinmodern-math.otf
78
80
  - fonts/lmroman10-bold.otf
79
81
  - fonts/lmroman10-bolditalic.otf
@@ -91,7 +93,6 @@ files:
91
93
  - lib/rsyntaxtree/utils.rb
92
94
  - lib/rsyntaxtree/version.rb
93
95
  - rsyntaxtree.gemspec
94
- - syntree.svg
95
96
  homepage: http://github.com/yohasebe/rsyntaxtree
96
97
  licenses:
97
98
  - MIT
Binary file
Binary file
data/syntree.svg DELETED
@@ -1,44 +0,0 @@
1
- <?xml version="1.0" standalone="no"?>
2
- <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
3
- "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
- <svg width="1847.0" height="992.0" version="1.1" xmlns="http://www.w3.org/2000/svg">
5
- <text style='fill: green; font-size: 64px; ; ;' x='536.0' y='961.0' text-decoration='' font-family='sans-serif'>the</text>
6
- <text style='fill: green; font-size: 64px; ; ;' x='699.0' y='961.0' text-decoration='' font-family='sans-serif'>president</text>
7
- <text style='fill: green; font-size: 64px; ; ;' x='408.0' y='785.0' text-decoration='' font-family='sans-serif'>of</text>
8
- <text style='fill: blue; font-size: 64px; ; ;' x='562.0' y='785.0' text-decoration='' font-family='sans-serif'>D</text>
9
- <line style='stroke:black; stroke-width:2;' x1='586' y1='890' x2='586' y2='806' />
10
- <text style='fill: blue; font-size: 64px; ; ;' x='818.0' y='785.0' text-decoration='' font-family='sans-serif'>N</text>
11
- <line style='stroke:black; stroke-width:2;' x1='843' y1='890' x2='843' y2='806' />
12
- <text style='fill: green; font-size: 64px; ; ;' x='1243.0' y='785.0' text-decoration='' font-family='sans-serif'>blame</text>
13
- <text style='fill: green; font-size: 64px; ; ;' x='1495.0' y='785.0' text-decoration='' font-family='sans-serif'>* himself</text>
14
- <text style='fill: green; font-size: 64px; ; ;' x='10.0' y='609.0' text-decoration='line-through' font-family='sans-serif'>Supporters</text>
15
- <text style='fill: blue; font-size: 64px; ; ;' x='420.5' y='609.0' text-decoration='' font-family='sans-serif'>P</text>
16
- <line style='stroke:black; stroke-width:2;' x1='440' y1='714' x2='440' y2='630' />
17
- <text style='fill: blue; font-size: 64px; ; ;' x='671.0' y='609.0' text-decoration='' font-family='sans-serif'>DP</text>
18
- <line style='stroke:black; stroke-width:2;' x1='586' y1='714' x2='714' y2='630' />
19
- <line style='stroke:black; stroke-width:2;' x1='843' y1='714' x2='714' y2='630' />
20
- <text style='fill: green; font-size: 64px; ; ;' x='1050.0' y='609.0' text-decoration='' font-family='sans-serif'>may</text>
21
- <text style='fill: blue; font-size: 64px; ; ;' x='1317.5' y='609.0' text-decoration='' font-family='sans-serif'>V</text>
22
- <line style='stroke:black; stroke-width:2;' x1='1337' y1='714' x2='1337' y2='630' />
23
- <text style='fill: blue; font-size: 64px; ; ;' x='1570.0' y='609.0' text-decoration='' font-family='sans-serif'>PRN</text>
24
- <polygon style='fill: none; stroke: black; stroke-width:2;' points='1773 714 1495 714 1634 630' />
25
- <text style='fill: blue; font-size: 64px; ; ;' x='152.5' y='433.0' text-decoration='' font-family='sans-serif'>N</text>
26
- <line style='stroke:black; stroke-width:2;' x1='177' y1='538' x2='177' y2='454' />
27
- <text style='fill: blue; font-size: 64px; ; ;' x='538.0' y='433.0' text-decoration='' font-family='sans-serif'>PP</text>
28
- <line style='stroke:black; stroke-width:2;' x1='440' y1='538' x2='577' y2='454' />
29
- <line style='stroke:black; stroke-width:2;' x1='714' y1='538' x2='577' y2='454' />
30
- <text style='fill: blue; font-size: 64px; ; ;' x='1096.5' y='433.0' text-decoration='' font-family='sans-serif'>T</text>
31
- <line style='stroke:black; stroke-width:2;' x1='1115' y1='538' x2='1115' y2='454' />
32
- <text style='fill: blue; font-size: 64px; ; ;' x='1447.0' y='433.0' text-decoration='' font-family='sans-serif'>VP</text>
33
- <line style='stroke:black; stroke-width:2;' x1='1337' y1='538' x2='1486' y2='454' />
34
- <line style='stroke:black; stroke-width:2;' x1='1634' y1='538' x2='1486' y2='454' />
35
- <text style='fill: blue; font-size: 64px; ; ;' x='333.0' y='257.0' text-decoration='' font-family='sans-serif'>NP</text>
36
- <line style='stroke:black; stroke-width:2;' x1='177' y1='362' x2='377' y2='278' />
37
- <line style='stroke:black; stroke-width:2;' x1='577' y1='362' x2='377' y2='278' />
38
- <text style='fill: blue; font-size: 64px; ; ;' x='1275.0' y='257.0' text-decoration='' font-family='sans-serif'>T'</text>
39
- <line style='stroke:black; stroke-width:2;' x1='1115' y1='362' x2='1300' y2='278' />
40
- <line style='stroke:black; stroke-width:2;' x1='1486' y1='362' x2='1300' y2='278' />
41
- <text style='fill: blue; font-size: 64px; ; ;' x='801.0' y='81.0' text-decoration='' font-family='sans-serif'>TP</text>
42
- <line style='stroke:black; stroke-width:2;' x1='377' y1='186' x2='839' y2='102' />
43
- <line style='stroke:black; stroke-width:2;' x1='1300' y1='186' x2='839' y2='102' />
44
- </svg>