svg_drawer 1.0.0
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 +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
|