svg_drawer 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/lib/fonts.yml +43 -0
- data/lib/svg_drawer/base.rb +215 -0
- data/lib/svg_drawer/circle.rb +139 -0
- data/lib/svg_drawer/line.rb +10 -0
- data/lib/svg_drawer/multipolyline.rb +144 -0
- data/lib/svg_drawer/path.rb +43 -0
- data/lib/svg_drawer/polyline.rb +171 -0
- data/lib/svg_drawer/table/blank_row.rb +32 -0
- data/lib/svg_drawer/table/border.rb +62 -0
- data/lib/svg_drawer/table/cell.rb +65 -0
- data/lib/svg_drawer/table/row.rb +127 -0
- data/lib/svg_drawer/table/table.rb +147 -0
- data/lib/svg_drawer/text_box.rb +153 -0
- data/lib/svg_drawer/utils/parameter_merger.rb +37 -0
- data/lib/svg_drawer/utils/rasem_wrapper.rb +14 -0
- data/lib/svg_drawer/utils/text.rb +48 -0
- data/lib/svg_drawer/version.rb +3 -0
- data/lib/svg_drawer.rb +31 -0
- data/svg_drawer.gemspec +21 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 79996165279d088f2e412c952599a1f4425e6022
|
4
|
+
data.tar.gz: 72e4bce3f625e41bf91d581ab1b17f529f7f8619
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 64e21f2a34481ceb931564a69b13c910fcda76de467e9f1507e8d472e9e3e51c0b20a953e4d10b1c38bf8ea485fe9c10bb1de7daf1ca7c07fb6c9d576777d63c
|
7
|
+
data.tar.gz: 545bf250522002f54b193fdc1ce20776f1287c30bacf5d52c488e5627f06b1f82076d6c37d5bbfaa136c48c4dd4c259433b8c2d3b8db7908674c8409ccb68485
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Simeon Manolov
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/lib/fonts.yml
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#
|
2
|
+
# Heights and widths calculated experimentally with chrome inspector.
|
3
|
+
#
|
4
|
+
# y_offset is also calculated experimentally with chrome inspector:
|
5
|
+
# As opposed to HTML, in SVG when a text is drawed, all text is
|
6
|
+
# *transposed* down by (1.2 * [font_height]) px.
|
7
|
+
# This causes problems when drawing a text box with a border, as
|
8
|
+
# chars like 'g' or 'p' will intersect with the bottom border (and
|
9
|
+
# and '_' char will even be drawed entirely below the border)
|
10
|
+
# This is compensated with the y_offset, which seems to be ~0.22
|
11
|
+
# (how that value is used can be seen in #calculate_y_offset_px)
|
12
|
+
#
|
13
|
+
|
14
|
+
default:
|
15
|
+
width: 0.63
|
16
|
+
height: 1.2
|
17
|
+
y_offset: 0.23
|
18
|
+
wrap_policies:
|
19
|
+
weak: 1
|
20
|
+
normal: 1
|
21
|
+
aggressive: 1
|
22
|
+
max: 1
|
23
|
+
|
24
|
+
'NK57 Monospace':
|
25
|
+
width: 0.63
|
26
|
+
|
27
|
+
'Ubuntu Mono':
|
28
|
+
width: 0.5
|
29
|
+
|
30
|
+
'Roboto Mono':
|
31
|
+
width: 0.596
|
32
|
+
|
33
|
+
'Courier New':
|
34
|
+
width: 0.8
|
35
|
+
|
36
|
+
'Arial':
|
37
|
+
width: 0.63
|
38
|
+
height: 1.3
|
39
|
+
wrap_policies:
|
40
|
+
weak: 0.9 # english text with very occasional capitals
|
41
|
+
normal: 1 # randomly mixed small/capital chars
|
42
|
+
aggressive: 1.17 # capital chars
|
43
|
+
max: 1.85 # capital "W" (widest english char)
|
@@ -0,0 +1,215 @@
|
|
1
|
+
module SvgDrawer
|
2
|
+
class Base
|
3
|
+
class ElementIncomplete < StandardError
|
4
|
+
def initialize(element)
|
5
|
+
super("Element incomplete: #{element.inspect}")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_reader :required_params, :special_params, :default_params
|
11
|
+
|
12
|
+
#
|
13
|
+
# The required_params contain param keys vital to the element.
|
14
|
+
# For example, a table can't be initialized without a :columns param.
|
15
|
+
# Their values can be provided via inherited_params.
|
16
|
+
#
|
17
|
+
def requires(*names)
|
18
|
+
required_params.concat(names)
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# The special_params contain param keys special to the element.
|
23
|
+
# They will not be stored in @child_params
|
24
|
+
# See note in #initialize
|
25
|
+
#
|
26
|
+
def special(*names)
|
27
|
+
# Ensure special param are also a valid param (see ParameterMerger#param)
|
28
|
+
names_hash = names.map { |n| [n, nil] }.to_h
|
29
|
+
|
30
|
+
# this just Hash#reverse_merge! as implemented in activesupport-4.2
|
31
|
+
default_params.merge!(names_hash) { |_key, left, _right| left }
|
32
|
+
special_params.concat(names)
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# The default_values is a hash of param values that will be
|
37
|
+
# used as a last-resort fallback
|
38
|
+
#
|
39
|
+
# With and height are applicable to all elements, so they
|
40
|
+
# are always included here to avoid "No such param" errors
|
41
|
+
# (see ParamMerger#param for details)
|
42
|
+
#
|
43
|
+
def defaults(hash)
|
44
|
+
default_params.update(hash)
|
45
|
+
end
|
46
|
+
|
47
|
+
def required_params
|
48
|
+
@required_params ||= []
|
49
|
+
end
|
50
|
+
|
51
|
+
# These should never be passed down to children
|
52
|
+
def special_params
|
53
|
+
@special_params ||= %i[id class inherited border borders]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Mark these as valid for *all* elements (no default value though)
|
57
|
+
def default_params
|
58
|
+
@default_params ||= {
|
59
|
+
id: nil,
|
60
|
+
class: name.gsub(/.*::/, '').downcase,
|
61
|
+
width: nil,
|
62
|
+
height: nil,
|
63
|
+
border: nil,
|
64
|
+
borders: nil,
|
65
|
+
border_style: nil
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
attr_writer :width, :height
|
71
|
+
attr_reader :params, :inherited_params, :child_params
|
72
|
+
|
73
|
+
#
|
74
|
+
# All elements can are initialized with a params hash *only*.
|
75
|
+
# This hash contains stuff regular keys like :width, :height, etc.
|
76
|
+
# If this hash does *not* contain any of the keys found
|
77
|
+
# in self.class.required_params, an error is risen.
|
78
|
+
# It may contain an :inherited. Its value is a hash
|
79
|
+
# which will serve as a fall-back to any keys not found in the top-level
|
80
|
+
# hash.
|
81
|
+
# For example:
|
82
|
+
# { width: 100, inherited: { width: 200, height: 500 } }
|
83
|
+
#
|
84
|
+
# Calling #param(:width) on the element will return 100
|
85
|
+
# Calling #param(:height) on the element will return 500
|
86
|
+
# Calling #param(:foo) will not be found in any of the hashes, so
|
87
|
+
# it will be looked up in self.class.default_params:
|
88
|
+
# - if found, will be returned
|
89
|
+
# - if not found, an error will be risen (see ParameterMerger#param)
|
90
|
+
#
|
91
|
+
# A special @child_params hash is automatically constructed every
|
92
|
+
# time #update_params! is called (incl. #initialize)
|
93
|
+
# It is a hash that will be passed on as the value of
|
94
|
+
# the :inherited key to any child elements constructed.
|
95
|
+
# Note that it will not contain any keys found in self.class.special_params
|
96
|
+
#
|
97
|
+
# Inheritance example:
|
98
|
+
#
|
99
|
+
# Table.new(font: 'A') do |table|
|
100
|
+
# table.row do |row|
|
101
|
+
# row.cell do |cell|
|
102
|
+
# cell.content = TextBox.new('foo') # text 1
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# table.row(font: 'B') do |row|
|
106
|
+
# row.cell do |cell|
|
107
|
+
# cell.content = TextBox.new('foo') # text 2
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# row.cell(font: 'C') do |cell|
|
111
|
+
# cell.content = TextBox.new('foo') # text 3
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# row.cell(font: 'C') do |cell|
|
115
|
+
# cell.content(TextBox.new('foo', font: 'D')) # text 4
|
116
|
+
# end
|
117
|
+
# end
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# The texts 1, 2, 3 and 4 will have font 'A', 'B', 'C' and 'D'.
|
121
|
+
#
|
122
|
+
def initialize(params = {})
|
123
|
+
@params = {}
|
124
|
+
@inherited_params = {}
|
125
|
+
@child_params = {}
|
126
|
+
@pmerger = Utils::ParameterMerger.new(@params,
|
127
|
+
@inherited_params,
|
128
|
+
self.class.default_params)
|
129
|
+
|
130
|
+
update_params!(params)
|
131
|
+
end
|
132
|
+
|
133
|
+
# The way to update an element's params
|
134
|
+
def update_params!(params)
|
135
|
+
@params.update(_deep_dup(params))
|
136
|
+
@inherited_params.update(@params.delete(:inherited) || {})
|
137
|
+
|
138
|
+
# Note: self.class.default_params is NOT to be merged in @child_params
|
139
|
+
@child_params = @inherited_params.merge(@params)
|
140
|
+
self.class.special_params.each { |name| @child_params.delete(name) }
|
141
|
+
|
142
|
+
self.class.required_params.each do |rp|
|
143
|
+
raise "Required param is missing: #{rp}" unless @pmerger.param?(rp)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def param(name, default = nil)
|
148
|
+
@pmerger.param(name) || default
|
149
|
+
end
|
150
|
+
|
151
|
+
def param!(name)
|
152
|
+
param(name) or raise "No default value for: #{name}"
|
153
|
+
end
|
154
|
+
|
155
|
+
def draw(*args, debug: false)
|
156
|
+
ensure_complete!
|
157
|
+
@debug = debug
|
158
|
+
_draw(*args)
|
159
|
+
end
|
160
|
+
|
161
|
+
def ensure_complete!
|
162
|
+
pending_element = incomplete
|
163
|
+
raise ElementIncomplete, pending_element if pending_element
|
164
|
+
end
|
165
|
+
|
166
|
+
#
|
167
|
+
# To be defined in subclasses
|
168
|
+
#
|
169
|
+
|
170
|
+
def width
|
171
|
+
raise NotImplementedError
|
172
|
+
end
|
173
|
+
|
174
|
+
def height
|
175
|
+
raise NotImplementedError
|
176
|
+
end
|
177
|
+
|
178
|
+
def incomplete
|
179
|
+
raise NotImplementedError
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
def _draw(*)
|
185
|
+
raise NotImplementedError
|
186
|
+
end
|
187
|
+
|
188
|
+
def _complete?
|
189
|
+
raise NotImplementedError
|
190
|
+
end
|
191
|
+
|
192
|
+
def draw_border(svg, width_override: width, height_override: height)
|
193
|
+
borders = param(:borders)
|
194
|
+
borders ||= %i[left right top bottom] if param(:border)
|
195
|
+
Border.draw(
|
196
|
+
svg,
|
197
|
+
width_override,
|
198
|
+
height_override,
|
199
|
+
borders,
|
200
|
+
param(:border_style),
|
201
|
+
param(:class),
|
202
|
+
@debug
|
203
|
+
)
|
204
|
+
end
|
205
|
+
|
206
|
+
def _deep_dup(value)
|
207
|
+
case value
|
208
|
+
when Array then value.map { |v| _deep_dup(v) }
|
209
|
+
when Hash then value.map { |k, v| [_deep_dup(k), _deep_dup(v)] }.to_h
|
210
|
+
when Fixnum, Float, Symbol, TrueClass, FalseClass then value
|
211
|
+
else value.dup
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module SvgDrawer
|
2
|
+
class Circle < Base
|
3
|
+
defaults fill: 'none',
|
4
|
+
stroke: 'black',
|
5
|
+
size: 1,
|
6
|
+
x_reposition: 'none', # none/left/center/right
|
7
|
+
y_reposition: 'none', # none/top/middle/bottom
|
8
|
+
expand: false,
|
9
|
+
shrink: false,
|
10
|
+
overflow: false,
|
11
|
+
scale: 1,
|
12
|
+
scale_size: true
|
13
|
+
|
14
|
+
def initialize(center, radius, params = {})
|
15
|
+
@center = center
|
16
|
+
@radius = radius
|
17
|
+
super(params)
|
18
|
+
end
|
19
|
+
|
20
|
+
def width
|
21
|
+
param(:overflow) ?
|
22
|
+
param(:width, calc_width) :
|
23
|
+
[param(:width, 0), calc_width].max
|
24
|
+
end
|
25
|
+
|
26
|
+
def height
|
27
|
+
param(:overflow) ?
|
28
|
+
param(:height, calc_height) :
|
29
|
+
[param(:height, 0), calc_height].max
|
30
|
+
end
|
31
|
+
|
32
|
+
def incomplete
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
def min_x
|
37
|
+
@min_x ||= @center.first - @radius
|
38
|
+
end
|
39
|
+
|
40
|
+
def max_x
|
41
|
+
@max_x ||= @center.first + @radius
|
42
|
+
end
|
43
|
+
|
44
|
+
def min_y
|
45
|
+
@min_y ||= @center.last - @radius
|
46
|
+
end
|
47
|
+
|
48
|
+
def max_y
|
49
|
+
@max_y ||= @center.last + @radius
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def _draw(parent)
|
55
|
+
size = param(:scale_size) ? param(:size) : param(:size) / scale
|
56
|
+
style = {}
|
57
|
+
|
58
|
+
# need symbol keys due to a bug in Rasem::SVGTag#write_styles
|
59
|
+
style[:fill] = param(:fill)
|
60
|
+
style[:stroke] = param(:stroke)
|
61
|
+
style[:'stroke-width'] = size
|
62
|
+
|
63
|
+
Utils::RasemWrapper.group(parent, class: 'circle') do |circle_group|
|
64
|
+
poly = circle_group.circle(@center.first, @center.last, @radius, style: style.dup)
|
65
|
+
poly.translate(translate_x, translate_y).scale(scale, scale)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def calc_width
|
70
|
+
calc_width_unscaled * scale
|
71
|
+
end
|
72
|
+
|
73
|
+
def calc_height
|
74
|
+
calc_height_unscaled * scale
|
75
|
+
end
|
76
|
+
|
77
|
+
def calc_width_unscaled
|
78
|
+
max_x - min_x
|
79
|
+
end
|
80
|
+
|
81
|
+
def calc_height_unscaled
|
82
|
+
max_y - min_y
|
83
|
+
end
|
84
|
+
|
85
|
+
def width_unscaled
|
86
|
+
param(:overflow) ?
|
87
|
+
param(:width, calc_width_unscaled) :
|
88
|
+
[param(:width, 0), calc_width_unscaled].max
|
89
|
+
end
|
90
|
+
|
91
|
+
def height_unscaled
|
92
|
+
param(:overflow) ?
|
93
|
+
param(:height, calc_height_unscaled) :
|
94
|
+
[param(:height, 0), calc_height_unscaled].max
|
95
|
+
end
|
96
|
+
|
97
|
+
def scale
|
98
|
+
[scale_x, scale_y].min * param(:scale)
|
99
|
+
end
|
100
|
+
|
101
|
+
def scale_x
|
102
|
+
return 1 unless param(:width) && (param(:expand) || param(:shrink))
|
103
|
+
scale = param(:width).to_d / calc_width_unscaled
|
104
|
+
return 1 if (scale > 1 && !param(:expand)) || (scale < 1 && !param(:shrink))
|
105
|
+
scale
|
106
|
+
end
|
107
|
+
|
108
|
+
def scale_y
|
109
|
+
return 1 unless param(:height) && (param(:expand) || param(:shrink))
|
110
|
+
scale = param(:height).to_d / calc_height_unscaled
|
111
|
+
return 1 if (scale > 1 && !param(:expand)) || (scale < 1 && !param(:shrink))
|
112
|
+
scale
|
113
|
+
end
|
114
|
+
|
115
|
+
def translate_x
|
116
|
+
width_diff = (width - calc_width)
|
117
|
+
|
118
|
+
case param(:x_reposition)
|
119
|
+
when 'left' then -min_x * scale
|
120
|
+
when 'center' then -min_x * scale + width_diff / 2
|
121
|
+
when 'right' then -min_x * scale + width_diff
|
122
|
+
when 'none' then 0
|
123
|
+
else raise "Bad x_reposition: #{param(:x_reposition)}. Valid are: [left, right, center, none]"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def translate_y
|
128
|
+
height_diff = height - calc_height
|
129
|
+
|
130
|
+
case param(:y_reposition)
|
131
|
+
when 'top' then -min_y * scale
|
132
|
+
when 'middle' then -min_y * scale + height_diff / 2
|
133
|
+
when 'bottom' then -min_y * scale + height_diff
|
134
|
+
when 'none' then 0
|
135
|
+
else raise "Bad y_reposition: #{param(:y_reposition)}. Valid are: [top, bottom, middle, none]"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module SvgDrawer
|
2
|
+
class Multipolyline < Base
|
3
|
+
special :x_reposition, :y_reposition, :resize, :overflow,
|
4
|
+
:height, :width, :scale, :scale_size
|
5
|
+
|
6
|
+
# See notes in Polyline
|
7
|
+
defaults fill: 'none',
|
8
|
+
stroke: 'black',
|
9
|
+
linecap: 'butt',
|
10
|
+
linejoin: 'miter',
|
11
|
+
size: 1,
|
12
|
+
x_reposition: 'none', # none/left/center/right
|
13
|
+
y_reposition: 'none', # none/top/middle/bottom
|
14
|
+
expand: false,
|
15
|
+
shrink: false,
|
16
|
+
overflow: false,
|
17
|
+
scale: 1,
|
18
|
+
scale_size: true
|
19
|
+
|
20
|
+
def initialize(strokes, params = {})
|
21
|
+
super(params)
|
22
|
+
@polylines = strokes.map { |stroke| Polyline.new(stroke, child_params) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def width
|
26
|
+
param(:overflow) ?
|
27
|
+
param(:width, calc_width) :
|
28
|
+
[param(:width, 0), calc_width].max
|
29
|
+
end
|
30
|
+
|
31
|
+
def height
|
32
|
+
param(:overflow) ?
|
33
|
+
param(:height, calc_height) :
|
34
|
+
[param(:height, 0), calc_height].max
|
35
|
+
end
|
36
|
+
|
37
|
+
def incomplete
|
38
|
+
@polylines.none? ? self : @polylines.find(&:incomplete)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def _draw(parent)
|
44
|
+
unless param(:scale_size)
|
45
|
+
@polylines.each { |p| p.update_params!(size: param(:size) / scale) }
|
46
|
+
end
|
47
|
+
|
48
|
+
Utils::RasemWrapper.group(parent, class: 'multi_polyline') do |mpoly_group|
|
49
|
+
# Need a sub-group to prevent parents from overwriting translate()
|
50
|
+
grouped = Utils::RasemWrapper.group(mpoly_group) do |g|
|
51
|
+
@polylines.each { |p| p.draw(g, debug: @debug) }
|
52
|
+
end
|
53
|
+
|
54
|
+
grouped.translate(translate_x, translate_y).scale(scale, scale)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def min_x
|
59
|
+
@min_x ||= @polylines.min_by(&:min_x).min_x
|
60
|
+
end
|
61
|
+
|
62
|
+
def max_x
|
63
|
+
@max_x ||= @polylines.max_by(&:max_x).max_x
|
64
|
+
end
|
65
|
+
|
66
|
+
def min_y
|
67
|
+
@min_y ||= @polylines.min_by(&:min_y).min_y
|
68
|
+
end
|
69
|
+
|
70
|
+
def max_y
|
71
|
+
@max_y ||= @polylines.max_by(&:max_y).max_y
|
72
|
+
end
|
73
|
+
|
74
|
+
def calc_width
|
75
|
+
calc_width_unscaled * scale
|
76
|
+
end
|
77
|
+
|
78
|
+
def calc_height
|
79
|
+
calc_height_unscaled * scale
|
80
|
+
end
|
81
|
+
|
82
|
+
def calc_width_unscaled
|
83
|
+
max_x - min_x
|
84
|
+
end
|
85
|
+
|
86
|
+
def calc_height_unscaled
|
87
|
+
max_y - min_y
|
88
|
+
end
|
89
|
+
|
90
|
+
def width_unscaled
|
91
|
+
param(:overflow) ?
|
92
|
+
param(:width, calc_width_unscaled) :
|
93
|
+
[param(:width, 0), calc_width_unscaled].max
|
94
|
+
end
|
95
|
+
|
96
|
+
def height_unscaled
|
97
|
+
param(:overflow) ?
|
98
|
+
param(:height, calc_height_unscaled) :
|
99
|
+
[param(:height, 0), calc_height_unscaled].max
|
100
|
+
end
|
101
|
+
|
102
|
+
def scale
|
103
|
+
[scale_x, scale_y].min * param(:scale)
|
104
|
+
end
|
105
|
+
|
106
|
+
def scale_x
|
107
|
+
return 1 unless param(:width) && (param(:expand) || param(:shrink))
|
108
|
+
scale = param(:width).to_d / calc_width_unscaled
|
109
|
+
return 1 if (scale > 1 && !param(:expand)) || (scale < 1 && !param(:shrink))
|
110
|
+
scale
|
111
|
+
end
|
112
|
+
|
113
|
+
def scale_y
|
114
|
+
return 1 unless param(:height) && (param(:expand) || param(:shrink))
|
115
|
+
scale = param(:height).to_d / calc_height_unscaled
|
116
|
+
return 1 if (scale > 1 && !param(:expand)) || (scale < 1 && !param(:shrink))
|
117
|
+
scale
|
118
|
+
end
|
119
|
+
|
120
|
+
def translate_x
|
121
|
+
width_diff = (width - calc_width)
|
122
|
+
|
123
|
+
case param(:x_reposition)
|
124
|
+
when 'left' then -min_x * scale
|
125
|
+
when 'center' then -min_x * scale + width_diff / 2
|
126
|
+
when 'right' then -min_x * scale + width_diff
|
127
|
+
when 'none' then 0
|
128
|
+
else raise "Bad x_reposition: #{param(:x_reposition)}. Valid are: [left, right, center, none]"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def translate_y
|
133
|
+
height_diff = height - calc_height
|
134
|
+
|
135
|
+
case param(:y_reposition)
|
136
|
+
when 'top' then -min_y * scale
|
137
|
+
when 'middle' then -min_y * scale + height_diff / 2
|
138
|
+
when 'bottom' then -min_y * scale + height_diff
|
139
|
+
when 'none' then 0
|
140
|
+
else raise "Bad y_reposition: #{param(:y_reposition)}. Valid are: [top, bottom, middle, none]"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module SvgDrawer
|
2
|
+
class Path < Base
|
3
|
+
# Required since there is no known way to calculate them
|
4
|
+
requires :width
|
5
|
+
requires :height
|
6
|
+
|
7
|
+
# Retranslate ensures the parent element can correctly draw borders
|
8
|
+
defaults scale: [1, 1],
|
9
|
+
overflow: true, # false not supported
|
10
|
+
retranslate: false # true not supported
|
11
|
+
|
12
|
+
def initialize(path_components, defaults = {})
|
13
|
+
super(defaults)
|
14
|
+
@components = path_components
|
15
|
+
end
|
16
|
+
|
17
|
+
# No idea how to compute dimensions for paths
|
18
|
+
def width
|
19
|
+
raise NotImplementedError unless param(:overflow)
|
20
|
+
param(:width)
|
21
|
+
end
|
22
|
+
|
23
|
+
def height
|
24
|
+
raise NotImplementedError unless param(:overflow)
|
25
|
+
param(:height)
|
26
|
+
end
|
27
|
+
|
28
|
+
def incomplete
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def _draw(parent)
|
35
|
+
# No idea how to find boundary coordinates
|
36
|
+
raise NotImplementedError if param(:retranslate)
|
37
|
+
|
38
|
+
Utils::RasemWrapper.group(parent, class: 'path') do |path_group|
|
39
|
+
@components.each { |path| path_group.path(d: path) }
|
40
|
+
end.scale(*param(:scale))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|