rsyntaxtree 1.0.8 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +77 -0
- data/.solargraph.yml +22 -0
- data/.tags +211 -10
- data/Gemfile +10 -5
- data/README.md +3 -2
- data/Rakefile +3 -1
- data/bin/rsyntaxtree +42 -50
- data/docs/Gemfile +3 -1
- data/docs/_layouts/default.html +1 -1
- data/lib/rsyntaxtree/base_graph.rb +260 -264
- data/lib/rsyntaxtree/element.rb +167 -179
- data/lib/rsyntaxtree/elementlist.rb +105 -124
- data/lib/rsyntaxtree/markup_parser.rb +82 -93
- data/lib/rsyntaxtree/string_parser.rb +221 -237
- data/lib/rsyntaxtree/svg_graph.rb +158 -197
- data/lib/rsyntaxtree/utils.rb +59 -63
- data/lib/rsyntaxtree/version.rb +3 -2
- data/lib/rsyntaxtree.rb +174 -177
- data/rsyntaxtree.gemspec +10 -10
- data/test/markup_parser_test.rb +3 -2
- metadata +23 -21
@@ -1,41 +1,41 @@
|
|
1
|
-
|
2
|
-
# -*- coding: utf-8 -*-
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
3
|
#==========================
|
5
4
|
# svg_graph.rb
|
6
5
|
#==========================
|
7
6
|
#
|
8
7
|
# Parses an element list into an SVG tree.
|
9
|
-
# Copyright (c) 2007-
|
8
|
+
# Copyright (c) 2007-2023 Yoichiro Hasebe <yohasebe@gmail.com>
|
10
9
|
|
11
10
|
require "tempfile"
|
12
|
-
|
13
|
-
|
11
|
+
require_relative 'base_graph'
|
12
|
+
require_relative 'utils'
|
14
13
|
|
15
14
|
module RSyntaxTree
|
16
15
|
class SVGGraph < BaseGraph
|
17
16
|
attr_accessor :width, :height
|
18
17
|
|
19
|
-
def initialize(element_list, params)
|
18
|
+
def initialize(element_list, params, global)
|
20
19
|
@height = 0
|
21
20
|
@width = 0
|
22
21
|
@extra_lines = []
|
23
|
-
@fontset
|
24
|
-
@fontsize
|
22
|
+
@fontset = params[:fontset]
|
23
|
+
@fontsize = params[:fontsize]
|
25
24
|
@transparent = params[:transparent]
|
26
25
|
@color = params[:color]
|
27
26
|
@fontstyle = params[:fontstyle]
|
28
27
|
@margin = params[:margin].to_i
|
29
28
|
@polyline = params[:polyline]
|
30
|
-
@line_styles
|
31
|
-
@polyline_styles
|
29
|
+
@line_styles = "<line style='stroke:black; stroke-width:#{FONT_SCALING};' x1='X1' y1='Y1' x2='X2' y2='Y2' />\n"
|
30
|
+
@polyline_styles = "<polyline style='stroke:black; stroke-width:#{FONT_SCALING}; fill:none;'
|
32
31
|
points='CHIX CHIY MIDX1 MIDY1 MIDX2 MIDY2 PARX PARY' />\n"
|
33
|
-
@polygon_styles
|
34
|
-
@text_styles
|
35
|
-
@tree_data
|
32
|
+
@polygon_styles = "<polygon style='fill: none; stroke: black; stroke-width:#{FONT_SCALING};' points='X1 Y1 X2 Y2 X3 Y3' />\n"
|
33
|
+
@text_styles = "<text white-space='pre' alignment-baseline='text-top' style='fill: COLOR; font-size: fontsize' x='X_VALUE' y='Y_VALUE'>CONTENT</text>\n"
|
34
|
+
@tree_data = String.new
|
36
35
|
@visited_x = {}
|
37
36
|
@visited_y = {}
|
38
|
-
|
37
|
+
@global = global
|
38
|
+
super(element_list, params, global)
|
39
39
|
end
|
40
40
|
|
41
41
|
def svg_data
|
@@ -49,32 +49,32 @@ module RSyntaxTree
|
|
49
49
|
y2 = @height + @margin
|
50
50
|
extra_lines = @extra_lines.join("\n")
|
51
51
|
|
52
|
-
as2
|
52
|
+
as2 = @global[:h_gap_between_nodes] / 2 * 0.8
|
53
53
|
as = as2 / 2
|
54
54
|
|
55
|
-
header
|
56
|
-
<?xml version="1.0" standalone="no"?>
|
57
|
-
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
rect
|
76
|
-
<rect x="#{x1}" y="#{y1}" width="#{x2}" height="#{y2}" stroke="none" fill="white" />"
|
77
|
-
|
55
|
+
header = <<~HDR
|
56
|
+
<?xml version="1.0" standalone="no"?>
|
57
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
58
|
+
<svg width="#{@width}" height="#{@height}" viewBox="#{x1}, #{y1}, #{x2}, #{y2}" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
59
|
+
<defs>
|
60
|
+
<marker id="arrow" markerUnits="strokeWidth" markerWidth="#{as2}" markerHeight="#{as2}" viewBox="0 0 #{as2} #{as2}" refX="#{as}" refY="0">
|
61
|
+
<polyline fill="none" stroke="#{@col_path}" stroke-width="1" points="0,#{as2},#{as},0,#{as2},#{as2}" />
|
62
|
+
</marker>
|
63
|
+
<pattern id="hatchBlack" x="10" y="10" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
|
64
|
+
<line x1="0" y="0" x2="0" y2="10" stroke="black" stroke-width="4"></line>
|
65
|
+
</pattern>
|
66
|
+
<pattern id="hatchForNode" x="10" y="10" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
|
67
|
+
<line x1="0" y="0" x2="0" y2="10" stroke="#{@col_node}" stroke-width="4"></line>
|
68
|
+
</pattern>
|
69
|
+
<pattern id="hatchForLeaf" x="10" y="10" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
|
70
|
+
<line x1="0" y="0" x2="0" y2="10" stroke="#{@col_leaf}" stroke-width="4"></line>
|
71
|
+
</pattern>
|
72
|
+
</defs>
|
73
|
+
HDR
|
74
|
+
|
75
|
+
rect = <<~RCT
|
76
|
+
<rect x="#{x1}" y="#{y1}" width="#{x2}" height="#{y2}" stroke="none" fill="white" />"
|
77
|
+
RCT
|
78
78
|
|
79
79
|
footer = "</svg>"
|
80
80
|
|
@@ -86,17 +86,15 @@ EOD
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def draw_a_path(s_x, s_y, t_x, t_y, target_arrow = :none)
|
89
|
-
|
90
|
-
|
91
|
-
y_spacing = $height_connector * 0.65
|
89
|
+
x_spacing = @global[:h_gap_between_nodes] * 1.25
|
90
|
+
y_spacing = @global[:height_connector] * 0.65
|
92
91
|
|
93
92
|
ymax = [s_y, t_y].max
|
94
|
-
if ymax
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
93
|
+
new_y = if ymax < @height
|
94
|
+
@height + y_spacing
|
95
|
+
else
|
96
|
+
ymax + y_spacing
|
97
|
+
end
|
100
98
|
|
101
99
|
if @visited_x[s_x]
|
102
100
|
new_s_x = s_x - x_spacing * @visited_x[s_x]
|
@@ -114,67 +112,64 @@ EOD
|
|
114
112
|
@visited_x[t_x] = 1
|
115
113
|
end
|
116
114
|
|
117
|
-
s_y +=
|
118
|
-
t_y +=
|
119
|
-
new_y +=
|
115
|
+
s_y += @global[:h_gap_between_nodes] / 2
|
116
|
+
t_y += @global[:h_gap_between_nodes] / 2
|
117
|
+
new_y += @global[:h_gap_between_nodes] / 2
|
120
118
|
|
121
119
|
dashed = true if target_arrow == :none
|
122
120
|
|
123
|
-
|
121
|
+
case target_arrow
|
122
|
+
when :single
|
124
123
|
@extra_lines << generate_line(new_s_x, s_y, new_s_x, new_y, @col_path, dashed)
|
125
124
|
@extra_lines << generate_line(new_s_x, new_y, new_t_x, new_y, @col_path, dashed)
|
126
|
-
@extra_lines << generate_line(new_t_x, new_y, new_t_x, t_y, @col_path
|
127
|
-
|
125
|
+
@extra_lines << generate_line(new_t_x, new_y, new_t_x, t_y, @col_path, dashed, true)
|
126
|
+
when :double
|
128
127
|
@extra_lines << generate_line(new_s_x, new_y, new_s_x, s_y, @col_path, dashed, true)
|
129
128
|
@extra_lines << generate_line(new_s_x, new_y, new_t_x, new_y, @col_path, dashed)
|
130
|
-
@extra_lines << generate_line(new_t_x, new_y, new_t_x, t_y, @col_path
|
129
|
+
@extra_lines << generate_line(new_t_x, new_y, new_t_x, t_y, @col_path, dashed, true)
|
131
130
|
else
|
132
131
|
@extra_lines << generate_line(new_s_x, s_y, new_s_x, new_y, @col_path, dashed)
|
133
132
|
@extra_lines << generate_line(new_s_x, new_y, new_t_x, new_y, @col_path, dashed)
|
134
|
-
@extra_lines << generate_line(new_t_x, new_y, new_t_x, t_y, @col_path
|
133
|
+
@extra_lines << generate_line(new_t_x, new_y, new_t_x, t_y, @col_path, dashed)
|
135
134
|
end
|
136
135
|
|
137
136
|
@height = new_y if new_y > @height
|
138
137
|
end
|
139
138
|
|
140
139
|
def draw_element(element)
|
141
|
-
top
|
142
|
-
|
143
|
-
|
144
|
-
bottom = top +$single_line_height
|
145
|
-
right = left + element.content_width
|
146
|
-
|
140
|
+
top = element.vertical_indent
|
141
|
+
left = element.horizontal_indent
|
142
|
+
right = left + element.content_width
|
147
143
|
txt_pos = left + (right - left) / 2
|
148
144
|
|
149
|
-
if
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
145
|
+
col = if element.type == ETYPE_LEAF
|
146
|
+
@col_leaf
|
147
|
+
else
|
148
|
+
@col_node
|
149
|
+
end
|
154
150
|
|
155
151
|
text_data = @text_styles.sub(/COLOR/, col)
|
156
152
|
text_data = text_data.sub(/fontsize/, @fontsize.to_s + "px;")
|
157
153
|
text_x = txt_pos - element.content_width / 2
|
158
|
-
text_y = top +
|
154
|
+
text_y = top + @global[:single_line_height] - @global[:height_connector_to_text]
|
159
155
|
text_data = text_data.sub(/X_VALUE/, text_x.to_s)
|
160
156
|
text_data = text_data.sub(/Y_VALUE/, text_y.to_s)
|
161
|
-
new_text = ""
|
157
|
+
new_text = +""
|
162
158
|
this_x = 0
|
163
159
|
this_y = 0
|
164
|
-
bc = {:
|
160
|
+
bc = { x: text_x - @global[:h_gap_between_nodes] / 2, y: top, width: element.content_width + @global[:h_gap_between_nodes], height: nil }
|
165
161
|
element.content.each_with_index do |l, idx|
|
166
162
|
case l[:type]
|
167
163
|
when :border, :bborder
|
168
164
|
x1 = text_x
|
169
|
-
if idx
|
165
|
+
if idx.zero?
|
170
166
|
text_y -= l[:height]
|
171
|
-
|
167
|
+
else
|
172
168
|
text_y += l[:height]
|
173
169
|
end
|
174
|
-
y1 = text_y -
|
170
|
+
y1 = text_y - @global[:single_line_height] / 8
|
175
171
|
x2 = text_x + element.content_width
|
176
172
|
y2 = y1
|
177
|
-
this_width = x2 - x1
|
178
173
|
case l[:type]
|
179
174
|
when :border
|
180
175
|
stroke_width = FONT_SCALING
|
@@ -192,31 +187,23 @@ EOD
|
|
192
187
|
end
|
193
188
|
this_x = txt_pos - (ewidth / 2)
|
194
189
|
end
|
195
|
-
text_y += l[:elements].map{|e| e[:height]}.max if idx != 0
|
190
|
+
text_y += l[:elements].map { |e| e[:height] }.max if idx != 0
|
196
191
|
|
197
|
-
l[:elements].
|
192
|
+
l[:elements].each do |e|
|
198
193
|
escaped_text = e[:text].gsub('>', '>').gsub('<', '<');
|
199
194
|
decorations = []
|
200
|
-
if e[:decoration].include?(:overline)
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
if e[:decoration].include?(:underline)
|
205
|
-
decorations << "underline"
|
206
|
-
end
|
207
|
-
|
208
|
-
if e[:decoration].include?(:linethrough)
|
209
|
-
decorations << "line-through"
|
210
|
-
end
|
211
|
-
decoration ="text-decoration=\"" + decorations.join(" ") + "\""
|
195
|
+
decorations << "overline" if e[:decoration].include?(:overline)
|
196
|
+
decorations << "underline" if e[:decoration].include?(:underline)
|
197
|
+
decorations << "line-through" if e[:decoration].include?(:linethrough)
|
198
|
+
decoration = "text-decoration=\"" + decorations.join(" ") + "\""
|
212
199
|
|
213
200
|
style = "style=\""
|
214
201
|
if e[:decoration].include?(:small)
|
215
202
|
style += "font-size: #{(SUBSCRIPT_CONST.to_f * 100).to_i}%; "
|
216
|
-
this_y = text_y - ((
|
203
|
+
this_y = text_y - ((@global[:single_x_metrics].height - @global[:single_x_metrics].height * SUBSCRIPT_CONST) / 4) + 2
|
217
204
|
elsif e[:decoration].include?(:superscript)
|
218
205
|
style += "font-size: #{(SUBSCRIPT_CONST.to_f * 100).to_i}%; "
|
219
|
-
this_y = text_y - (
|
206
|
+
this_y = text_y - (@global[:single_x_metrics].height / 4) + 1
|
220
207
|
elsif e[:decoration].include?(:subscript)
|
221
208
|
style += "font-size: #{(SUBSCRIPT_CONST.to_f * 100).to_i}%; "
|
222
209
|
this_y = text_y + 4
|
@@ -224,72 +211,59 @@ EOD
|
|
224
211
|
this_y = text_y
|
225
212
|
end
|
226
213
|
|
227
|
-
if e[:decoration].include?(:bold) || e[:decoration].include?(:bolditalic)
|
228
|
-
|
229
|
-
end
|
230
|
-
|
231
|
-
if e[:decoration].include?(:italic) || e[:decoration].include?(:bolditalic)
|
232
|
-
style += "font-style: italic; "
|
233
|
-
end
|
234
|
-
|
214
|
+
style += "font-weight: bold; " if e[:decoration].include?(:bold) || e[:decoration].include?(:bolditalic)
|
215
|
+
style += "font-style: italic; " if e[:decoration].include?(:italic) || e[:decoration].include?(:bolditalic)
|
235
216
|
style += "\""
|
236
217
|
|
237
218
|
case @fontstyle
|
238
219
|
when /(?:cjk)/
|
239
220
|
fontstyle = "'WenQuanYi Zen Hei', 'Noto Sans', OpenMoji, 'OpenMoji Color', 'OpenMoji Black', sans-serif"
|
240
221
|
when /(?:sans)/
|
241
|
-
if e[:cjk]
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
222
|
+
fontstyle = if e[:cjk]
|
223
|
+
"'Noto Sans JP', 'Noto Sans', OpenMoji, 'OpenMoji Color', 'OpenMoji Black', sans-serif"
|
224
|
+
else
|
225
|
+
"'Noto Sans', 'Noto Sans JP', OpenMoji, 'OpenMoji Color', 'OpenMoji Black', sans-serif"
|
226
|
+
end
|
246
227
|
when /(?:serif)/
|
247
|
-
if e[:cjk]
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
228
|
+
fontstyle = if e[:cjk]
|
229
|
+
"'Noto Serif JP', 'Noto Serif', OpenMoji, 'OpenMoji Color', 'OpenMoji Black', serif"
|
230
|
+
else
|
231
|
+
"'Noto Serif', 'Noto Serif JP', OpenMoji, 'OpenMoji Color', 'OpenMoji Black', serif"
|
232
|
+
end
|
252
233
|
end
|
253
234
|
|
254
235
|
if e[:decoration].include?(:box) || e[:decoration].include?(:circle) || e[:decoration].include?(:bar)
|
255
236
|
enc_height = e[:height]
|
256
237
|
enc_y = this_y - e[:height] * 0.8 + FONT_SCALING
|
257
|
-
|
258
|
-
|
259
|
-
enc_width = e[:width]
|
260
|
-
enc_x = this_x
|
261
|
-
else
|
262
|
-
enc_width = e[:width]
|
263
|
-
enc_x = this_x
|
264
|
-
end
|
238
|
+
enc_width = e[:width]
|
239
|
+
enc_x = this_x
|
265
240
|
|
266
241
|
if e[:decoration].include?(:hatched)
|
267
242
|
case element.type
|
268
243
|
when ETYPE_LEAF
|
269
|
-
if @color
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
244
|
+
fill = if @color
|
245
|
+
"url(#hatchForLeaf)"
|
246
|
+
else
|
247
|
+
"url(#hatchBlack)"
|
248
|
+
end
|
274
249
|
when ETYPE_NODE
|
275
|
-
if @color
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
250
|
+
fill = if @color
|
251
|
+
"url(#hatchForNode)"
|
252
|
+
else
|
253
|
+
"url(#hatchBlack)"
|
254
|
+
end
|
280
255
|
end
|
281
256
|
else
|
282
257
|
fill = "none"
|
283
258
|
end
|
284
259
|
|
285
260
|
enc = nil
|
286
|
-
bar = nil
|
287
261
|
|
288
|
-
if e[:decoration].include?(:bstroke)
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
262
|
+
stroke_width = if e[:decoration].include?(:bstroke)
|
263
|
+
FONT_SCALING * 2.5
|
264
|
+
else
|
265
|
+
FONT_SCALING
|
266
|
+
end
|
293
267
|
|
294
268
|
if e[:decoration].include?(:box)
|
295
269
|
enc = "<rect style='stroke: #{col}; stroke-width:#{stroke_width};'
|
@@ -319,25 +293,22 @@ EOD
|
|
319
293
|
r_arrowhead = "<polyline stroke-linejoin='bevel' fill='none' stroke='#{col}' stroke-width='#{stroke_width}' points='#{x2 - ar_hwidth},#{y2 - ar_hwidth / 2} #{x2},#{y2} #{x2 - ar_hwidth},#{y2 + ar_hwidth / 2}' />\n"
|
320
294
|
@extra_lines << r_arrowhead
|
321
295
|
end
|
322
|
-
|
323
|
-
|
324
296
|
end
|
325
297
|
|
326
298
|
@extra_lines << enc if enc
|
327
299
|
|
328
|
-
if e[:text].size == 1
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
300
|
+
this_x += if e[:text].size == 1
|
301
|
+
(e[:height] - e[:content_width]) / 2
|
302
|
+
else
|
303
|
+
@global[:width_half_x] / 2
|
304
|
+
end
|
305
|
+
|
333
306
|
new_text << set_tspan(this_x, this_y, style, decoration, fontstyle, escaped_text)
|
334
|
-
if e[:text].size == 1
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
this_x += $width_half_X / 2
|
340
|
-
end
|
307
|
+
this_x += if e[:text].size == 1
|
308
|
+
e[:content_width] + (e[:height] - e[:content_width]) / 2
|
309
|
+
else
|
310
|
+
e[:content_width] + @global[:width_half_x] / 2
|
311
|
+
end
|
341
312
|
|
342
313
|
elsif e[:decoration].include?(:whitespace)
|
343
314
|
this_x += e[:width]
|
@@ -346,26 +317,26 @@ EOD
|
|
346
317
|
new_text << set_tspan(this_x, this_y, style, decoration, fontstyle, escaped_text)
|
347
318
|
this_x += e[:width]
|
348
319
|
end
|
349
|
-
|
350
320
|
end
|
351
321
|
end
|
352
322
|
@height = text_y if text_y > @height
|
353
323
|
end
|
354
|
-
bc[:y] = bc[:y] +
|
355
|
-
bc[:height] = text_y - bc[:y] +
|
356
|
-
|
357
|
-
|
324
|
+
bc[:y] = bc[:y] + @global[:height_connector_to_text] * 3 / 4
|
325
|
+
bc[:height] = text_y - bc[:y] + @global[:height_connector_to_text]
|
326
|
+
case element.enclosure
|
327
|
+
when :brackets
|
328
|
+
@extra_lines << generate_line(bc[:x], bc[:y], bc[:x] + @global[:h_gap_between_nodes] / 2, bc[:y], col)
|
358
329
|
@extra_lines << generate_line(bc[:x], bc[:y], bc[:x], bc[:y] + bc[:height], col)
|
359
|
-
@extra_lines << generate_line(bc[:x], bc[:y] + bc[:height], bc[:x] +
|
360
|
-
@extra_lines << generate_line(bc[:x] + bc[:width], bc[:y], bc[:x] + bc[:width] -
|
330
|
+
@extra_lines << generate_line(bc[:x], bc[:y] + bc[:height], bc[:x] + @global[:h_gap_between_nodes] / 2, bc[:y] + bc[:height], col)
|
331
|
+
@extra_lines << generate_line(bc[:x] + bc[:width], bc[:y], bc[:x] + bc[:width] - @global[:h_gap_between_nodes] / 2, bc[:y], col)
|
361
332
|
@extra_lines << generate_line(bc[:x] + bc[:width], bc[:y], bc[:x] + bc[:width], bc[:y] + bc[:height], col)
|
362
|
-
@extra_lines << generate_line(bc[:x] + bc[:width], bc[:y] + bc[:height], bc[:x] + bc[:width] -
|
363
|
-
|
333
|
+
@extra_lines << generate_line(bc[:x] + bc[:width], bc[:y] + bc[:height], bc[:x] + bc[:width] - @global[:h_gap_between_nodes] / 2, bc[:y] + bc[:height], col)
|
334
|
+
when :rectangle
|
364
335
|
@extra_lines << generate_line(bc[:x], bc[:y], bc[:x] + bc[:width], bc[:y], col)
|
365
336
|
@extra_lines << generate_line(bc[:x], bc[:y], bc[:x], bc[:y] + bc[:height], col)
|
366
337
|
@extra_lines << generate_line(bc[:x] + bc[:width], bc[:y], bc[:x] + bc[:width], bc[:y] + bc[:height], col)
|
367
338
|
@extra_lines << generate_line(bc[:x], bc[:y] + bc[:height], bc[:x] + bc[:width], bc[:y] + bc[:height], col)
|
368
|
-
|
339
|
+
when :brectangle
|
369
340
|
@extra_lines << generate_line(bc[:x], bc[:y], bc[:x] + bc[:width], bc[:y], col, false, false, 2)
|
370
341
|
@extra_lines << generate_line(bc[:x], bc[:y], bc[:x], bc[:y] + bc[:height], col, false, false, 2)
|
371
342
|
@extra_lines << generate_line(bc[:x] + bc[:width], bc[:y], bc[:x] + bc[:width], bc[:y] + bc[:height], col, false, false, 2)
|
@@ -384,9 +355,7 @@ EOD
|
|
384
355
|
"<tspan x='#{this_x}' y='#{this_y}' #{style} #{decoration} font-family=\"#{fontstyle}\">#{text}</tspan>\n"
|
385
356
|
end
|
386
357
|
|
387
|
-
|
388
358
|
def draw_paths
|
389
|
-
rockbottom = 0
|
390
359
|
path_pool_target = {}
|
391
360
|
path_pool_other = {}
|
392
361
|
path_pool_source = {}
|
@@ -397,7 +366,7 @@ EOD
|
|
397
366
|
elist.each do |element|
|
398
367
|
x1 = element.horizontal_indent + element.content_width / 2
|
399
368
|
y1 = element.vertical_indent + element.content_height
|
400
|
-
y1 +=
|
369
|
+
y1 += @global[:height_connector_to_text] if element.enclosure != :none
|
401
370
|
et = element.path
|
402
371
|
et.each do |tr|
|
403
372
|
if /\A>(\d+)\z/ =~ tr
|
@@ -417,28 +386,24 @@ EOD
|
|
417
386
|
path_pool_source[tr] = [x1, y1]
|
418
387
|
end
|
419
388
|
path_flags << tr
|
420
|
-
if path_flags.tally.any?{|
|
421
|
-
raise RSTError, "Error: input text contains a path having more than two ends:\n > #{tr}"
|
422
|
-
end
|
389
|
+
raise RSTError, "Error: input text contains a path having more than two ends:\n > #{tr}" if path_flags.tally.any? { |_k, v| v > 2 }
|
423
390
|
end
|
424
391
|
end
|
425
392
|
|
426
393
|
path_flags.tally.each do |k, v|
|
427
|
-
if v == 1
|
428
|
-
raise RSTError, "Error: input text contains a path having only one end:\n > #{k}"
|
429
|
-
end
|
394
|
+
raise RSTError, "Error: input text contains a path having only one end:\n > #{k}" if v == 1
|
430
395
|
end
|
431
396
|
|
432
397
|
paths = []
|
433
398
|
path_pool_source.each do |k, v|
|
434
399
|
path_flags.delete(k)
|
435
|
-
if targets = path_pool_target[k]
|
400
|
+
if (targets = path_pool_target[k])
|
436
401
|
targets.each do |t|
|
437
|
-
paths << {x1: v[0], y1: v[1], x2: t[0], y2: t[1], arrow: :single}
|
402
|
+
paths << { x1: v[0], y1: v[1], x2: t[0], y2: t[1], arrow: :single }
|
438
403
|
end
|
439
|
-
elsif others = path_pool_other[k]
|
404
|
+
elsif (others = path_pool_other[k])
|
440
405
|
others.each do |t|
|
441
|
-
paths << {x1: v[0], y1: v[1], x2: t[0], y2: t[1], arrow: :none}
|
406
|
+
paths << { x1: v[0], y1: v[1], x2: t[0], y2: t[1], arrow: :none }
|
442
407
|
end
|
443
408
|
end
|
444
409
|
end
|
@@ -447,13 +412,13 @@ EOD
|
|
447
412
|
targets = path_pool_target[k]
|
448
413
|
fst = targets.shift
|
449
414
|
targets.each do |t|
|
450
|
-
paths << {x1: fst[0], y1: fst[1], x2: t[0], y2: t[1], arrow: :double}
|
415
|
+
paths << { x1: fst[0], y1: fst[1], x2: t[0], y2: t[1], arrow: :double }
|
451
416
|
end
|
452
417
|
end
|
453
418
|
|
454
419
|
paths.each do |t|
|
455
|
-
draw_a_path(t[:x1], t[:y1] +
|
456
|
-
t[:x2], t[:y2] +
|
420
|
+
draw_a_path(t[:x1], t[:y1] + @global[:height_connector_to_text] / 2,
|
421
|
+
t[:x2], t[:y2] + @global[:height_connector_to_text] / 2,
|
457
422
|
t[:arrow])
|
458
423
|
end
|
459
424
|
|
@@ -461,11 +426,11 @@ EOD
|
|
461
426
|
end
|
462
427
|
|
463
428
|
def generate_line(x1, y1, x2, y2, col, dashed = false, arrow = false, stroke_width = 1)
|
464
|
-
if arrow
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
429
|
+
string = if arrow
|
430
|
+
"marker-end='url(#arrow)' "
|
431
|
+
else
|
432
|
+
""
|
433
|
+
end
|
469
434
|
dasharray = dashed ? "stroke-dasharray='8 8'" : ""
|
470
435
|
swidth = FONT_SCALING * stroke_width
|
471
436
|
|
@@ -474,36 +439,34 @@ EOD
|
|
474
439
|
|
475
440
|
# Draw a line between child/parent elements
|
476
441
|
def line_to_parent(parent, child)
|
477
|
-
if
|
478
|
-
return
|
479
|
-
end
|
442
|
+
return if child.horizontal_indent.zero?
|
480
443
|
|
481
444
|
if @polyline
|
482
445
|
chi_x = child.horizontal_indent + child.content_width / 2
|
483
|
-
chi_y = child.vertical_indent +
|
446
|
+
chi_y = child.vertical_indent + @global[:height_connector_to_text] / 2
|
484
447
|
|
485
448
|
par_x = parent.horizontal_indent + parent.content_width / 2
|
486
|
-
par_y = parent.vertical_indent + parent.content_height +
|
449
|
+
par_y = parent.vertical_indent + parent.content_height + @global[:height_connector_to_text]
|
487
450
|
|
488
451
|
mid_x1 = chi_x
|
489
|
-
mid_y1 = par_y
|
452
|
+
mid_y1 = par_y + (chi_y - par_y) / 2
|
490
453
|
|
491
454
|
mid_x2 = par_x
|
492
455
|
mid_y2 = mid_y1
|
493
456
|
|
494
457
|
@tree_data += @polyline_styles.sub(/CHIX/, chi_x.to_s)
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
458
|
+
.sub(/CHIY/, chi_y.to_s)
|
459
|
+
.sub(/MIDX1/, mid_x1.to_s)
|
460
|
+
.sub(/MIDY1/, mid_y1.to_s)
|
461
|
+
.sub(/MIDX2/, mid_x2.to_s)
|
462
|
+
.sub(/MIDY2/, mid_y2.to_s)
|
463
|
+
.sub(/PARX/, par_x.to_s)
|
464
|
+
.sub(/PARY/, par_y.to_s)
|
502
465
|
else
|
503
466
|
x1 = child.horizontal_indent + child.content_width / 2
|
504
|
-
y1 = child.vertical_indent +
|
467
|
+
y1 = child.vertical_indent + @global[:height_connector_to_text] / 2
|
505
468
|
x2 = parent.horizontal_indent + parent.content_width / 2
|
506
|
-
y2 = parent.vertical_indent + parent.content_height +
|
469
|
+
y2 = parent.vertical_indent + parent.content_height + @global[:height_connector_to_text]
|
507
470
|
|
508
471
|
line_data = @line_styles.sub(/X1/, x1.to_s)
|
509
472
|
line_data = line_data.sub(/Y1/, y1.to_s)
|
@@ -514,16 +477,14 @@ EOD
|
|
514
477
|
|
515
478
|
# Draw a triangle between child/parent elements
|
516
479
|
def triangle_to_parent(parent, child)
|
517
|
-
if
|
518
|
-
return
|
519
|
-
end
|
480
|
+
return if child.horizontal_indent.zero?
|
520
481
|
|
521
482
|
x1 = child.horizontal_indent
|
522
|
-
y1 = child.vertical_indent +
|
483
|
+
y1 = child.vertical_indent + @global[:height_connector_to_text] / 2
|
523
484
|
x2 = child.horizontal_indent + child.content_width
|
524
|
-
y2 = child.vertical_indent +
|
485
|
+
y2 = child.vertical_indent + @global[:height_connector_to_text] / 2
|
525
486
|
x3 = parent.horizontal_indent + parent.content_width / 2
|
526
|
-
y3 = parent.vertical_indent + parent.content_height +
|
487
|
+
y3 = parent.vertical_indent + parent.content_height + @global[:height_connector_to_text]
|
527
488
|
|
528
489
|
polygon_data = @polygon_styles.sub(/X1/, x1.to_s)
|
529
490
|
polygon_data = polygon_data.sub(/Y1/, y1.to_s)
|