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
@@ -0,0 +1,171 @@
|
|
1
|
+
module SvgDrawer
|
2
|
+
class Polyline < Base
|
3
|
+
# NOTE: reposition and scale behaviors can be moved to Base
|
4
|
+
# but that is not needed at the moment
|
5
|
+
#
|
6
|
+
# :expand ensures that if the elem is smaller than the given dimensions,
|
7
|
+
# it will be scaled *up* until its either X or Y dim hits the bounds
|
8
|
+
# :shrink is similar, but scales the element *down* until both X and Y
|
9
|
+
# dim fit within the bounds
|
10
|
+
#
|
11
|
+
# If either :expand or :shrink are given, :overflow is ignored
|
12
|
+
#
|
13
|
+
# :scale_size is taken into account only if :shrink and/or :expand are
|
14
|
+
# given, and determines whether the given size (i.e. stroke-width) is
|
15
|
+
# also scaled accordingly
|
16
|
+
#
|
17
|
+
# :dotspace, if given, will cause the line to become dotted
|
18
|
+
#
|
19
|
+
defaults fill: 'none',
|
20
|
+
stroke: 'black',
|
21
|
+
linecap: 'butt',
|
22
|
+
linejoin: 'miter',
|
23
|
+
size: 1,
|
24
|
+
x_reposition: 'none', # none/left/center/right
|
25
|
+
y_reposition: 'none', # none/top/middle/bottom
|
26
|
+
expand: false,
|
27
|
+
shrink: false,
|
28
|
+
dotspace: 0,
|
29
|
+
overflow: false,
|
30
|
+
scale: 1,
|
31
|
+
scale_size: true
|
32
|
+
|
33
|
+
def initialize(points, params = {})
|
34
|
+
@points = points
|
35
|
+
super(params)
|
36
|
+
end
|
37
|
+
|
38
|
+
def width
|
39
|
+
param(:overflow) ?
|
40
|
+
param(:width, calc_width) :
|
41
|
+
[param(:width, 0), calc_width].max
|
42
|
+
end
|
43
|
+
|
44
|
+
def height
|
45
|
+
param(:overflow) ?
|
46
|
+
param(:height, calc_height) :
|
47
|
+
[param(:height, 0), calc_height].max
|
48
|
+
end
|
49
|
+
|
50
|
+
def incomplete
|
51
|
+
@points.size < 4 || @points.size.odd? ? self : nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def min_x
|
55
|
+
@min_x ||= @points.each_slice(2).min_by(&:first).first - cap_size
|
56
|
+
end
|
57
|
+
|
58
|
+
def max_x
|
59
|
+
@max_x ||= @points.each_slice(2).max_by(&:first).first + cap_size
|
60
|
+
end
|
61
|
+
|
62
|
+
def min_y
|
63
|
+
@min_y ||= @points.each_slice(2).min_by(&:last).last - cap_size
|
64
|
+
end
|
65
|
+
|
66
|
+
def max_y
|
67
|
+
@max_y ||= @points.each_slice(2).max_by(&:last).last + cap_size
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def _draw(parent)
|
73
|
+
size = param(:scale_size) ? param(:size) : param(:size) / scale
|
74
|
+
dotspace = param(:scale_size) ? param(:dotspace) : param(:dotspace) / scale
|
75
|
+
dotsize = size
|
76
|
+
style = {}
|
77
|
+
|
78
|
+
if param(:linecap).eql?('round')
|
79
|
+
dotsize = 0
|
80
|
+
dotspace *= 2
|
81
|
+
end
|
82
|
+
|
83
|
+
# need symbol keys due to a bug in Rasem::SVGTag#write_styles
|
84
|
+
style[:fill] = param(:fill)
|
85
|
+
style[:stroke] = param(:stroke)
|
86
|
+
style[:'stroke-width'] = size
|
87
|
+
style[:'stroke-linecap'] = param(:linecap)
|
88
|
+
style[:'stroke-linejoin'] = param(:linejoin)
|
89
|
+
style[:'stroke-dasharray'] = "#{dotsize}, #{dotspace}" if dotspace > 0
|
90
|
+
|
91
|
+
Utils::RasemWrapper.group(parent, class: 'polyline') do |polyline_group|
|
92
|
+
poly = polyline_group.polyline(@points, style: style.dup)
|
93
|
+
poly.translate(translate_x, translate_y).scale(scale, scale)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def calc_width
|
98
|
+
calc_width_unscaled * scale
|
99
|
+
end
|
100
|
+
|
101
|
+
def calc_height
|
102
|
+
calc_height_unscaled * scale
|
103
|
+
end
|
104
|
+
|
105
|
+
def calc_width_unscaled
|
106
|
+
max_x - min_x
|
107
|
+
end
|
108
|
+
|
109
|
+
def calc_height_unscaled
|
110
|
+
max_y - min_y
|
111
|
+
end
|
112
|
+
|
113
|
+
def width_unscaled
|
114
|
+
param(:overflow) ?
|
115
|
+
param(:width, calc_width_unscaled) :
|
116
|
+
[param(:width, 0), calc_width_unscaled].max
|
117
|
+
end
|
118
|
+
|
119
|
+
def height_unscaled
|
120
|
+
param(:overflow) ?
|
121
|
+
param(:height, calc_height_unscaled) :
|
122
|
+
[param(:height, 0), calc_height_unscaled].max
|
123
|
+
end
|
124
|
+
|
125
|
+
def scale
|
126
|
+
[scale_x, scale_y].min * param(:scale)
|
127
|
+
end
|
128
|
+
|
129
|
+
def scale_x
|
130
|
+
return 1 unless param(:width) && (param(:expand) || param(:shrink))
|
131
|
+
scale = param(:width).to_d / calc_width_unscaled
|
132
|
+
return 1 if (scale > 1 && !param(:expand)) || (scale < 1 && !param(:shrink))
|
133
|
+
scale
|
134
|
+
end
|
135
|
+
|
136
|
+
def scale_y
|
137
|
+
return 1 unless param(:height) && (param(:expand) || param(:shrink))
|
138
|
+
scale = param(:height).to_d / calc_height_unscaled
|
139
|
+
return 1 if (scale > 1 && !param(:expand)) || (scale < 1 && !param(:shrink))
|
140
|
+
scale
|
141
|
+
end
|
142
|
+
|
143
|
+
def translate_x
|
144
|
+
width_diff = (width - calc_width)
|
145
|
+
|
146
|
+
case param(:x_reposition)
|
147
|
+
when 'left' then -min_x * scale
|
148
|
+
when 'center' then -min_x * scale + width_diff / 2
|
149
|
+
when 'right' then -min_x * scale + width_diff
|
150
|
+
when 'none' then 0
|
151
|
+
else raise "Bad x_reposition: #{param(:x_reposition)}. Valid are: [left, right, center, none]"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def translate_y
|
156
|
+
height_diff = height - calc_height
|
157
|
+
|
158
|
+
case param(:y_reposition)
|
159
|
+
when 'top' then -min_y * scale
|
160
|
+
when 'middle' then -min_y * scale + height_diff / 2
|
161
|
+
when 'bottom' then -min_y * scale + height_diff
|
162
|
+
when 'none' then 0
|
163
|
+
else raise "Bad y_reposition: #{param(:y_reposition)}. Valid are: [top, bottom, middle, none]"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def cap_size
|
168
|
+
param(:size).to_d / 2
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SvgDrawer
|
2
|
+
class BlankRow < Base
|
3
|
+
requires :columns
|
4
|
+
requires :width
|
5
|
+
requires :height
|
6
|
+
|
7
|
+
def incomplete
|
8
|
+
false
|
9
|
+
end
|
10
|
+
|
11
|
+
def width
|
12
|
+
@width ||= param(:width)
|
13
|
+
end
|
14
|
+
|
15
|
+
def height
|
16
|
+
@height ||= param(:height)
|
17
|
+
end
|
18
|
+
|
19
|
+
def cell_widths
|
20
|
+
Array.new(param(:columns), 0)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def _draw(parent, _col_widths)
|
26
|
+
Utils::RasemWrapper.group(parent, class: param(:class), id: param(:id)) do |row_group|
|
27
|
+
draw_border(row_group)
|
28
|
+
row_group.rectangle(0, 0, width, height, fill: 'none', stroke: 'none')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module SvgDrawer
|
2
|
+
module Border
|
3
|
+
module_function
|
4
|
+
|
5
|
+
DEFAULT_STYLE = { stroke: 'black', size: 1 }.freeze
|
6
|
+
|
7
|
+
#
|
8
|
+
# Draw a rectangle with the given width and height
|
9
|
+
# The rectangle is actually 4 lines, with opacity of 1 or 0,
|
10
|
+
# depending on the values in the `borders` array.
|
11
|
+
# (e.g. [:left, :top])
|
12
|
+
#
|
13
|
+
# All lines share the same style, given by the border_style hash
|
14
|
+
# (see DEFAULT_STYLE for possible keys and their default values)
|
15
|
+
#
|
16
|
+
# For debugging purposes, lines can always be drawn even when there
|
17
|
+
# are no borders specified -- they are drawn transparent in this case.
|
18
|
+
# Drawing opacity=0 lines helps debugging in web inspector, but has
|
19
|
+
# some performance impact.
|
20
|
+
#
|
21
|
+
# @param parent [Rasem::SVGTagWithParent]
|
22
|
+
# @param width [Integer]
|
23
|
+
# @param height [Integer]
|
24
|
+
# @param borders [Array] (optional)
|
25
|
+
# @param border_style [Hash] (optional)
|
26
|
+
# @param svg_class [String] (optional)
|
27
|
+
# @param debug [Boolean] (optional) draw invisible borders
|
28
|
+
# @return [Rasem::SVGTagWithParent]
|
29
|
+
#
|
30
|
+
def draw(parent, width, height, borders, border_style, svg_class, debug)
|
31
|
+
return if !debug && (borders.nil? || borders.empty?)
|
32
|
+
|
33
|
+
style = DEFAULT_STYLE.merge(border_style || {})
|
34
|
+
style['stroke-width'] = style.delete(:size)
|
35
|
+
borders ||= []
|
36
|
+
|
37
|
+
line_points = {
|
38
|
+
top: [0, 0, width, 0],
|
39
|
+
right: [width, 0, width, height],
|
40
|
+
bottom: [width, height, 0, height],
|
41
|
+
left: [0, height, 0, 0]
|
42
|
+
}
|
43
|
+
|
44
|
+
klass = 'border'
|
45
|
+
klass.prepend("#{svg_class} ") if svg_class
|
46
|
+
|
47
|
+
Utils::RasemWrapper.group(parent, class: klass) do |group|
|
48
|
+
line_points.each do |border, points|
|
49
|
+
line_style = style.dup
|
50
|
+
|
51
|
+
# It is useful to draw an invisible border as this
|
52
|
+
# significantly helps debugging
|
53
|
+
unless borders.include?(border)
|
54
|
+
debug ? line_style[:opacity] = 0 : next
|
55
|
+
end
|
56
|
+
|
57
|
+
group.line(*points, line_style)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module SvgDrawer
|
2
|
+
class Cell < Base
|
3
|
+
def width
|
4
|
+
return @width if @width
|
5
|
+
ensure_complete!
|
6
|
+
@width = [param(:width, 0), @content.width].max
|
7
|
+
end
|
8
|
+
|
9
|
+
def height
|
10
|
+
return @height if @height
|
11
|
+
ensure_complete!
|
12
|
+
@height = [param(:height, 0), @content.height].max
|
13
|
+
end
|
14
|
+
|
15
|
+
def incomplete
|
16
|
+
@content.nil? ? self : @content.incomplete
|
17
|
+
end
|
18
|
+
|
19
|
+
def content(element = nil)
|
20
|
+
return @content unless element
|
21
|
+
raise TypeError, 'Argument must to respond to #draw' unless element.respond_to?(:draw)
|
22
|
+
element.update_params!(inherited: child_params)
|
23
|
+
@content = element
|
24
|
+
end
|
25
|
+
|
26
|
+
def text_box(text, params = {})
|
27
|
+
@content = TextBox.new(text, params.merge(inherited: child_params))
|
28
|
+
end
|
29
|
+
|
30
|
+
def path(path_components, params = {})
|
31
|
+
@content = Path.new(path_components, params.merge(inherited: child_params))
|
32
|
+
end
|
33
|
+
|
34
|
+
def polyline(points, params = {})
|
35
|
+
@content = Polyline.new(points, params.merge(inherited: child_params))
|
36
|
+
end
|
37
|
+
|
38
|
+
def multipolyline(strokes, params = {})
|
39
|
+
@content = Multipolyline.new(strokes, params.merge(inherited: child_params))
|
40
|
+
end
|
41
|
+
|
42
|
+
def line(points, params = {})
|
43
|
+
@content = Line.new(points, params.merge(inherited: child_params))
|
44
|
+
end
|
45
|
+
|
46
|
+
def circle(center, radius, params = {})
|
47
|
+
@content = Circle.new(center, radius, params.merge(inherited: child_params))
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# See Row#draw for info on col_width and row_height
|
52
|
+
#
|
53
|
+
# @param parent [Rasem::SVGTagWithParent]
|
54
|
+
# @param col_width [Integer] Table-wide max colum width
|
55
|
+
# @param row_height [Integer] Table-wide max row height
|
56
|
+
# @return [Rasem::SVGTagWithParent]
|
57
|
+
#
|
58
|
+
def _draw(parent)
|
59
|
+
Utils::RasemWrapper.group(parent, class: param(:class), id: param(:id)) do |cell_group|
|
60
|
+
draw_border(cell_group)
|
61
|
+
@content.draw(cell_group, debug: @debug)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module SvgDrawer
|
2
|
+
class Row < Base
|
3
|
+
requires :columns
|
4
|
+
|
5
|
+
special :width # row width is not the same as cell width
|
6
|
+
special :columns # makes no sense for a cell
|
7
|
+
special :col_widths # makes no sense for a cell
|
8
|
+
|
9
|
+
def width
|
10
|
+
ensure_complete!
|
11
|
+
sum_width = cell_widths.reduce(&:+)
|
12
|
+
[param(:width, 0), sum_width].max
|
13
|
+
end
|
14
|
+
|
15
|
+
def height
|
16
|
+
ensure_complete!
|
17
|
+
[param(:height, 0), cell_heights.max].max
|
18
|
+
end
|
19
|
+
|
20
|
+
def incomplete
|
21
|
+
cells.size != param(:columns) ? self : find_incomplete_descendant
|
22
|
+
end
|
23
|
+
|
24
|
+
def cell_widths
|
25
|
+
ensure_complete!
|
26
|
+
cells.map(&:width)
|
27
|
+
end
|
28
|
+
|
29
|
+
def cell_heights
|
30
|
+
ensure_complete!
|
31
|
+
cells.map(&:height)
|
32
|
+
end
|
33
|
+
|
34
|
+
def col_widths
|
35
|
+
Table.col_widths(param(:col_widths), param(:width), param(:columns))
|
36
|
+
end
|
37
|
+
|
38
|
+
def cells
|
39
|
+
@cells ||= []
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_cell(cell)
|
43
|
+
raise TypeError, "Expected Cell, got: #{cell.class}" unless cell.is_a?(Cell)
|
44
|
+
cell.update_params!(inherited: cell_params)
|
45
|
+
cells << cell
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# @param params [Hash] cell params.
|
51
|
+
# @return [Row] self
|
52
|
+
#
|
53
|
+
def cell(params = {})
|
54
|
+
raise 'Cannot add more cells' unless incomplete
|
55
|
+
cell = Cell.new(params.merge(inherited: cell_params))
|
56
|
+
yield(cell)
|
57
|
+
cells << cell
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def text_cell(text, params = {})
|
62
|
+
cell(params) { |c| c.text_box(text) }
|
63
|
+
end
|
64
|
+
|
65
|
+
def path_cell(path_components, params = {})
|
66
|
+
cell(params) { |c| c.path(path_components) }
|
67
|
+
end
|
68
|
+
|
69
|
+
def line_cell(points, params = {})
|
70
|
+
cell(params) { |c| c.line(points) }
|
71
|
+
end
|
72
|
+
|
73
|
+
def polyline_cell(points, params = {})
|
74
|
+
cell(params) { |c| c.polyline(points) }
|
75
|
+
end
|
76
|
+
|
77
|
+
def multipolyline_cell(strokes, params = {})
|
78
|
+
cell(params) { |c| c.multipolyline(strokes) }
|
79
|
+
end
|
80
|
+
|
81
|
+
def circle_cell(center, radius, params = {})
|
82
|
+
cell(params) { |c| c.circle(center, radius) }
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
#
|
88
|
+
# A note on cell widths:
|
89
|
+
# Cells are drawed not with their initial widths, but with the
|
90
|
+
# table-wide maximum width for the corresponding columns.
|
91
|
+
# This must happen at draw time, as we can't know what the max col
|
92
|
+
# width is until we have added all rows for the entire table.
|
93
|
+
#
|
94
|
+
# Similarly, for the heights:
|
95
|
+
# Cells are not drawed with their initial heigths, but with the
|
96
|
+
# row-wide maximum height.
|
97
|
+
# This must happen at draw time, as we can't know what the max cell
|
98
|
+
# height is until we have added all cells for this row.
|
99
|
+
#
|
100
|
+
# @param parent [Rasem::SVGTagWithParent]
|
101
|
+
# @param col_widths [Array] Table-wide max column widths
|
102
|
+
# @return [Rasem::SVGTagWithParent]
|
103
|
+
#
|
104
|
+
def _draw(parent, max_col_widths)
|
105
|
+
Utils::RasemWrapper.group(parent, class: param(:class), id: param(:id)) do |row_group|
|
106
|
+
draw_border(row_group, width_override: max_col_widths.reduce(&:+))
|
107
|
+
|
108
|
+
cells.zip(max_col_widths).reduce(0) do |x, (cell, col_width)|
|
109
|
+
cell.draw(row_group, debug: @debug).translate(x, 0)
|
110
|
+
x + col_width
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def cell_params
|
116
|
+
return child_params unless col_widths && col_widths[cells.size]
|
117
|
+
child_params.merge(width: col_widths[cells.size])
|
118
|
+
end
|
119
|
+
|
120
|
+
def find_incomplete_descendant
|
121
|
+
cells.each.with_object(nil) do |cell, _|
|
122
|
+
res = cell.incomplete
|
123
|
+
break res if res
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module SvgDrawer
|
2
|
+
class Table < Base
|
3
|
+
requires :columns
|
4
|
+
|
5
|
+
special :height # table height is not the same as row height
|
6
|
+
special :row_height # makes no sense for a row
|
7
|
+
|
8
|
+
defaults col_widths: nil
|
9
|
+
|
10
|
+
#
|
11
|
+
# Infer col_widths from width (if needed)
|
12
|
+
#
|
13
|
+
def self.col_widths(col_widths, width, columns)
|
14
|
+
return if col_widths.nil? && width.nil?
|
15
|
+
sum_width = col_widths.reduce(&:+) if col_widths
|
16
|
+
|
17
|
+
if col_widths && width && sum_width != width
|
18
|
+
raise ArgumentError, "Sum of given col widths (#{col_widths}) doesn't match total element width (#{width})"
|
19
|
+
end
|
20
|
+
|
21
|
+
col_widths || Array.new(columns, width.to_d / columns)
|
22
|
+
end
|
23
|
+
|
24
|
+
def width
|
25
|
+
ensure_complete!
|
26
|
+
sum_width = col_widths ? col_widths.reduce(&:+) : 0
|
27
|
+
max_width = max_col_widths.reduce(&:+)
|
28
|
+
|
29
|
+
[sum_width, max_width].max
|
30
|
+
end
|
31
|
+
|
32
|
+
def height
|
33
|
+
ensure_complete!
|
34
|
+
sum_height = rows.reduce(0) { |a, e| a + e.height }
|
35
|
+
[param(:height, 0), sum_height].max
|
36
|
+
end
|
37
|
+
|
38
|
+
def incomplete
|
39
|
+
rows.none? ? self : find_incomplete_descendant
|
40
|
+
end
|
41
|
+
|
42
|
+
def rows
|
43
|
+
@rows ||= []
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_row(row)
|
47
|
+
raise TypeError, "Expected Row, got: #{row.class}" unless row.is_a?(Row)
|
48
|
+
row.update_params!(inherited: row_params)
|
49
|
+
rows << row
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# The params hash can contain a special :height value
|
54
|
+
# It will be used instead of the @row_height when creating the row
|
55
|
+
#
|
56
|
+
# @param params [Hash] row params
|
57
|
+
# @return [Table] self
|
58
|
+
#
|
59
|
+
def row(params = {})
|
60
|
+
row = Row.new(params.merge(inherited: row_params))
|
61
|
+
yield(row)
|
62
|
+
rows << row
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
def text_row(texts, params = {})
|
67
|
+
texts = texts.nil? ? [nil] : Array(texts)
|
68
|
+
row(params) { |r| texts.each { |text| r.text_cell(text) } }
|
69
|
+
end
|
70
|
+
|
71
|
+
def path_row(path_components, params = {})
|
72
|
+
row(params) { |r| r.path_cell(path_components) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def line_row(points, params = {})
|
76
|
+
row(params) { |r| r.line_cell(points) }
|
77
|
+
end
|
78
|
+
|
79
|
+
def polyline_row(points, params = {})
|
80
|
+
row(params) { |r| r.polyline_cell(points) }
|
81
|
+
end
|
82
|
+
|
83
|
+
def multipolyline_row(strokes, params = {})
|
84
|
+
row(params) { |r| r.multipolyline_cell(strokes) }
|
85
|
+
end
|
86
|
+
|
87
|
+
def circle_row(center, radius, params = {})
|
88
|
+
row(params) { |r| r.circle_cell(center, radius) }
|
89
|
+
end
|
90
|
+
|
91
|
+
def sub_table_row(params = {})
|
92
|
+
t = Table.new(params)
|
93
|
+
row { |r| r.cell { |c| c.content(t) && yield(t) } }
|
94
|
+
end
|
95
|
+
|
96
|
+
def blank_row(params = {})
|
97
|
+
raise ArgumentError, ':height required' if !param(:row_height) && !params[:height]
|
98
|
+
rows << BlankRow.new(params.merge(inherited: row_params))
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
def col_widths
|
103
|
+
Table.col_widths(param(:col_widths), param(:width), param(:columns))
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
#
|
109
|
+
# The dimension overrides given when the table is actually
|
110
|
+
# the child of a (parent) table cell.
|
111
|
+
# In this case the overrides are used to draw proper borders
|
112
|
+
# (since the Cell element of the parent determines its)
|
113
|
+
#
|
114
|
+
# @param parent [Rasem::SVGTagWithParent]
|
115
|
+
# @param width_override [Integer] (optional) container width
|
116
|
+
# @param height_override [Integer] (optional) container height
|
117
|
+
# @return [Rasem::SVGTagWithParent]
|
118
|
+
#
|
119
|
+
def _draw(parent)
|
120
|
+
Utils::RasemWrapper.group(parent, class: param(:class), id: param(:id)) do |table_group|
|
121
|
+
draw_border(table_group)
|
122
|
+
|
123
|
+
rows.reduce(0) do |y, row|
|
124
|
+
row.draw(table_group, max_col_widths, debug: @debug).translate(0, y)
|
125
|
+
y + row.height
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def row_params
|
131
|
+
param(:row_height)
|
132
|
+
return child_params unless param(:row_height)
|
133
|
+
child_params.merge(height: param(:row_height))
|
134
|
+
end
|
135
|
+
|
136
|
+
def find_incomplete_descendant
|
137
|
+
rows.each.with_object(nil) do |row, _|
|
138
|
+
res = row.incomplete
|
139
|
+
break res if res
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def max_col_widths
|
144
|
+
rows.map(&:cell_widths).transpose.map(&:max)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|