rsyntaxtree 0.8.5 → 0.9.2
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 +4 -4
- data/bin/rsyntaxtree +4 -1
- data/fonts/NotoSansJP-Bold.otf +0 -0
- data/fonts/NotoSansJP-Regular.otf +0 -0
- data/fonts/NotoSansMath-Regular.ttf +0 -0
- data/fonts/NotoSerifJP-Bold.otf +0 -0
- data/fonts/NotoSerifJP-Regular.otf +0 -0
- data/lib/rsyntaxtree/element.rb +3 -3
- data/lib/rsyntaxtree/error_message.rb +1 -1
- data/lib/rsyntaxtree/graph.rb +65 -45
- data/lib/rsyntaxtree/string_parser.rb +9 -2
- data/lib/rsyntaxtree/svg_graph.rb +114 -59
- data/lib/rsyntaxtree/tree_graph.rb +54 -26
- data/lib/rsyntaxtree/version.rb +1 -1
- data/lib/rsyntaxtree.rb +37 -29
- metadata +6 -4
- data/fonts/NotoSansCJKjp-Regular.otf +0 -0
- data/fonts/NotoSerifCJKjp-Regular.otf +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e72c92c9014be74d57f4406e6f1e143b3f9e8dffc32f48ecfebd4a0adbf6a4e2
|
4
|
+
data.tar.gz: 4efd624a82d601829b48ed8fedec7265d65b5bc353deb5356ac37a6eefa0a7da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b26b81c9ebfec90f73e66cf562ad664d8081291fd869c4e9913220ab24e90fe81f081c729abf35f32af4141084ae26e8c29af97cb1a1e0e5be165900d358cc45
|
7
|
+
data.tar.gz: 7f08d17d331d2510357bb586d5c70190418c134654d1fbdac6c2a0b7b0c2716caabf8762ed231eabf627e59d5b07cc77b3e40d46e2ce1f78f90852122a166a32
|
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
|
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
|
data/lib/rsyntaxtree/element.rb
CHANGED
@@ -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
|
-
|
27
|
-
if /\A.+\^\z/ =~ content
|
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
|
data/lib/rsyntaxtree/graph.rb
CHANGED
@@ -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
|
36
|
-
@width
|
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
|
@@ -58,7 +58,6 @@ 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
|
@@ -67,6 +66,9 @@ class Graph
|
|
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(
|
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(
|
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
|
-
|
116
|
+
x = calc_element_width(e)
|
117
|
+
w += x
|
114
118
|
end
|
115
|
-
|
119
|
+
e = @e_list.get_next
|
116
120
|
end
|
117
121
|
return w
|
118
122
|
end
|
@@ -168,30 +172,31 @@ 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 == "
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
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
|
-
|
194
|
+
elements_to_draw = {}
|
195
|
+
triangles_to_draw = []
|
196
|
+
lines_to_draw = []
|
197
|
+
|
198
|
+
lmost = {:level => nil, :value => nil, :type => nil}
|
199
|
+
rmost = nil
|
195
200
|
h.times do |i|
|
196
201
|
curlevel = h - i - 1
|
197
202
|
e_arr.each_with_index do |j, idx|
|
@@ -209,7 +214,7 @@ class Graph
|
|
209
214
|
right = k.indent + kw / 2 if k.indent + kw / 2 > right
|
210
215
|
end
|
211
216
|
|
212
|
-
|
217
|
+
elements_to_draw[j.id] = {:left => left, :curlevel => curlevel, :width => right - left, :content => j.content, :type => j.type}
|
213
218
|
@e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
|
214
219
|
|
215
220
|
children.each do |child|
|
@@ -223,12 +228,12 @@ class Graph
|
|
223
228
|
if (@leafstyle == "auto" && ETYPE_LEAF == k.type)
|
224
229
|
if words.length > 1 || k.triangle
|
225
230
|
txt_width = img_get_txt_width(k.content, @font, @font_size)
|
226
|
-
|
231
|
+
triangles_to_draw << {:indent => k.indent, :curlevel => curlevel + 1, :width1 => dw, :width2 => txt_width}
|
227
232
|
else
|
228
|
-
|
233
|
+
lines_to_draw << {:indent1 => k.indent, :curlevel => curlevel + 1, :width1 => dw, :indent2 => j.indent, :width2 => tw}
|
229
234
|
end
|
230
235
|
else
|
231
|
-
|
236
|
+
lines_to_draw << {:indent1 => k.indent, :curlevel => curlevel + 1, :width1 => dw, :indent2 => j.indent, :width2 => tw}
|
232
237
|
end
|
233
238
|
end
|
234
239
|
end
|
@@ -237,32 +242,46 @@ class Graph
|
|
237
242
|
elements = e_arr.select do |l|
|
238
243
|
l.level == curlevel && @e_list.get_children(l.id).empty?
|
239
244
|
end
|
240
|
-
|
245
|
+
|
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}
|
252
|
+
end
|
253
|
+
end
|
241
254
|
end
|
242
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
|
243
268
|
end
|
244
|
-
end
|
245
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
|
246
276
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
lw = img_get_txt_width(l.content, @font, @font_size)
|
256
|
-
left = l.indent
|
257
|
-
right = left + lw
|
258
|
-
# if pw > tw
|
259
|
-
# left = pleft
|
260
|
-
# right = pright
|
261
|
-
# end
|
262
|
-
unless @check.include? l.id
|
263
|
-
draw_element(left, curlevel, right - left, l.content, l.type)
|
264
|
-
@check << l.id
|
265
|
-
end
|
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])
|
266
285
|
end
|
267
286
|
end
|
268
287
|
|
@@ -284,8 +303,9 @@ class Graph
|
|
284
303
|
return text
|
285
304
|
end
|
286
305
|
|
287
|
-
def img_get_txt_height(text, font, font_size, multiline =
|
306
|
+
def img_get_txt_height(text, font, font_size, multiline = true)
|
288
307
|
metrics = img_get_txt_metrics(text, font, font_size, multiline)
|
308
|
+
# y = (metrics.bounds.y2 - metrics.bounds.y1).round
|
289
309
|
y = metrics.height
|
290
310
|
return y
|
291
311
|
end
|
@@ -171,8 +171,15 @@ class StringParser
|
|
171
171
|
end
|
172
172
|
when "\\"
|
173
173
|
escape = true
|
174
|
-
when
|
175
|
-
|
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
|
29
|
-
|
30
|
-
|
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
|
-
|
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:
|
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="#{
|
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
|
-
|
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 =
|
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
|
-
|
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]
|
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
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
sub_data = sub_data.sub(/X_VALUE/, sub_x.
|
210
|
-
sub_data = sub_data.sub(/Y_VALUE/, sub_y.
|
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 = txt_pos + (main_width / 2) - (sub_width / 2)
|
266
|
+
sub_y = main_y + sub_height / 6
|
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/,
|
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.
|
231
|
-
line_data = line_data.sub(/Y1/, fromTop.
|
232
|
-
line_data = line_data.sub(/X2/, toLeft.
|
233
|
-
@tree_data += line_data.sub(/Y2/, toBot.
|
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)
|
247
|
-
fromLeft1 = (fromCenter + textW / 2)
|
248
|
-
fromLeft2 = (fromCenter - textW / 2)
|
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.
|
258
|
-
polygon_data = polygon_data.sub(/Y1/, fromTop.
|
259
|
-
polygon_data = polygon_data.sub(/X2/, fromLeft2.
|
260
|
-
polygon_data = polygon_data.sub(/Y2/, fromTop.
|
261
|
-
polygon_data = polygon_data.sub(/X3/, toLeft.
|
262
|
-
@tree_data += polygon_data.sub(/Y3/, toBot.
|
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 =
|
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
|
-
|
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
|
28
|
-
@font
|
29
|
-
@font_size
|
30
|
-
@font_it
|
31
|
-
@font_bd
|
32
|
-
@font_itbd
|
33
|
-
@font_math
|
34
|
-
@font_cjk
|
35
|
-
@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.
|
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
|
-
|
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
|
@@ -174,14 +190,14 @@ class TreeGraph < Graph
|
|
174
190
|
|
175
191
|
if sub != ""
|
176
192
|
sub_width = img_get_txt_width(sub.to_s, sub_font, @sub_size)
|
193
|
+
sub_height = img_get_txt_height(sub.to_s, sub_font, @sub_size)
|
177
194
|
else
|
178
195
|
sub_width = 0
|
196
|
+
sub_height = 0
|
179
197
|
end
|
180
198
|
|
181
199
|
# Center text in the element
|
182
|
-
|
183
|
-
|
184
|
-
txt_pos = left + (right - left) / 2 - txt_width / 2
|
200
|
+
txt_pos = left + (right - left) / 2
|
185
201
|
|
186
202
|
# Select apropriate color
|
187
203
|
if(type == ETYPE_LEAF)
|
@@ -199,22 +215,33 @@ class TreeGraph < Graph
|
|
199
215
|
|
200
216
|
# Draw main text
|
201
217
|
@gc.pointsize(@font_size)
|
202
|
-
|
218
|
+
@gc.kerning = 0
|
219
|
+
@gc.interline_spacing = 0
|
220
|
+
@gc.interword_spacing = 0
|
221
|
+
|
222
|
+
main_x = txt_pos - sub_width / 2
|
203
223
|
main_y = top + @e_height - @m[:e_padd]
|
204
224
|
|
205
|
-
@gc.interline_spacing = -(@main_height / 3)
|
225
|
+
# @gc.interline_spacing = -(@main_height / 3)
|
206
226
|
@gc.font(main_font)
|
207
227
|
@gc.decorate(main_decoration)
|
208
|
-
|
228
|
+
numlines = main.count("\\n")
|
229
|
+
if numlines > 1
|
230
|
+
@height = @height + @main_height * numlines
|
231
|
+
end
|
232
|
+
@gc.text_align(CenterAlign)
|
233
|
+
@gc.text(main_x.ceil, main_y.ceil, main.gsub("\\n", "\n"))
|
209
234
|
|
210
235
|
# Draw subscript text
|
211
|
-
if (sub
|
236
|
+
if (sub != "" )
|
212
237
|
@gc.pointsize(@sub_size)
|
213
|
-
sub_x =
|
214
|
-
sub_y = top +
|
238
|
+
sub_x = main_x + (main_width / 2) + (sub_width / 2)
|
239
|
+
sub_y = top + main_height + sub_height / 4
|
240
|
+
@height += sub_height / 4
|
215
241
|
@gc.font(sub_font)
|
216
242
|
@gc.decorate(sub_decoration)
|
217
|
-
@gc.
|
243
|
+
@gc.text_align(CenterAlign)
|
244
|
+
@gc.text(sub_x.ceil, sub_y.ceil, " " + sub.gsub("\\n", "\n"))
|
218
245
|
end
|
219
246
|
end
|
220
247
|
|
@@ -249,6 +276,7 @@ class TreeGraph < Graph
|
|
249
276
|
fromLeft1 = (fromCenter + textW / 2).ceil
|
250
277
|
fromLeft2 = (fromCenter - textW / 2).ceil
|
251
278
|
toBot = (row2px(fromY - 1) + @e_height)
|
279
|
+
|
252
280
|
if symmetrize
|
253
281
|
toLeft = (toX + textW / 2 + @m[:b_side])
|
254
282
|
else
|
@@ -283,7 +311,7 @@ class TreeGraph < Graph
|
|
283
311
|
end
|
284
312
|
end
|
285
313
|
|
286
|
-
def img_get_txt_width(text, font, font_size, multiline =
|
314
|
+
def img_get_txt_width(text, font, font_size, multiline = true)
|
287
315
|
parts = text.split("_", 2)
|
288
316
|
main_before = parts[0].strip
|
289
317
|
sub = parts[1]
|
data/lib/rsyntaxtree/version.rb
CHANGED
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 =
|
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 + "/
|
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 + "/
|
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 + "/
|
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
|
102
|
-
:color
|
103
|
-
:autosub
|
104
|
-
:
|
105
|
-
:
|
106
|
-
:
|
107
|
-
:
|
108
|
-
:
|
109
|
-
:
|
110
|
-
:
|
111
|
-
:
|
112
|
-
:
|
113
|
-
:
|
114
|
-
:
|
115
|
-
:
|
116
|
-
:
|
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
|
-
:e_padd =>
|
121
|
-
:v_space =>
|
122
|
-
:h_space =>
|
123
|
-
:b_side =>
|
124
|
-
:b_topbot =>
|
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.
|
4
|
+
version: 0.9.2
|
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-
|
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/
|
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/
|
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
|
Binary file
|
Binary file
|