rsyntaxtree 0.7.2 → 0.7.3

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
  SHA1:
3
- metadata.gz: 4579e09d625fbfed3bf8415818f1d3e1faae5f1c
4
- data.tar.gz: b8541960fc98bc992340960990bf53a36a73730d
3
+ metadata.gz: '0586aec9488760401c729ef70db38c730883341e'
4
+ data.tar.gz: f9f95b8713cbcef850c3be737e5cd2cd2c3babe5
5
5
  SHA512:
6
- metadata.gz: fa4628dc3690b7532d80ca4d093d12fef301a8054ab034d9ea8b45302e802c65ac96d3cc2ce12edba514d439d75f6851e8d4297c50e042c9a2b1e2a56f7c9735
7
- data.tar.gz: a11e485de6a1cdc3f11be9d75e0177b6b7f46ec6fbc1b6a23ba827227e593610a99206c26271f6dd4b2cc194316cb41bbefdab7eb872c7b44c4f97bab96bfdc0
6
+ metadata.gz: cbbe7a1e063d5098f40b0f67b1fe832b92ff58b3bb7d9b3823ccafae6979dd1930c7b7ac5f787b0cdf17eeda267c15faf5c2305b843576cff15b83f062bd6be3
7
+ data.tar.gz: dfa583dbcf971ccaab04ce2db7f3dd633e0110e575063ab82cf80b740e73348397d41afa6922595d2a12c0a709b717f48ac7949f626d0235a907eb14223f1e76
data/README.md CHANGED
@@ -15,7 +15,7 @@ Working web interface of is available at <https://yohasebe.com/rsyntaxtree>.
15
15
  For the web interface, see Usage section of <https://yohasebe.com/rsyntaxtree>.
16
16
 
17
17
  For the command-line interface, type `$rsyntaxtree -h` after installation. Here's what you get:
18
-
18
+
19
19
  ```text
20
20
  RSyntaxTree, (linguistic) syntax tree generator written in Ruby.
21
21
 
@@ -25,14 +25,14 @@ where [options] are:
25
25
  -o, --outdir=<s> Output directory (default: ./)
26
26
  -f, --format=<s> Output format: png, pdf, or svg (default: png)
27
27
  -l, --leafstyle=<s> visual style of tree leaves: auto, triangle, bar, or nothing (default: auto)
28
- -n, --fontstyle=<s> Font style (available when ttf font is specified): sans, serif, mono, cjk (default: sans)
28
+ -n, --fontstyle=<s> Font style: sans, serif, cjk (default: sans)
29
29
  -t, --font=<s> Path to a ttf font used to generate tree (optional)
30
30
  -s, --fontsize=<i> Size: 8-26 (default: 16)
31
31
  -c, --color=<s> Color text and bars: on or off (default: on)
32
32
  -y, --symmetrize=<s> Generate symmetrical, balanced tree: on or off (default: on)
33
33
  -a, --autosub=<s> Put subscript numbers to nodes: on or off (default: off)
34
34
  -m, --margin=<i> Margin: 0-120 (default: 0)
35
- -v, --vheight=<f> Connector Height: 0.5-2.0 (default: 0.5)
35
+ -v, --vheight=<f> Connector Height: 0.5-2.0 (default: 1.0)
36
36
  -e, --version Print version and exit
37
37
  -h, --help Show this message
38
38
  ```
@@ -22,7 +22,7 @@ EOS
22
22
  :default => "png"
23
23
  opt :leafstyle, "visual style of tree leaves: auto, triangle, bar, or nothing",
24
24
  :default => "auto"
25
- opt :fontstyle, "Font style (available when ttf font is specified): sans, serif, mono, cjk",
25
+ opt :fontstyle, "Font style (available when ttf font is specified): sans, serif, cjk",
26
26
  :default => "sans"
27
27
  opt :font, "Path to a ttf font used to generate tree (optional)", :type => String
28
28
  opt :fontsize, "Size: 8-26",
@@ -42,7 +42,7 @@ end
42
42
  Trollop::die :outdir, "must be an exsting directory path" unless FileTest::directory?(opts[:outdir])
43
43
  Trollop::die :format, "must be png, jpg, gif, or svg" unless /\A(png|jpg|gif|pdf|svg)\z/ =~ opts[:format]
44
44
  Trollop::die :leafstyle, "must be auto, triangle, bar, or nothing" unless /\A(auto|triangle|bar|nothing)\z/ =~ opts[:leafstyle]
45
- Trollop::die :fontstyle, "must be sans, serif, mono, cjk" unless /\A(sans|serif|mono|cjk)\z/ =~ opts[:fontstyle]
45
+ Trollop::die :fontstyle, "must be sans, serif, cjk" unless /\A(sans|serif|cjk)\z/ =~ opts[:fontstyle]
46
46
  Trollop::die :font, "must be path to an existing ttf font" if opts[:font] && !File::exists?(opts[:font])
47
47
  Trollop::die :fontsize, "must be in the range of 8-26" unless opts[:fontsize] >= 8 && opts[:fontsize] <= 26
48
48
  Trollop::die :color, "must be either on or off" unless /\A(on|off)\z/ =~ opts[:color]
Binary file
Binary file
Binary file
@@ -12,25 +12,11 @@
12
12
  # excellent program phpSyntaxTree.
13
13
  # Copyright (c) 2007-2018 Yoichiro Hasebe <yohasebe@gmail.com>
14
14
  # Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
15
- #
16
- # This program is free software; you can redistribute it and/or modify
17
- # it under the terms of the GNU General Public License as published by
18
- # the Free Software Foundation; either version 2 of the License, or
19
- # (at your option) any later version.
20
- #
21
- # This program is distributed in the hope that it will be useful,
22
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
23
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
- # GNU General Public License for more details.
25
- #
26
- # You should have received a copy of the GNU General Public License
27
- # along with this program; if not, write to the Free Software
28
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29
15
 
30
16
  $LOAD_PATH << File.join( File.dirname(__FILE__), 'rsyntaxtree')
31
17
 
32
18
  require 'uri'
33
- require 'imgutils'
19
+ require 'utils'
34
20
  require 'element'
35
21
  require 'elementlist'
36
22
  require 'string_parser'
@@ -38,13 +24,13 @@ require 'tree_graph'
38
24
  require 'svg_graph'
39
25
  require 'error_message'
40
26
  require 'version'
41
- require 'pp'
42
27
 
43
28
  FONT_DIR = File.expand_path(File.dirname(__FILE__) + "/../fonts")
44
29
 
45
30
  ETYPE_UNDEFINED = 0
46
31
  ETYPE_NODE = 1
47
32
  ETYPE_LEAF = 2
33
+ SUBSCRIPT_CONST = 0.7
48
34
 
49
35
  class RSGenerator
50
36
  def initialize(params = {})
@@ -61,6 +47,7 @@ class RSGenerator
61
47
  .gsub('-OABRACKET-', '<')
62
48
  .gsub('-CABRACKET-', '>')
63
49
  new_params[key] = data
50
+ new_params[:multibyte] = data.contains_cjk?
64
51
  when :symmetrize, :color, :autosub
65
52
  new_params[key] = value && value != "off" ? true : false
66
53
  when :fontsize
@@ -71,23 +58,32 @@ class RSGenerator
71
58
  new_params[key] = value.to_f
72
59
  when :fontstyle
73
60
  if value == "noto-sans" || value == "sans"
74
- new_params[:font] = FONT_DIR + "/NotoSansCJKjp-Regular.otf"
61
+ new_params[:font] = FONT_DIR + "/NotoSans-Regular.ttf"
62
+ new_params[:font_it] = FONT_DIR + "/NotoSans-Italic.ttf"
63
+ new_params[:font_bd] = FONT_DIR + "/NotoSans-Bold.ttf"
64
+ new_params[:font_bdit] = FONT_DIR + "/NotoSans-BoldItalic.ttf"
65
+ new_params[:font_cjk] = FONT_DIR + "/NotoSansCJKjp-Regular.otf"
66
+ new_params[:fontstyle] = "sans"
75
67
  elsif value == "noto-serif" || value == "serif"
76
- new_params[:font] = FONT_DIR + "/NotoSerifCJKjp-Regular.otf"
77
- elsif value == "noto-mono" || value == "mono"
78
- new_params[:font] = FONT_DIR + "/NotoSansMonoCJKjp-Regular.otf"
79
- elsif value == "western-sans"
80
- new_params[:font] = FONT_DIR + "/DroidSans.ttf"
81
- elsif value == "western-serif"
82
- new_params[:font] = FONT_DIR + "/DroidSerif-Regular.ttf"
68
+ new_params[:font] = FONT_DIR + "/NotoSerif-Regular.ttf"
69
+ new_params[:font_it] = FONT_DIR + "/NotoSerif-Italic.ttf"
70
+ new_params[:font_bd] = FONT_DIR + "/NotoSerif-Bold.ttf"
71
+ new_params[:font_bdit] = FONT_DIR + "/NotoSerif-BoldItalic.ttf"
72
+ new_params[:font_cjk] = FONT_DIR + "/NotoSerifCJKjp-Regular.otf"
73
+ new_params[:fontstyle] = "serif"
83
74
  elsif value == "cjk zenhei" || value == "cjk"
84
75
  new_params[:font] = FONT_DIR + "/wqy-zenhei.ttf"
76
+ new_params[:font_it] = FONT_DIR + "/NotoSans-Italic.ttf"
77
+ new_params[:font_bd] = FONT_DIR + "/NotoSans-Bold.ttf"
78
+ new_params[:font_bdit] = FONT_DIR + "/NotoSans-BoldItalic.ttf"
79
+ new_params[:font_cjk] = FONT_DIR + "/wqy-zenhei.ttf"
80
+ new_params[:fontstyle] = "sans"
85
81
  end
86
82
  else
87
83
  new_params[key] = value
88
84
  end
89
85
  end
90
-
86
+
91
87
  @params = {
92
88
  :symmetrize => true,
93
89
  :color => true,
@@ -95,11 +91,15 @@ class RSGenerator
95
91
  :fontsize => 18,
96
92
  :format => "png",
97
93
  :leafstyle => "auto",
98
- :font => FONT_DIR + "/NotoSansCKjp-Regular.otf",
99
94
  :filename => "syntree",
100
95
  :data => "",
101
96
  :margin => 0,
102
- :vheight => 1.0
97
+ :vheight => 1.0,
98
+ :fontstyle => "sans",
99
+ :font => "/NotoSansCJKjp-Regular.otf",
100
+ :font_it => "/NotoSans-Italic.ttf",
101
+ :font_bd => "/NotoSans-Bold.ttf",
102
+ :font_bdit => "/NotoSans-BoldItalic.ttf",
103
103
  }
104
104
  @metrics = {
105
105
  :e_width => 120,
@@ -109,7 +109,7 @@ class RSGenerator
109
109
  :b_side => 10,
110
110
  :b_topbot => 10
111
111
  }
112
-
112
+
113
113
  @params.merge! new_params
114
114
  @params[:fontsize] = @params[:fontsize] * 2
115
115
  @params[:margin] = @params[:margin] * 2
@@ -120,7 +120,7 @@ class RSGenerator
120
120
  sp = StringParser.new(text)
121
121
  sp.valid?
122
122
  end
123
-
123
+
124
124
  def draw_png
125
125
  @params[:format] = "png"
126
126
  draw_tree
@@ -140,17 +140,7 @@ class RSGenerator
140
140
  @params[:format] = "pdf"
141
141
  draw_tree
142
142
  end
143
-
144
- def draw_tree
145
- sp = StringParser.new(@params[:data])
146
- sp.parse
147
- sp.auto_subscript if @params[:autosub]
148
- elist = sp.get_elementlist
149
- graph = TreeGraph.new(elist, @metrics,
150
- @params[:symmetrize], @params[:color], @params[:leafstyle], @params[:font], @params[:fontsize], @params[:format], @params[:margin])
151
- graph.to_blob(@params[:format])
152
- end
153
-
143
+
154
144
  def draw_svg
155
145
  @params[:format] = "svg"
156
146
  sp = StringParser.new(@params[:data].gsub('&', '&amp;').gsub('%', '&#37;'))
@@ -158,8 +148,23 @@ class RSGenerator
158
148
  sp.auto_subscript if @params[:autosub]
159
149
  elist = sp.get_elementlist
160
150
  graph = SVGGraph.new(elist, @metrics,
161
- @params[:symmetrize], @params[:color], @params[:leafstyle], @params[:font], @params[:fontsize])
151
+ @params[:symmetrize], @params[:color], @params[:leafstyle], @params[:multibyte],
152
+ @params[:fontstyle], @params[:fontsize],
153
+ )
162
154
  graph.svg_data
163
155
  end
156
+
157
+ def draw_tree
158
+ sp = StringParser.new(@params[:data])
159
+ sp.parse
160
+ sp.auto_subscript if @params[:autosub]
161
+ elist = sp.get_elementlist
162
+ graph = TreeGraph.new(elist, @metrics,
163
+ @params[:symmetrize], @params[:color], @params[:leafstyle], @params[:multibyte],
164
+ @params[:fontstyle], @params[:font], @params[:font_it], @params[:font_bd], @params[:font_bdit],
165
+ @params[:font_cjk], @params[:fontsize], @params[:margin],
166
+ )
167
+ graph.to_blob(@params[:format])
168
+ end
164
169
  end
165
170
 
@@ -12,20 +12,6 @@
12
12
  #
13
13
  # Copyright (c) 2007-2018 Yoichiro Hasebe <yohasebe@gmail.com>
14
14
  # Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
15
- #
16
- # This program is free software; you can redistribute it and/or modify
17
- # it under the terms of the GNU General Public License as published by
18
- # the Free Software Foundation; either version 2 of the License, or
19
- # (at your option) any later version.
20
- #
21
- # This program is distributed in the hope that it will be useful,
22
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
23
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
- # GNU General Public License for more details.
25
- #
26
- # You should have received a copy of the GNU General Public License
27
- # along with this program; if not, write to the Free Software
28
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29
15
 
30
16
  class Element
31
17
 
@@ -46,7 +32,7 @@ class Element
46
32
  @triangle = false # draw triangle instead of stright bar when in auto mode
47
33
  end
48
34
  end
49
-
35
+
50
36
  # Debug helper function
51
37
  def dump
52
38
  printf( "ID : %d\n", @id );
@@ -12,20 +12,6 @@
12
12
  #
13
13
  # Copyright (c) 2007-2018 Yoichiro Hasebe <yohasebe@gmail.com>
14
14
  # Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
15
- #
16
- # This program is free software; you can redistribute it and/or modify
17
- # it under the terms of the GNU General Public License as published by
18
- # the Free Software Foundation; either version 2 of the License, or
19
- # (at your option) any later version.
20
- #
21
- # This program is distributed in the hope that it will be useful,
22
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
23
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
- # GNU General Public License for more details.
25
- #
26
- # You should have received a copy of the GNU General Public License
27
- # along with this program; if not, write to the Free Software
28
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29
15
 
30
16
  require 'element'
31
17
 
@@ -36,7 +22,7 @@ class ElementList
36
22
  @elements = Array.new # The element array
37
23
  @iterator = -1 # Iterator index (used for get_first / get_next)
38
24
  end
39
-
25
+
40
26
  def add(element)
41
27
  @elements << element
42
28
  if(element.parent != 0)
@@ -132,6 +118,4 @@ class ElementList
132
118
  end
133
119
  return maxlevel + 1;
134
120
  end
135
-
136
121
  end
137
-
@@ -12,22 +12,6 @@
12
12
  #
13
13
  # Copyright (c) 2007-2018 Yoichiro Hasebe <yohasebe@gmail.com>
14
14
  # Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
15
- #
16
- # This program is free software; you can redistribute it and/or modify
17
- # it under the terms of the GNU General Public License as published by
18
- # the Free Software Foundation; either version 2 of the License, or
19
- # (at your option) any later version.
20
- #
21
- # This program is distributed in the hope that it will be useful,
22
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
23
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
- # GNU General Public License for more details.
25
- #
26
- # You should have received a copy of the GNU General Public License
27
- # along with this program; if not, write to the Free Software
28
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29
-
30
- require 'imgutils'
31
15
 
32
16
  class ErrorMessage
33
17
 
@@ -38,7 +22,7 @@ class ErrorMessage
38
22
  @font_size = font_size
39
23
  @filename = filename
40
24
  @format = format
41
-
25
+
42
26
  metrics = img_get_txt_metrics(text, font, font_size, true)
43
27
 
44
28
  @im = Image.new(metrics.width, metrics.height)
@@ -51,6 +35,27 @@ class ErrorMessage
51
35
  @gc.text(0, 0, text)
52
36
  end
53
37
 
38
+ def img_get_txt_metrics(text, font, font_size, multiline)
39
+
40
+ background = Image.new(500, 250)
41
+
42
+ gc = Draw.new
43
+ gc.annotate(background, 0, 0, 0, 0, text) do |gc|
44
+ gc.font = font
45
+ gc.pointsize = font_size
46
+ gc.gravity = CenterGravity
47
+ gc.stroke = 'none'
48
+ end
49
+
50
+ if multiline
51
+ metrics = gc.get_multiline_type_metrics(background, text)
52
+ else
53
+ metrics = gc.get_type_metrics(background, text)
54
+ end
55
+
56
+ return metrics
57
+ end
58
+
54
59
  def draw
55
60
  @gc.draw(@im)
56
61
  end
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ #==========================
5
+ # graph.rb
6
+ #==========================
7
+ #
8
+ # Image utility functions to inspect text font metrics
9
+ #
10
+ # This file is part of RSyntaxTree, which is a ruby port of Andre Eisenbach's
11
+ # excellent program phpSyntaxTree.
12
+ #
13
+ # Copyright (c) 2007-2018 Yoichiro Hasebe <yohasebe@gmail.com>
14
+ # Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
15
+
16
+ require 'rmagick'
17
+ include Magick
18
+
19
+ class Graph
20
+
21
+ def initialize(e_list, metrics, symmetrize, color, leafstyle, multibyte, font, font_size)
22
+
23
+ # Set class-specific parameters beforehand in subclass
24
+
25
+ # Store parameters
26
+ @e_list = e_list
27
+ @m = metrics
28
+ @multibyte = multibyte
29
+ @leafstyle = leafstyle
30
+ @symmetrize = symmetrize
31
+
32
+ # Calculate image dimensions
33
+ @e_height = font_size + @m[:e_padd] * 2
34
+ h = @e_list.get_level_height
35
+ w = calc_level_width(0)
36
+ @width = w + @m[:b_side] * 2
37
+ @height = h * @e_height + (h-1) * (@m[:v_space] + font_size) + @m[:b_topbot] * 2
38
+
39
+ # Initialize the image and colors
40
+ @col_bg = "none"
41
+ @col_fg = "black"
42
+ @col_line = "black"
43
+
44
+ if color
45
+ @col_node = "blue"
46
+ @col_leaf = "green"
47
+ @col_trace = "red"
48
+ else
49
+ @col_node = "black"
50
+ @col_leaf = "black"
51
+ @col_trace = "black"
52
+ end
53
+
54
+ @main_height = img_get_txt_height("l", font, font_size)
55
+ @sub_size = (font_size * SUBSCRIPT_CONST)
56
+ @sub_space_width = img_get_txt_width("l", font, @sub_size)
57
+ end
58
+
59
+ def img_get_txt_metrics(text, font, font_size, multiline)
60
+
61
+ background = Image.new(500, 250)
62
+
63
+ gc = Draw.new
64
+ gc.annotate(background, 0, 0, 0, 0, text) do |gc|
65
+ gc.font = font
66
+ gc.pointsize = font_size
67
+ gc.gravity = CenterGravity
68
+ gc.stroke = 'none'
69
+ end
70
+
71
+ if multiline
72
+ metrics = gc.get_multiline_type_metrics(background, text)
73
+ else
74
+ metrics = gc.get_type_metrics(background, text)
75
+ end
76
+
77
+ return metrics
78
+ end
79
+
80
+ # Calculate the width of the element. If the element is
81
+ # a node, the calculation will be performed recursively
82
+ # for all child elements.
83
+ def calc_element_width(e)
84
+ w = 0
85
+
86
+ children = @e_list.get_children(e.id)
87
+
88
+ if(children.length == 0)
89
+ w = img_get_txt_width(e.content, @font, @font_size) + @font_size
90
+ else
91
+ children.each do |child|
92
+ child_e = @e_list.get_id(child)
93
+ w += calc_element_width(child_e)
94
+ end
95
+
96
+ tw = img_get_txt_width(e.content, @font, @font_size) + @font_size
97
+ if(tw > w)
98
+ fix_child_size(e.id, w, tw)
99
+ w = tw
100
+ end
101
+ end
102
+
103
+ @e_list.set_element_width(e.id, w)
104
+ return w
105
+ end
106
+
107
+ # Calculate the width of all elements in a certain level
108
+ def calc_level_width(level)
109
+ w = 0
110
+ e = @e_list.get_first
111
+ while e
112
+ if(e.level == level)
113
+ w += calc_element_width(e)
114
+ end
115
+ e = @e_list.get_next
116
+ end
117
+ return w
118
+ end
119
+
120
+ def calc_children_width(id)
121
+ left = 0
122
+ right = 0
123
+ c_list = @e_list.get_children(id)
124
+ return nil if c_list.empty?
125
+
126
+ c_list.each do |c|
127
+ left = c.indent if indent == 0 or left > c.indent
128
+ end
129
+ c_list.each do |c|
130
+ right = c.indent + e.width if c.indent + c.width > right
131
+ end
132
+ return [left, right]
133
+ end
134
+
135
+ def get_children_indent(id)
136
+ calc_children_width(id)[0]
137
+ end
138
+
139
+ def get_children_width(id)
140
+ calc_children_width(id)[1] - get_children_indent(id)
141
+ end
142
+
143
+ # Parse the elements in the list top to bottom and
144
+ # draw the elements into the image.
145
+ # As we it iterate through the levels, the element
146
+ # indentation is calculated.
147
+ def parse_list
148
+
149
+ # Calc element list recursively....
150
+ e_arr = @e_list.get_elements
151
+
152
+ h = @e_list.get_level_height
153
+ h.times do |i|
154
+ x = 0
155
+ e_arr.each do |j|
156
+
157
+ if (j.level == i)
158
+ cw = @e_list.get_element_width(j.id)
159
+ parent_indent = @e_list.get_indent(j.parent)
160
+ if (x < parent_indent)
161
+ x = parent_indent
162
+ end
163
+ @e_list.set_indent(j.id, x)
164
+
165
+ if !@symmetrize
166
+ draw_element(x, i, cw, j.content, j.type)
167
+ if(j.parent != 0 )
168
+ words = j.content.split(" ")
169
+ unless @leafstyle == "nothing" && ETYPE_LEAF == j.type
170
+ if (@leafstyle == "triangle" && ETYPE_LEAF == j.type && x == parent_indent && words.length > 0)
171
+ txt_width = img_get_txt_width(j.content, @font, @font_size)
172
+ triangle_to_parent(x, i, cw, txt_width, @symmetrize)
173
+ elsif (@leafstyle == "auto" && ETYPE_LEAF == j.type && x == parent_indent)
174
+ if words.length > 1 || j.triangle
175
+ txt_width = img_get_txt_width(j.content, @font, @font_size)
176
+ triangle_to_parent(x, i, cw, txt_width, @symmetrize)
177
+ else
178
+ line_to_parent(x, i, cw, @e_list.get_indent(j.parent), @e_list.get_element_width(j.parent))
179
+ end
180
+ else
181
+ line_to_parent(x, i, cw, @e_list.get_indent(j.parent), @e_list.get_element_width(j.parent))
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ x += cw
188
+ end
189
+ end
190
+ end
191
+ return true if !@symmetrize
192
+ h.times do |i|
193
+ curlevel = h - i - 1
194
+ indent = 0
195
+ e_arr.each_with_index do |j, idx|
196
+ if (j.level == curlevel)
197
+ # Draw a line to the parent element
198
+ children = @e_list.get_children(j.id)
199
+
200
+ tw = img_get_txt_width(j.content, @font, @font_size)
201
+ if children.length > 1
202
+ left, right = -1, -1
203
+ children.each do |child|
204
+ k = @e_list.get_id(child)
205
+ kw = img_get_txt_width(k.content, @font, @font_size)
206
+ left = k.indent + kw / 2 if k.indent + kw / 2 < left or left == -1
207
+ right = k.indent + kw / 2 if k.indent + kw / 2 > right
208
+ end
209
+ draw_element(left, curlevel, right - left, j.content, j.type)
210
+ @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
211
+
212
+ children.each do |child|
213
+ k = @e_list.get_id(child)
214
+ words = k.content.split(" ")
215
+ dw = img_get_txt_width(k.content, @font, @font_size)
216
+ unless @leafstyle == "nothing" && ETYPE_LEAF == k.type
217
+ if (@leafstyle == "triangle" && ETYPE_LEAF == k.type && k.indent == j.indent && words.length > 0)
218
+ txt_width = img_get_txt_width(k.content, @font, @font_size)
219
+ triangle_to_parent(k.indent, curlevel + 1, dw, txt_width)
220
+ elsif (@leafstyle == "auto" && ETYPE_LEAF == k.type && k.indent == j.indent)
221
+ if words.length > 1 || k.triangle
222
+ txt_width = img_get_txt_width(k.content, @font, @font_size)
223
+ triangle_to_parent(k.indent, curlevel + 1, dw, txt_width)
224
+ else
225
+ line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
226
+ end
227
+ else
228
+ line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
229
+ end
230
+ end
231
+ end
232
+
233
+ else
234
+ unless children.empty?
235
+ k = @e_list.get_id(children[0])
236
+ kw = img_get_txt_width(k.content, @font, @font_size)
237
+ left = k.indent
238
+ right = k.indent + kw
239
+ draw_element(left, curlevel, right - left, j.content, j.type)
240
+ @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
241
+ else
242
+ parent = @e_list.get_id(j.parent)
243
+ pw = img_get_txt_width(parent.content, @font, @font_size)
244
+ pleft = parent.indent
245
+ pright = pleft + pw
246
+ left = j.indent
247
+ right = left + tw
248
+ if pw > tw
249
+ left = pleft
250
+ right = pright
251
+ end
252
+ draw_element(left, curlevel, right - left, j.content, j.type)
253
+ @e_list.set_indent(j.id, left + (right - left) / 2 - tw / 2)
254
+ end
255
+
256
+ unless children.empty?
257
+ k = @e_list.get_id(children[0])
258
+ words = k.content.split(" ")
259
+ dw = img_get_txt_width(k.content, @font, @font_size)
260
+ unless @leafstyle == "nothing" && ETYPE_LEAF == k.type
261
+ if (@leafstyle == "triangle" && ETYPE_LEAF == k.type && words.length > 0)
262
+ txt_width = img_get_txt_width(k.content, @font, @font_size)
263
+ triangle_to_parent(k.indent, curlevel + 1, dw, txt_width)
264
+ elsif (@leafstyle == "auto" && ETYPE_LEAF == k.type)
265
+ if words.length > 1 || k.triangle
266
+ txt_width = img_get_txt_width(k.content, @font, @font_size)
267
+ triangle_to_parent(k.indent, curlevel + 1, dw, txt_width)
268
+ else
269
+ line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
270
+ end
271
+ else
272
+ line_to_parent(k.indent, curlevel + 1, dw, j.indent, tw)
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
278
+ end
279
+ end
280
+ end
281
+
282
+ # Calculate top position from row (level)
283
+ def row2px(row)
284
+ @m[:b_topbot] + @e_height * row + (@m[:v_space] + @font_size) * row
285
+ end
286
+
287
+ def get_txt_only(text)
288
+ text = text.strip
289
+ if /\A([\+\-\=\*]+).+/ =~ text
290
+ prefix = $1
291
+ prefix_l = Regexp.escape(prefix)
292
+ prefix_r = Regexp.escape(prefix.reverse)
293
+ if /\A#{prefix_l}(.+)#{prefix_r}\z/ =~ text
294
+ return $1
295
+ end
296
+ end
297
+ return text
298
+ end
299
+
300
+ def img_get_txt_height(text, font, font_size, multiline = false)
301
+ metrics = img_get_txt_metrics(text, font, font_size, multiline)
302
+ y = metrics.height
303
+ return y
304
+ end
305
+ end