rsyntaxtree 1.0.8 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,63 +1,59 @@
1
- #!/usr/bin/env ruby
2
- # -*- coding: utf-8 -*-
3
-
4
- #==========================
5
- # utils.rb
6
- #==========================
7
- #
8
- # Image utility functions to inspect text font metrics
9
- # Copyright (c) 2007-2021 Yoichiro Hasebe <yohasebe@gmail.com>
10
-
11
- require 'rmagick'
12
- include Magick
13
-
14
- class String
15
- def contains_cjk?
16
- !!(self.gsub(WHITESPACE_BLOCK, "") =~ /\p{Han}|\p{Katakana}|\p{Hiragana}|\p{Hangul}|[^\x01-\x7E]/)
17
- end
18
-
19
- def contains_emoji?
20
- !!(self.gsub(WHITESPACE_BLOCK, "").gsub(/\d/, "") =~ /\p{Emoji}/)
21
- end
22
-
23
- def all_emoji?
24
- !!(self.gsub(WHITESPACE_BLOCK, "").gsub(/\d/, "") =~ /\A\p{Emoji}[\p{Emoji}\s]*\z/)
25
- end
26
-
27
- def split_by_emoji
28
- results = []
29
- self.split(//).each do |ch|
30
- case ch
31
- when /\d/, WHITESPACE_BLOCK
32
- results << {:type => :normal, :char => ch}
33
- when /\p{Emoji}/
34
- results << {:type => :emoji, :char => ch}
35
- else
36
- results << {:type => :normal, :char => ch}
37
- end
38
- end
39
- results.reject{|string| string == ""}
40
- end
41
- end
42
-
43
- module FontMetrics
44
- def get_metrics(text, font, fontsize, font_style, font_weight)
45
- background = Image.new(1, 1)
46
- gc = Draw.new
47
- gc.annotate(background, 0, 0, 0, 0, text) do |gc|
48
- gc.font = font
49
- gc.font_style = font_style == :italic ? ItalicStyle : NormalStyle
50
- gc.font_weight = font_weight == :bold ? BoldWeight : NormalWeight
51
- gc.pointsize = fontsize
52
- gc.gravity = CenterGravity
53
- gc.stroke = 'none'
54
- gc.kerning = 0
55
- gc.interline_spacing = 0
56
- gc.interword_spacing = 0
57
- end
58
- metrics = gc.get_multiline_type_metrics(background, text)
59
- return metrics
60
- end
61
- module_function :get_metrics
62
- end
63
-
1
+ # frozen_string_literal: true
2
+
3
+ #==========================
4
+ # utils.rb
5
+ #==========================
6
+ #
7
+ # Image utility functions to inspect text font metrics
8
+ # Copyright (c) 2007-2023 Yoichiro Hasebe <yohasebe@gmail.com>
9
+
10
+ require 'rmagick'
11
+
12
+ class String
13
+ def contains_cjk?
14
+ !!(gsub(WHITESPACE_BLOCK, "") =~ /\p{Han}|\p{Katakana}|\p{Hiragana}|\p{Hangul}|[^\x01-\x7E]/)
15
+ end
16
+
17
+ def contains_emoji?
18
+ !!(gsub(WHITESPACE_BLOCK, "").gsub(/\d/, "") =~ /\p{Emoji}/)
19
+ end
20
+
21
+ def all_emoji?
22
+ !!(gsub(WHITESPACE_BLOCK, "").gsub(/\d/, "") =~ /\A\p{Emoji}[\p{Emoji}\s]*\z/)
23
+ end
24
+
25
+ def split_by_emoji
26
+ results = []
27
+ split(//).each do |ch|
28
+ results << case ch
29
+ when /\d/, WHITESPACE_BLOCK
30
+ { type: :normal, char: ch }
31
+ when /\p{Emoji}/
32
+ { type: :emoji, char: ch }
33
+ else
34
+ { type: :normal, char: ch }
35
+ end
36
+ end
37
+ results.reject { |string| string == "" }
38
+ end
39
+ end
40
+
41
+ module FontMetrics
42
+ def get_metrics(text, font, fontsize, font_style, font_weight)
43
+ background = Magick::Image.new(1, 1)
44
+ gc = Magick::Draw.new
45
+ gc.annotate(background, 0, 0, 0, 0, text) do |gca|
46
+ gca.font = font
47
+ gca.font_style = font_style == :italic ? Magick::ItalicStyle : Magick::NormalStyle
48
+ gca.font_weight = font_weight == :bold ? Magick::BoldWeight : Magick::NormalWeight
49
+ gca.pointsize = fontsize
50
+ gca.gravity = Magick::CenterGravity
51
+ gca.stroke = 'none'
52
+ gca.kerning = 0
53
+ gca.interline_spacing = 0
54
+ gca.interword_spacing = 0
55
+ end
56
+ gc.get_multiline_type_metrics(background, text)
57
+ end
58
+ module_function :get_metrics
59
+ end
@@ -1,4 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RSyntaxTree
2
- VERSION = "1.0.8"
4
+ VERSION = "1.1.0"
3
5
  end
4
-
data/lib/rsyntaxtree.rb CHANGED
@@ -1,177 +1,174 @@
1
- #!/usr/bin/env ruby
2
- # -*- coding: utf-8 -*-
3
-
4
- #==========================
5
- # rsyntaxtree.rb
6
- #==========================
7
- #
8
- # Facade of rsyntaxtree library. When loaded by a driver script, it does all
9
- # the necessary 'require' to use the library.
10
- # Copyright (c) 2007-2021 Yoichiro Hasebe <yohasebe@gmail.com>
11
-
12
- $LOAD_PATH << File.join( File.dirname(__FILE__), 'rsyntaxtree')
13
-
14
- FONT_DIR = File.expand_path(File.dirname(__FILE__) + "/../fonts")
15
- ETYPE_NODE = 1
16
- ETYPE_LEAF = 2
17
- SUBSCRIPT_CONST = 0.7
18
- FONT_SCALING = 2
19
- WHITESPACE_BLOCK = ""
20
-
21
- class RSTError < StandardError
22
- def initialize(msg="Error: something unexpected occurred")
23
- msg.gsub!(WHITESPACE_BLOCK, "<>")
24
- super msg
25
- end
26
- end
27
-
28
- require 'cgi'
29
- require 'rsvg2'
30
- require 'utils'
31
- require 'element'
32
- require 'elementlist'
33
- require 'string_parser'
34
- require 'svg_graph'
35
- require 'version'
36
- require 'rmagick'
37
-
38
- module RSyntaxTree
39
-
40
- class RSGenerator
41
-
42
- def initialize(params = {})
43
- new_params = {}
44
- fontset = {}
45
- params.each do |keystr, value|
46
- key = keystr.to_sym
47
- case key
48
- when :data
49
- data = value
50
- data = data.gsub('-AMP-', '&')
51
- .gsub('-PERCENT-', "%")
52
- .gsub('-PRIME-', "'")
53
- .gsub('-SCOLON-', ';')
54
- .gsub('-OABRACKET-', '<')
55
- .gsub('-CABRACKET-', '>')
56
- .gsub('¥¥', '\¥')
57
- .gsub(/(?<!\\)¥/, "\\")
58
- new_params[key] = data
59
-
60
- when :symmetrize, :color, :transparent, :polyline
61
- new_params[key] = value && (value != "off" && value != "false") ? true : false
62
- when :fontsize
63
- new_params[key] = value.to_i * FONT_SCALING
64
- when :margin
65
- new_params[key] = value.to_i * FONT_SCALING * 5
66
- when :vheight
67
- new_params[key] = value.to_f
68
- when :fontstyle
69
- if value == "noto-sans" || value == "sans"
70
- fontset[:normal] = FONT_DIR + "/NotoSans-Regular.ttf"
71
- fontset[:italic] = FONT_DIR + "/NotoSans-Italic.ttf"
72
- fontset[:bold] = FONT_DIR + "/NotoSans-Bold.ttf"
73
- fontset[:bolditalic] = FONT_DIR + "/NotoSans-BoldItalic.ttf"
74
- fontset[:math] = FONT_DIR + "/NotoSansMath-Regular.ttf"
75
- fontset[:cjk] = FONT_DIR + "/NotoSansJP-Regular.otf"
76
- fontset[:emoji] = FONT_DIR + "/OpenMoji-Black.ttf"
77
- new_params[:fontstyle] = "sans"
78
- elsif value == "noto-serif" || value == "serif"
79
- fontset[:normal] = FONT_DIR + "/NotoSerif-Regular.ttf"
80
- fontset[:italic] = FONT_DIR + "/NotoSerif-Italic.ttf"
81
- fontset[:bold] = FONT_DIR + "/NotoSerif-Bold.ttf"
82
- fontset[:bolditalic] = FONT_DIR + "/NotoSerif-BoldItalic.ttf"
83
- fontset[:math] = FONT_DIR + "/latinmodern-math.otf"
84
- fontset[:cjk] = FONT_DIR + "/NotoSerifJP-Regular.otf"
85
- fontset[:emoji] = FONT_DIR + "/OpenMoji-Black.ttf"
86
- new_params[:fontstyle] = "serif"
87
- elsif value == "cjk zenhei" || value == "cjk"
88
- fontset[:normal] = FONT_DIR + "/wqy-zenhei.ttf"
89
- fontset[:italic] = FONT_DIR + "/NotoSans-Italic.ttf"
90
- fontset[:bold] = FONT_DIR + "/NotoSans-Bold.ttf"
91
- fontset[:bolditalic] = FONT_DIR + "/NotoSans-BoldItalic.ttf"
92
- fontset[:math] = FONT_DIR + "/NotoSansMath-Regular.ttf"
93
- fontset[:cjk] = FONT_DIR + "/wqy-zenhei.ttf"
94
- fontset[:emoji] = FONT_DIR + "/OpenMoji-Black.ttf"
95
- new_params[:fontstyle] = "cjk"
96
- end
97
- else
98
- new_params[key] = value
99
- end
100
- end
101
-
102
- # defaults to the following
103
- @params = {
104
- :symmetrize => true,
105
- :color => true,
106
- :transparent => false,
107
- :fontsize => 16,
108
- :format => "png",
109
- :leafstyle => "auto",
110
- :filename => "syntree",
111
- :data => "",
112
- :margin => 0,
113
- :vheight => 1.0,
114
- :polyline => false,
115
- }
116
-
117
- @params.merge! new_params
118
- @params[:fontsize] = @params[:fontsize] * FONT_SCALING
119
- @params[:margin] = @params[:margin] * FONT_SCALING
120
- @params[:fontset] = fontset
121
-
122
- $single_X_metrics = FontMetrics.get_metrics("X", fontset[:normal], @params[:fontsize], :normal, :normal)
123
- $height_connector_to_text = $single_X_metrics.height / 2.0
124
- $single_line_height = $single_X_metrics.height * 2.0
125
- $width_half_X = $single_X_metrics.width / 2.0
126
- $height_connector = $single_X_metrics.height * 1.0 * @params[:vheight]
127
- $h_gap_between_nodes = $single_X_metrics.width * 0.8
128
- $box_vertical_margin = $single_X_metrics.height * 0.8
129
- end
130
-
131
- def self.check_data(text)
132
- if text.to_s == ""
133
- raise RSTError, "Error: input text is empty"
134
- end
135
- StringParser.valid?(text)
136
- end
137
-
138
- def draw_png(binary = false)
139
- svg = draw_svg
140
- rsvg = RSVG::Handle.new_from_data(svg)
141
- dim = rsvg.dimensions
142
- surface = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, dim.width, dim.height)
143
- context = Cairo::Context.new(surface)
144
- context.render_rsvg_handle(rsvg)
145
- b = StringIO.new
146
- surface.write_to_png(b)
147
- if binary
148
- return b
149
- else
150
- return b.string
151
- end
152
- end
153
-
154
- def draw_svg
155
- sp = StringParser.new(@params[:data].gsub('&', '&amp;'), @params[:fontset], @params[:fontsize])
156
- sp.parse
157
- graph = SVGGraph.new(sp.get_elementlist, @params)
158
- graph.svg_data
159
- end
160
-
161
- def draw_tree
162
- svg = draw_svg
163
- image, data = Magick::Image.from_blob(svg) do |im|
164
- im.format = 'svg'
165
- end
166
- if @params[:format] == "png"
167
- image = draw_png(false)
168
- else
169
- image.to_blob do |im|
170
- im.format = @params[:format].upcase
171
- end
172
- end
173
- image
174
- end
175
- end
176
- end
177
-
1
+ # frozen_string_literal: true
2
+
3
+ #==========================
4
+ # rsyntaxtree.rb
5
+ #==========================
6
+ #
7
+ # Facade of rsyntaxtree library. When loaded by a driver script, it does all
8
+ # the necessary 'require' to use the library.
9
+ # Copyright (c) 2007-2023 Yoichiro Hasebe <yohasebe@gmail.com>
10
+
11
+ FONT_DIR = File.expand_path(File.join(__dir__, "/../fonts"))
12
+ ETYPE_NODE = 1
13
+ ETYPE_LEAF = 2
14
+ SUBSCRIPT_CONST = 0.7
15
+ FONT_SCALING = 2
16
+ WHITESPACE_BLOCK = "■"
17
+
18
+ class RSTError < StandardError
19
+ def initialize(msg = "Error: something unexpected occurred")
20
+ msg.gsub!(WHITESPACE_BLOCK, "<>")
21
+ super msg
22
+ end
23
+ end
24
+
25
+ require_relative 'rsyntaxtree/utils'
26
+ require_relative 'rsyntaxtree/element'
27
+ require_relative 'rsyntaxtree/elementlist'
28
+ require_relative 'rsyntaxtree/svg_graph'
29
+ require_relative 'rsyntaxtree/version'
30
+ require_relative 'rsyntaxtree/string_parser'
31
+
32
+ require 'cgi'
33
+ require 'rsvg2'
34
+ require 'rmagick'
35
+
36
+ module RSyntaxTree
37
+ class RSGenerator
38
+ def initialize(params = {})
39
+ new_params = {}
40
+ fontset = {}
41
+ params.each do |keystr, value|
42
+ key = keystr.to_sym
43
+ case key
44
+ when :data
45
+ data = value
46
+ data = data.gsub('-AMP-', '&')
47
+ .gsub('-PERCENT-', "%")
48
+ .gsub('-PRIME-', "'")
49
+ .gsub('-SCOLON-', ';')
50
+ .gsub('-OABRACKET-', '<')
51
+ .gsub('-CABRACKET-', '>')
52
+ .gsub('¥¥', '\¥')
53
+ .gsub(/(?<!\\)¥/, "\\")
54
+ new_params[key] = data
55
+
56
+ when :symmetrize, :color, :transparent, :polyline
57
+ new_params[key] = value && (value != "off" && value != "false") ? true : false
58
+ when :fontsize
59
+ new_params[key] = value.to_i * FONT_SCALING
60
+ when :margin
61
+ new_params[key] = value.to_i * FONT_SCALING * 5
62
+ when :vheight
63
+ new_params[key] = value.to_f
64
+ when :fontstyle
65
+ case value
66
+ when "noto-sans", "sans"
67
+ fontset[:normal] = FONT_DIR + "/NotoSans-Regular.ttf"
68
+ fontset[:italic] = FONT_DIR + "/NotoSans-Italic.ttf"
69
+ fontset[:bold] = FONT_DIR + "/NotoSans-Bold.ttf"
70
+ fontset[:bolditalic] = FONT_DIR + "/NotoSans-BoldItalic.ttf"
71
+ fontset[:math] = FONT_DIR + "/NotoSansMath-Regular.ttf"
72
+ fontset[:cjk] = FONT_DIR + "/NotoSansJP-Regular.otf"
73
+ fontset[:emoji] = FONT_DIR + "/OpenMoji-Black.ttf"
74
+ new_params[:fontstyle] = "sans"
75
+ when "noto-serif", value == "serif"
76
+ fontset[:normal] = FONT_DIR + "/NotoSerif-Regular.ttf"
77
+ fontset[:italic] = FONT_DIR + "/NotoSerif-Italic.ttf"
78
+ fontset[:bold] = FONT_DIR + "/NotoSerif-Bold.ttf"
79
+ fontset[:bolditalic] = FONT_DIR + "/NotoSerif-BoldItalic.ttf"
80
+ fontset[:math] = FONT_DIR + "/latinmodern-math.otf"
81
+ fontset[:cjk] = FONT_DIR + "/NotoSerifJP-Regular.otf"
82
+ fontset[:emoji] = FONT_DIR + "/OpenMoji-Black.ttf"
83
+ new_params[:fontstyle] = "serif"
84
+ when "cjk zenhei", "cjk"
85
+ fontset[:normal] = FONT_DIR + "/wqy-zenhei.ttf"
86
+ fontset[:italic] = FONT_DIR + "/NotoSans-Italic.ttf"
87
+ fontset[:bold] = FONT_DIR + "/NotoSans-Bold.ttf"
88
+ fontset[:bolditalic] = FONT_DIR + "/NotoSans-BoldItalic.ttf"
89
+ fontset[:math] = FONT_DIR + "/NotoSansMath-Regular.ttf"
90
+ fontset[:cjk] = FONT_DIR + "/wqy-zenhei.ttf"
91
+ fontset[:emoji] = FONT_DIR + "/OpenMoji-Black.ttf"
92
+ new_params[:fontstyle] = "cjk"
93
+ end
94
+ else
95
+ new_params[key] = value
96
+ end
97
+ end
98
+
99
+ # defaults to the following
100
+ @params = {
101
+ symmetrize: true,
102
+ color: true,
103
+ transparent: false,
104
+ fontsize: 16,
105
+ format: "png",
106
+ leafstyle: "auto",
107
+ filename: "syntree",
108
+ data: "",
109
+ margin: 0,
110
+ vheight: 1.0,
111
+ polyline: false
112
+ }
113
+
114
+ @params.merge! new_params
115
+ @params[:fontsize] = @params[:fontsize] * FONT_SCALING
116
+ @params[:margin] = @params[:margin] * FONT_SCALING
117
+ @params[:fontset] = fontset
118
+
119
+ single_x_metrics = FontMetrics.get_metrics("X", fontset[:normal], @params[:fontsize], :normal, :normal)
120
+ @global = {}
121
+ @global[:single_x_metrics] = single_x_metrics
122
+ @global[:height_connector_to_text] = single_x_metrics.height / 2.0
123
+ @global[:single_line_height] = single_x_metrics.height * 2.0
124
+ @global[:width_half_x] = single_x_metrics.width / 2.0
125
+ @global[:height_connector] = single_x_metrics.height * 1.0 * @params[:vheight]
126
+ @global[:h_gap_between_nodes] = single_x_metrics.width * 0.8
127
+ @global[:box_vertical_margin] = single_x_metrics.height * 0.8
128
+ end
129
+
130
+ def self.check_data(text)
131
+ raise RSTError, "Error: input text is empty" if text.to_s == ""
132
+
133
+ StringParser.valid?(text)
134
+ end
135
+
136
+ def draw_png(binary = false)
137
+ svg = draw_svg
138
+ rsvg = RSVG::Handle.new_from_data(svg)
139
+ dim = rsvg.dimensions
140
+ surface = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, dim.width, dim.height)
141
+ context = Cairo::Context.new(surface)
142
+ context.render_rsvg_handle(rsvg)
143
+ b = StringIO.new
144
+ surface.write_to_png(b)
145
+ if binary
146
+ b
147
+ else
148
+ b.string
149
+ end
150
+ end
151
+
152
+ def draw_svg
153
+ sp = StringParser.new(@params[:data].gsub('&', '&amp;'), @params[:fontset], @params[:fontsize], @global)
154
+ sp.parse
155
+ graph = SVGGraph.new(sp.get_elementlist, @params, @global)
156
+ graph.svg_data
157
+ end
158
+
159
+ def draw_tree
160
+ svg = draw_svg
161
+ image, _data = Magick::Image.from_blob(svg) do |im|
162
+ im.format = 'svg'
163
+ end
164
+ if @params[:format] == "png"
165
+ image = draw_png(false)
166
+ else
167
+ image.to_blob do |im|
168
+ im.format = @params[:format].upcase
169
+ end
170
+ end
171
+ image
172
+ end
173
+ end
174
+ end
data/rsyntaxtree.gemspec CHANGED
@@ -1,7 +1,6 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
3
2
 
4
- require "rsyntaxtree/version"
3
+ require_relative "lib/rsyntaxtree/version"
5
4
 
6
5
  Gem::Specification.new do |s|
7
6
  s.name = "rsyntaxtree"
@@ -9,16 +8,17 @@ Gem::Specification.new do |s|
9
8
  s.authors = ["Yoichiro Hasebe"]
10
9
  s.email = ["yohasebe@gmail.com"]
11
10
  s.homepage = "http://github.com/yohasebe/rsyntaxtree"
12
- s.summary = %q{RSyntaxTree is a graphical syntax tree generator written in Ruby}
13
- s.description = %q{Syntax tree generator made with Ruby}
11
+ s.summary = "RSyntaxTree is a graphical syntax tree generator written in Ruby"
12
+ s.description = "Syntax tree generator made with Ruby"
14
13
  s.licenses = ["MIT"]
14
+ s.required_ruby_version = Gem::Requirement.new(">= 2.6")
15
15
  s.files = `git ls-files`.split("\n")
16
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
17
17
  s.require_paths = ["lib"]
18
18
 
19
19
  # specify any dependencies here; for example:
20
- s.add_runtime_dependency 'rmagick', '~> 4.2', '>= 4.2.3'
21
- s.add_runtime_dependency 'optimist', '~> 3.0', '>= 3.0.1'
22
- s.add_runtime_dependency 'parslet'
23
- s.add_runtime_dependency 'rsvg2'
20
+ s.add_runtime_dependency "optimist", "~> 3.0", ">= 3.0.1"
21
+ s.add_runtime_dependency "parslet"
22
+ s.add_runtime_dependency "rmagick", "~> 4.2", ">= 4.2.3"
23
+ s.add_runtime_dependency "rsvg2"
24
24
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "minitest/autorun"
2
4
  require "minitest/pride"
3
- require "./lib/rsyntaxtree/markup_parser.rb"
5
+ require_relative "../lib/rsyntaxtree/markup_parser"
4
6
 
5
7
  class MarkupParserTest < Minitest::Test
6
8
  def setup
@@ -203,5 +205,4 @@ class MarkupParserTest < Minitest::Test
203
205
  Markup.parse text2
204
206
  # {:status=>:error, :text=>"!^#----\\n\\nX_Y_Z+1+>2"}
205
207
  end
206
-
207
208
  end