prawn-svg 0.34.1 → 0.35.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/lint.yml +19 -0
- data/.github/workflows/test.yml +14 -27
- data/.gitignore +0 -1
- data/.rubocop.yml +86 -0
- data/.rubocop_todo.yml +51 -0
- data/Gemfile +4 -3
- data/Gemfile.lock +81 -0
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/lib/prawn/svg/attributes/clip_path.rb +3 -3
- data/lib/prawn/svg/attributes/opacity.rb +3 -3
- data/lib/prawn/svg/attributes/stroke.rb +9 -9
- data/lib/prawn/svg/attributes/transform.rb +2 -2
- data/lib/prawn/svg/attributes.rb +1 -1
- data/lib/prawn/svg/calculators/arc_to_bezier_curve.rb +17 -15
- data/lib/prawn/svg/calculators/aspect_ratio.rb +16 -14
- data/lib/prawn/svg/calculators/document_sizing.rb +9 -10
- data/lib/prawn/svg/calculators/pixels.rb +8 -5
- data/lib/prawn/svg/color.rb +209 -212
- data/lib/prawn/svg/css/font_family_parser.rb +2 -2
- data/lib/prawn/svg/css/selector_parser.rb +39 -35
- data/lib/prawn/svg/css/stylesheets.rb +31 -24
- data/lib/prawn/svg/css/values_parser.rb +68 -0
- data/lib/prawn/svg/document.rb +6 -5
- data/lib/prawn/svg/elements/base.rb +39 -34
- data/lib/prawn/svg/elements/circle.rb +4 -4
- data/lib/prawn/svg/elements/clip_path.rb +0 -1
- data/lib/prawn/svg/elements/depth_first_base.rb +6 -6
- data/lib/prawn/svg/elements/ellipse.rb +3 -4
- data/lib/prawn/svg/elements/gradient.rb +49 -51
- data/lib/prawn/svg/elements/image.rb +5 -5
- data/lib/prawn/svg/elements/marker.rb +6 -7
- data/lib/prawn/svg/elements/path.rb +46 -47
- data/lib/prawn/svg/elements/polygon.rb +1 -1
- data/lib/prawn/svg/elements/polyline.rb +2 -2
- data/lib/prawn/svg/elements/rect.rb +3 -3
- data/lib/prawn/svg/elements/text.rb +1 -1
- data/lib/prawn/svg/elements/text_component.rb +22 -22
- data/lib/prawn/svg/elements/use.rb +4 -8
- data/lib/prawn/svg/elements/viewport.rb +5 -4
- data/lib/prawn/svg/elements.rb +30 -29
- data/lib/prawn/svg/extension.rb +2 -1
- data/lib/prawn/svg/font.rb +7 -7
- data/lib/prawn/svg/font_registry.rb +13 -13
- data/lib/prawn/svg/gradients.rb +3 -2
- data/lib/prawn/svg/interface.rb +4 -3
- data/lib/prawn/svg/loaders/data.rb +2 -2
- data/lib/prawn/svg/loaders/file.rb +12 -14
- data/lib/prawn/svg/loaders/web.rb +4 -8
- data/lib/prawn/svg/pathable.rb +41 -37
- data/lib/prawn/svg/properties.rb +34 -33
- data/lib/prawn/svg/renderer.rb +7 -7
- data/lib/prawn/svg/state.rb +1 -1
- data/lib/prawn/svg/transform_parser.rb +5 -5
- data/lib/prawn/svg/ttf.rb +21 -17
- data/lib/prawn/svg/url_loader.rb +1 -1
- data/lib/prawn/svg/version.rb +1 -1
- data/lib/prawn-svg.rb +1 -0
- data/prawn-svg.gemspec +4 -6
- data/spec/integration_spec.rb +77 -70
- data/spec/prawn/svg/attributes/opacity_spec.rb +11 -15
- data/spec/prawn/svg/attributes/transform_spec.rb +6 -6
- data/spec/prawn/svg/calculators/aspect_ratio_spec.rb +50 -50
- data/spec/prawn/svg/calculators/document_sizing_spec.rb +35 -35
- data/spec/prawn/svg/calculators/pixels_spec.rb +31 -30
- data/spec/prawn/svg/color_spec.rb +31 -31
- data/spec/prawn/svg/css/font_family_parser_spec.rb +12 -12
- data/spec/prawn/svg/css/selector_parser_spec.rb +21 -21
- data/spec/prawn/svg/css/stylesheets_spec.rb +51 -43
- data/spec/prawn/svg/css/values_parser_spec.rb +16 -0
- data/spec/prawn/svg/document_spec.rb +15 -14
- data/spec/prawn/svg/elements/base_spec.rb +39 -34
- data/spec/prawn/svg/elements/gradient_spec.rb +39 -39
- data/spec/prawn/svg/elements/line_spec.rb +22 -22
- data/spec/prawn/svg/elements/marker_spec.rb +44 -47
- data/spec/prawn/svg/elements/path_spec.rb +134 -110
- data/spec/prawn/svg/elements/polygon_spec.rb +18 -18
- data/spec/prawn/svg/elements/polyline_spec.rb +16 -16
- data/spec/prawn/svg/elements/text_spec.rb +149 -127
- data/spec/prawn/svg/font_registry_spec.rb +34 -34
- data/spec/prawn/svg/font_spec.rb +4 -4
- data/spec/prawn/svg/interface_spec.rb +47 -39
- data/spec/prawn/svg/loaders/data_spec.rb +21 -21
- data/spec/prawn/svg/loaders/file_spec.rb +43 -40
- data/spec/prawn/svg/loaders/web_spec.rb +15 -15
- data/spec/prawn/svg/pathable_spec.rb +21 -21
- data/spec/prawn/svg/properties_spec.rb +51 -51
- data/spec/prawn/svg/transform_parser_spec.rb +12 -12
- data/spec/prawn/svg/ttf_spec.rb +5 -5
- data/spec/prawn/svg/url_loader_spec.rb +25 -23
- data/spec/spec_helper.rb +4 -4
- metadata +12 -143
@@ -10,15 +10,17 @@ module Prawn::SVG::CSS
|
|
10
10
|
case token
|
11
11
|
when Modifier
|
12
12
|
part = token.type
|
13
|
-
result.last[part] ||= part == :name ? +
|
13
|
+
result.last[part] ||= part == :name ? +'' : []
|
14
14
|
when Identifier
|
15
15
|
return unless part
|
16
|
+
|
16
17
|
result.last[part] << token.name
|
17
18
|
when Attribute
|
18
|
-
return unless [
|
19
|
+
return unless ['=', '*=', '~=', '^=', '|=', '$=', nil].include?(token.operator)
|
20
|
+
|
19
21
|
(result.last[:attribute] ||= []) << [token.key, token.operator, token.value]
|
20
22
|
when Combinator
|
21
|
-
result << {combinator: token.type}
|
23
|
+
result << { combinator: token.type }
|
22
24
|
part = nil
|
23
25
|
end
|
24
26
|
end
|
@@ -26,9 +28,7 @@ module Prawn::SVG::CSS
|
|
26
28
|
result
|
27
29
|
end
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
VALID_CSS_IDENTIFIER_CHAR = /[a-zA-Z0-9_\u00a0-\uffff-]/
|
31
|
+
VALID_CSS_IDENTIFIER_CHAR = /[a-zA-Z0-9_\u00a0-\uffff-]/.freeze
|
32
32
|
Identifier = Struct.new(:name)
|
33
33
|
Modifier = Struct.new(:type)
|
34
34
|
Combinator = Struct.new(:type)
|
@@ -50,38 +50,38 @@ module Prawn::SVG::CSS
|
|
50
50
|
if VALID_CSS_IDENTIFIER_CHAR.match(char)
|
51
51
|
result.last.key = String.new(char)
|
52
52
|
attribute = :key
|
53
|
-
elsif char !=
|
53
|
+
elsif char != ' ' && char != "\t"
|
54
54
|
return
|
55
55
|
end
|
56
56
|
|
57
57
|
when :key
|
58
58
|
if VALID_CSS_IDENTIFIER_CHAR.match(char)
|
59
59
|
result.last.key << char
|
60
|
-
elsif char ==
|
60
|
+
elsif char == ']'
|
61
61
|
attribute = nil
|
62
|
-
elsif
|
62
|
+
elsif '=*~^|$'.include?(char)
|
63
63
|
result.last.operator = String.new(char)
|
64
64
|
attribute = :operator
|
65
|
-
elsif
|
65
|
+
elsif [' ', "\t"].include?(char)
|
66
66
|
attribute = :pre_operator
|
67
67
|
else
|
68
68
|
return
|
69
69
|
end
|
70
70
|
|
71
71
|
when :pre_operator
|
72
|
-
if
|
72
|
+
if '=*~^|$'.include?(char)
|
73
73
|
result.last.operator = String.new(char)
|
74
74
|
attribute = :operator
|
75
|
-
elsif char !=
|
75
|
+
elsif char != ' ' && char != "\t"
|
76
76
|
return
|
77
77
|
end
|
78
78
|
|
79
79
|
when :operator
|
80
|
-
if
|
80
|
+
if '=*~^|$'.include?(char)
|
81
81
|
result.last.operator << char
|
82
|
-
elsif
|
82
|
+
elsif [' ', "\t"].include?(char)
|
83
83
|
attribute = :pre_value
|
84
|
-
elsif
|
84
|
+
elsif ['"', "'"].include?(char)
|
85
85
|
result.last.value = +''
|
86
86
|
attribute = String.new(char)
|
87
87
|
else
|
@@ -90,16 +90,16 @@ module Prawn::SVG::CSS
|
|
90
90
|
end
|
91
91
|
|
92
92
|
when :pre_value
|
93
|
-
if
|
93
|
+
if ['"', "'"].include?(char)
|
94
94
|
result.last.value = +''
|
95
95
|
attribute = String.new(char)
|
96
|
-
elsif char !=
|
96
|
+
elsif char != ' ' && char != "\t"
|
97
97
|
result.last.value = String.new(char)
|
98
98
|
attribute = :value
|
99
99
|
end
|
100
100
|
|
101
101
|
when :value
|
102
|
-
if char ==
|
102
|
+
if char == ']'
|
103
103
|
result.last.value = String.new(result.last.value.rstrip)
|
104
104
|
attribute = nil
|
105
105
|
else
|
@@ -107,7 +107,7 @@ module Prawn::SVG::CSS
|
|
107
107
|
end
|
108
108
|
|
109
109
|
when '"', "'"
|
110
|
-
if char ==
|
110
|
+
if char == '\\' && !quote
|
111
111
|
quote = true
|
112
112
|
elsif char == attribute && !quote
|
113
113
|
attribute = :post_string
|
@@ -117,9 +117,9 @@ module Prawn::SVG::CSS
|
|
117
117
|
end
|
118
118
|
|
119
119
|
when :post_string
|
120
|
-
if char ==
|
120
|
+
if char == ']'
|
121
121
|
attribute = nil
|
122
|
-
elsif char !=
|
122
|
+
elsif char != ' ' && char != "\t"
|
123
123
|
return
|
124
124
|
end
|
125
125
|
end
|
@@ -129,37 +129,41 @@ module Prawn::SVG::CSS
|
|
129
129
|
when Identifier
|
130
130
|
result.last.name << char
|
131
131
|
else
|
132
|
-
result << Modifier.new(:name)
|
132
|
+
result << Modifier.new(:name) unless result.last.is_a?(Modifier)
|
133
133
|
result << Identifier.new(char)
|
134
134
|
end
|
135
135
|
else
|
136
136
|
case char
|
137
|
-
when
|
137
|
+
when '.'
|
138
138
|
result << Modifier.new(:class)
|
139
|
-
when
|
139
|
+
when '#'
|
140
140
|
result << Modifier.new(:id)
|
141
|
-
when
|
141
|
+
when ':'
|
142
142
|
result << Modifier.new(:pseudo_class)
|
143
|
-
when
|
143
|
+
when ' ', "\t"
|
144
144
|
result << Combinator.new(:descendant) unless result.last.is_a?(Combinator)
|
145
|
-
when
|
145
|
+
when '>'
|
146
146
|
result.pop if result.last == Combinator.new(:descendant)
|
147
147
|
result << Combinator.new(:child)
|
148
|
-
when
|
148
|
+
when '+'
|
149
149
|
result.pop if result.last == Combinator.new(:descendant)
|
150
150
|
result << Combinator.new(:adjacent)
|
151
|
-
when
|
151
|
+
when '~'
|
152
152
|
result.pop if result.last == Combinator.new(:descendant)
|
153
153
|
result << Combinator.new(:siblings)
|
154
|
-
when
|
154
|
+
when '*'
|
155
155
|
return unless result.empty? || result.last.is_a?(Combinator)
|
156
|
+
|
156
157
|
result << Modifier.new(:name)
|
157
|
-
result << Identifier.new(
|
158
|
-
when
|
159
|
-
|
160
|
-
|
158
|
+
result << Identifier.new('*')
|
159
|
+
when '(' # e.g. :nth-child(3n+4)
|
160
|
+
unless result.last.is_a?(Identifier) && result[-2] && result[-2].is_a?(Modifier) && result[-2].type == :pseudo_class
|
161
|
+
return
|
162
|
+
end
|
163
|
+
|
164
|
+
result.last.name << '('
|
161
165
|
brackets = true
|
162
|
-
when
|
166
|
+
when '['
|
163
167
|
result << Attribute.new
|
164
168
|
attribute = :pre_key
|
165
169
|
else
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module Prawn::SVG::CSS
|
2
2
|
class Stylesheets
|
3
|
+
USER_AGENT_STYLESHEET = 'svg, symbol, image, marker, pattern, foreignObject { overflow: hidden }'.freeze
|
4
|
+
|
3
5
|
attr_reader :css_parser, :root, :media
|
4
6
|
|
5
7
|
def initialize(css_parser, root, media = :all)
|
@@ -9,6 +11,7 @@ module Prawn::SVG::CSS
|
|
9
11
|
end
|
10
12
|
|
11
13
|
def load
|
14
|
+
load_user_agent_stylesheet
|
12
15
|
load_style_elements
|
13
16
|
xpath_styles = gather_xpath_styles
|
14
17
|
associate_xpath_styles_with_elements(xpath_styles)
|
@@ -16,6 +19,10 @@ module Prawn::SVG::CSS
|
|
16
19
|
|
17
20
|
private
|
18
21
|
|
22
|
+
def load_user_agent_stylesheet
|
23
|
+
css_parser.add_block!(USER_AGENT_STYLESHEET)
|
24
|
+
end
|
25
|
+
|
19
26
|
def load_style_elements
|
20
27
|
REXML::XPath.match(root, '//style').each do |source|
|
21
28
|
data = source.texts.map(&:value).join
|
@@ -32,14 +39,14 @@ module Prawn::SVG::CSS
|
|
32
39
|
rule_set.each_declaration { |*data| declarations << data }
|
33
40
|
|
34
41
|
rule_set.selectors.each do |selector_text|
|
35
|
-
|
36
|
-
xpath = css_selector_to_xpath(selector)
|
37
|
-
specificity = calculate_specificity(selector)
|
38
|
-
specificity << order
|
39
|
-
order += 1
|
42
|
+
next unless (selector = Prawn::SVG::CSS::SelectorParser.parse(selector_text))
|
40
43
|
|
41
|
-
|
42
|
-
|
44
|
+
xpath = css_selector_to_xpath(selector)
|
45
|
+
specificity = calculate_specificity(selector)
|
46
|
+
specificity << order
|
47
|
+
order += 1
|
48
|
+
|
49
|
+
xpath_styles << [xpath, declarations, specificity]
|
43
50
|
end
|
44
51
|
end
|
45
52
|
|
@@ -59,7 +66,7 @@ module Prawn::SVG::CSS
|
|
59
66
|
end
|
60
67
|
|
61
68
|
def xpath_quote(value)
|
62
|
-
%
|
69
|
+
%("#{value.gsub('\\', '\\\\').gsub('"', '\\"')}") if value
|
63
70
|
end
|
64
71
|
|
65
72
|
def css_selector_to_xpath(selector)
|
@@ -69,33 +76,33 @@ module Prawn::SVG::CSS
|
|
69
76
|
|
70
77
|
result = case element[:combinator]
|
71
78
|
when :child
|
72
|
-
+
|
79
|
+
+'/'
|
73
80
|
when :adjacent
|
74
81
|
pseudo_classes << 'first-child'
|
75
|
-
+
|
82
|
+
+'/following-sibling::'
|
76
83
|
when :siblings
|
77
|
-
+
|
84
|
+
+'/following-sibling::'
|
78
85
|
else
|
79
|
-
+
|
86
|
+
+'//'
|
80
87
|
end
|
81
88
|
|
82
89
|
positions = []
|
83
90
|
pseudo_classes.each do |pc|
|
84
91
|
case pc
|
85
|
-
when
|
86
|
-
when
|
92
|
+
when 'first-child' then positions << '1'
|
93
|
+
when 'last-child' then positions << 'last()'
|
87
94
|
when /^nth-child\((\d+)\)$/ then positions << $1
|
88
95
|
end
|
89
96
|
end
|
90
97
|
|
91
|
-
|
92
|
-
result <<
|
98
|
+
unless positions.empty?
|
99
|
+
result << '*' unless require_function_name
|
93
100
|
require_function_name = true
|
94
101
|
|
95
102
|
logic = if positions.length == 1
|
96
103
|
positions.first
|
97
104
|
else
|
98
|
-
positions.map { |position| "position()=#{position}" }.join(
|
105
|
+
positions.map { |position| "position()=#{position}" }.join(' and ')
|
99
106
|
end
|
100
107
|
|
101
108
|
result << "[#{logic}]"
|
@@ -114,17 +121,17 @@ module Prawn::SVG::CSS
|
|
114
121
|
case operator
|
115
122
|
when nil
|
116
123
|
result << "[@#{key}]"
|
117
|
-
when
|
124
|
+
when '='
|
118
125
|
result << "[@#{key}=#{xpath_quote value}]"
|
119
|
-
when
|
126
|
+
when '^='
|
120
127
|
result << "[starts-with(@#{key}, #{xpath_quote value})]"
|
121
|
-
when
|
128
|
+
when '$='
|
122
129
|
result << "[substring(@#{key}, string-length(@#{key}) - #{value.length - 1}) = #{xpath_quote value})]"
|
123
|
-
when
|
130
|
+
when '*='
|
124
131
|
result << "[contains(@#{key}, #{xpath_quote value})]"
|
125
|
-
when
|
132
|
+
when '~='
|
126
133
|
result << "[contains(concat(' ',@#{key},' '), #{xpath_quote " #{value} "})]"
|
127
|
-
when
|
134
|
+
when '|='
|
128
135
|
result << "[contains(concat('-',@#{key},'-'), #{xpath_quote "-#{value}-"})]"
|
129
136
|
end
|
130
137
|
end
|
@@ -138,7 +145,7 @@ module Prawn::SVG::CSS
|
|
138
145
|
[
|
139
146
|
a + (element[:id] || []).length,
|
140
147
|
b + (element[:class] || []).length + (element[:attribute] || []).length + (element[:pseudo_class] || []).length,
|
141
|
-
c + (element[:name] && element[:name] !=
|
148
|
+
c + (element[:name] && element[:name] != '*' ? 1 : 0)
|
142
149
|
]
|
143
150
|
end
|
144
151
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Prawn::SVG::CSS
|
2
|
+
class ValuesParser
|
3
|
+
class << self
|
4
|
+
def parse(values)
|
5
|
+
result = []
|
6
|
+
|
7
|
+
while values
|
8
|
+
value, remainder = parse_next(values)
|
9
|
+
break unless value
|
10
|
+
|
11
|
+
result << value
|
12
|
+
values = remainder
|
13
|
+
end
|
14
|
+
|
15
|
+
result
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def parse_next(values)
|
21
|
+
values = values.strip
|
22
|
+
return if values.empty?
|
23
|
+
|
24
|
+
if (matches = values.match(/\A([a-z-]+)\(\s*(.+)/i))
|
25
|
+
parse_function_call(matches[1].downcase, matches[2])
|
26
|
+
else
|
27
|
+
values.split(/\s+/, 2)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Note this does not support space-separated arguments.
|
32
|
+
# I don't think CSS 2 has any, but in case it does here is the place to add them.
|
33
|
+
def parse_function_call(name, rest)
|
34
|
+
arguments = []
|
35
|
+
in_quote = nil
|
36
|
+
in_escape = false
|
37
|
+
current = +''
|
38
|
+
|
39
|
+
rest.each_char.with_index do |char, index|
|
40
|
+
if in_escape
|
41
|
+
current << char
|
42
|
+
in_escape = false
|
43
|
+
elsif %w[" '].include?(char)
|
44
|
+
if in_quote == char
|
45
|
+
in_quote = nil
|
46
|
+
elsif in_quote.nil?
|
47
|
+
in_quote = char
|
48
|
+
else
|
49
|
+
current << char
|
50
|
+
end
|
51
|
+
elsif char == '\\'
|
52
|
+
in_escape = true
|
53
|
+
elsif in_quote.nil? && char == ','
|
54
|
+
arguments << current.strip
|
55
|
+
current = +''
|
56
|
+
elsif in_quote.nil? && char == ')'
|
57
|
+
arguments << current.strip
|
58
|
+
return [[name, arguments], rest[index + 1..]]
|
59
|
+
else
|
60
|
+
current << char
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
[rest, nil]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/prawn/svg/document.rb
CHANGED
@@ -2,7 +2,7 @@ class Prawn::SVG::Document
|
|
2
2
|
Error = Class.new(StandardError)
|
3
3
|
InvalidSVGData = Class.new(Error)
|
4
4
|
|
5
|
-
DEFAULT_FALLBACK_FONT_NAME =
|
5
|
+
DEFAULT_FALLBACK_FONT_NAME = 'Times-Roman'.freeze
|
6
6
|
|
7
7
|
# An +Array+ of warnings that occurred while parsing the SVG data.
|
8
8
|
attr_reader :warnings
|
@@ -20,10 +20,11 @@ class Prawn::SVG::Document
|
|
20
20
|
@root = REXML::Document.new(data).root
|
21
21
|
|
22
22
|
if @root.nil?
|
23
|
-
if data.respond_to?(:end_with?) && data.end_with?(
|
24
|
-
raise InvalidSVGData,
|
23
|
+
if data.respond_to?(:end_with?) && data.end_with?('.svg')
|
24
|
+
raise InvalidSVGData,
|
25
|
+
"The data supplied is not a valid SVG document. It looks like you've supplied a filename instead; use IO.read(filename) to get the data before you pass it to prawn-svg."
|
25
26
|
else
|
26
|
-
raise InvalidSVGData,
|
27
|
+
raise InvalidSVGData, 'The data supplied is not a valid SVG document.'
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
@@ -65,7 +66,7 @@ class Prawn::SVG::Document
|
|
65
66
|
when nil, :rgb then :rgb
|
66
67
|
when :cmyk then :cmyk
|
67
68
|
else
|
68
|
-
raise ArgumentError,
|
69
|
+
raise ArgumentError, ':color_mode must be set to :rgb (default) or :cmyk'
|
69
70
|
end
|
70
71
|
end
|
71
72
|
end
|
@@ -13,9 +13,9 @@ class Prawn::SVG::Elements::Base
|
|
13
13
|
|
14
14
|
include Prawn::SVG::TransformParser
|
15
15
|
|
16
|
-
PAINT_TYPES = %w
|
16
|
+
PAINT_TYPES = %w[fill stroke].freeze
|
17
17
|
COMMA_WSP_REGEXP = Prawn::SVG::Elements::COMMA_WSP_REGEXP
|
18
|
-
SVG_NAMESPACE =
|
18
|
+
SVG_NAMESPACE = 'http://www.w3.org/2000/svg'.freeze
|
19
19
|
|
20
20
|
SkipElementQuietly = Class.new(StandardError)
|
21
21
|
SkipElementError = Class.new(StandardError)
|
@@ -37,7 +37,7 @@ class Prawn::SVG::Elements::Base
|
|
37
37
|
@properties = Prawn::SVG::Properties.new
|
38
38
|
|
39
39
|
if source && !state.inside_use
|
40
|
-
id = source.attributes[
|
40
|
+
id = source.attributes['id']
|
41
41
|
id = id.strip if id
|
42
42
|
|
43
43
|
document.elements_by_id[id] = self if id && id != ''
|
@@ -65,19 +65,16 @@ class Prawn::SVG::Elements::Base
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def name
|
68
|
-
@name ||= source ? source.name :
|
68
|
+
@name ||= source ? source.name : '???'
|
69
69
|
end
|
70
70
|
|
71
71
|
protected
|
72
72
|
|
73
|
-
def parse
|
74
|
-
end
|
73
|
+
def parse; end
|
75
74
|
|
76
|
-
def apply
|
77
|
-
end
|
75
|
+
def apply; end
|
78
76
|
|
79
|
-
def bounding_box
|
80
|
-
end
|
77
|
+
def bounding_box; end
|
81
78
|
|
82
79
|
def container?
|
83
80
|
false
|
@@ -128,13 +125,13 @@ class Prawn::SVG::Elements::Base
|
|
128
125
|
return unless source
|
129
126
|
|
130
127
|
svg_child_elements.each do |elem|
|
131
|
-
if element_class = Prawn::SVG::Elements::TAG_CLASS_MAPPING[elem.name.to_sym]
|
132
|
-
add_call
|
128
|
+
if (element_class = Prawn::SVG::Elements::TAG_CLASS_MAPPING[elem.name.to_sym])
|
129
|
+
add_call 'save'
|
133
130
|
|
134
131
|
child = element_class.new(@document, elem, @calls, state.dup)
|
135
132
|
child.process
|
136
133
|
|
137
|
-
add_call
|
134
|
+
add_call 'restore'
|
138
135
|
else
|
139
136
|
@document.warnings << "Unknown tag '#{elem.name}'; ignoring"
|
140
137
|
end
|
@@ -185,9 +182,7 @@ class Prawn::SVG::Elements::Base
|
|
185
182
|
|
186
183
|
next if [nil, 'inherit', 'none'].include?(color)
|
187
184
|
|
188
|
-
if color == 'currentColor'
|
189
|
-
color = computed_properties.color
|
190
|
-
end
|
185
|
+
color = computed_properties.color if color == 'currentColor'
|
191
186
|
|
192
187
|
results = Prawn::SVG::Color.parse(color, document.gradients, document.color_mode)
|
193
188
|
|
@@ -207,18 +202,12 @@ class Prawn::SVG::Elements::Base
|
|
207
202
|
|
208
203
|
# If we were unable to find a suitable color candidate,
|
209
204
|
# we turn off this type of paint.
|
210
|
-
if success.nil?
|
211
|
-
computed_properties.set(type, 'none')
|
212
|
-
end
|
205
|
+
computed_properties.set(type, 'none') if success.nil?
|
213
206
|
end
|
214
207
|
end
|
215
208
|
|
216
|
-
def clamp(value, min_value, max_value)
|
217
|
-
[[value, min_value].max, max_value].min
|
218
|
-
end
|
219
|
-
|
220
209
|
def extract_attributes_and_properties
|
221
|
-
if styles = document.element_styles[source]
|
210
|
+
if (styles = document.element_styles[source])
|
222
211
|
# TODO : implement !important, at the moment it's just ignored
|
223
212
|
styles.each do |name, value, _important|
|
224
213
|
@properties.set(name, value)
|
@@ -238,11 +227,11 @@ class Prawn::SVG::Elements::Base
|
|
238
227
|
def parse_css_declarations(declarations)
|
239
228
|
# copied from css_parser
|
240
229
|
declarations
|
241
|
-
.gsub(/(
|
242
|
-
.split(/[
|
230
|
+
.gsub(/(^\s*)|(\s*$)/, '')
|
231
|
+
.split(/[;$]+/m)
|
243
232
|
.each_with_object({}) do |decs, output|
|
244
|
-
if matches = decs.match(/\s*(.[^:]*)\s
|
245
|
-
property, value,
|
233
|
+
if (matches = decs.match(/\s*(.[^:]*)\s*:\s*(.[^;]*)\s*(;|\Z)/i))
|
234
|
+
property, value, = matches.captures
|
246
235
|
output[property.downcase] = value
|
247
236
|
end
|
248
237
|
end
|
@@ -251,23 +240,39 @@ class Prawn::SVG::Elements::Base
|
|
251
240
|
def require_attributes(*names)
|
252
241
|
missing_attrs = names - attributes.keys
|
253
242
|
if missing_attrs.any?
|
254
|
-
raise MissingAttributesError, "Must have attributes #{missing_attrs.join(
|
243
|
+
raise MissingAttributesError, "Must have attributes #{missing_attrs.join(', ')} on tag #{name}; skipping tag"
|
255
244
|
end
|
256
245
|
end
|
257
246
|
|
258
247
|
def require_positive_value(*args)
|
259
|
-
if args.any? {|arg| arg.nil? || arg <= 0}
|
248
|
+
if args.any? { |arg| arg.nil? || arg <= 0 }
|
260
249
|
raise SkipElementError, "Invalid attributes on tag #{name}; skipping tag"
|
261
250
|
end
|
262
251
|
end
|
263
252
|
|
264
|
-
def extract_element_from_url_id_reference(
|
265
|
-
|
266
|
-
|
267
|
-
|
253
|
+
def extract_element_from_url_id_reference(values, expected_type = nil)
|
254
|
+
Prawn::SVG::CSS::ValuesParser.parse(values).detect do |value|
|
255
|
+
case value
|
256
|
+
in ['url', [url]]
|
257
|
+
element = document.elements_by_id[url[1..]] if url.start_with?('#')
|
258
|
+
break element if element && (expected_type.nil? || element.name == expected_type)
|
259
|
+
else
|
260
|
+
nil
|
261
|
+
end
|
262
|
+
end
|
268
263
|
end
|
269
264
|
|
270
265
|
def href_attribute
|
271
266
|
attributes['xlink:href'] || attributes['href']
|
272
267
|
end
|
268
|
+
|
269
|
+
def overflow_hidden?
|
270
|
+
['hidden', 'scroll'].include?(computed_properties.overflow)
|
271
|
+
end
|
272
|
+
|
273
|
+
def clone_element_source(source)
|
274
|
+
new_source = source.dup
|
275
|
+
document.element_styles[new_source] = document.element_styles[source]
|
276
|
+
new_source
|
277
|
+
end
|
273
278
|
end
|
@@ -4,8 +4,8 @@ class Prawn::SVG::Elements::Circle < Prawn::SVG::Elements::Base
|
|
4
4
|
def parse
|
5
5
|
require_attributes 'r'
|
6
6
|
|
7
|
-
@x = x(attributes['cx'] ||
|
8
|
-
@y = y(attributes['cy'] ||
|
7
|
+
@x = x(attributes['cx'] || '0')
|
8
|
+
@y = y(attributes['cy'] || '0')
|
9
9
|
@r = pixels(attributes['r'])
|
10
10
|
|
11
11
|
require_positive_value @r
|
@@ -13,9 +13,9 @@ class Prawn::SVG::Elements::Circle < Prawn::SVG::Elements::Base
|
|
13
13
|
|
14
14
|
def apply
|
15
15
|
if USE_NEW_CIRCLE_CALL
|
16
|
-
add_call
|
16
|
+
add_call 'circle', [@x, @y], @r
|
17
17
|
else
|
18
|
-
add_call
|
18
|
+
add_call 'circle_at', [@x, @y], radius: @r
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -13,7 +13,7 @@ class Prawn::SVG::Elements::DepthFirstBase < Prawn::SVG::Elements::Base
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def parse_and_apply
|
16
|
-
raise
|
16
|
+
raise 'unsupported'
|
17
17
|
end
|
18
18
|
|
19
19
|
protected
|
@@ -36,11 +36,11 @@ class Prawn::SVG::Elements::DepthFirstBase < Prawn::SVG::Elements::Base
|
|
36
36
|
return unless source
|
37
37
|
|
38
38
|
svg_child_elements.each do |elem|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
next unless (element_class = Prawn::SVG::Elements::TAG_CLASS_MAPPING[elem.name.to_sym])
|
40
|
+
|
41
|
+
child = element_class.new(@document, elem, @calls, state.dup)
|
42
|
+
child.parse_step
|
43
|
+
@children << child
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
@@ -4,8 +4,8 @@ class Prawn::SVG::Elements::Ellipse < Prawn::SVG::Elements::Base
|
|
4
4
|
def parse
|
5
5
|
require_attributes 'rx', 'ry'
|
6
6
|
|
7
|
-
@x = x(attributes['cx'] ||
|
8
|
-
@y = y(attributes['cy'] ||
|
7
|
+
@x = x(attributes['cx'] || '0')
|
8
|
+
@y = y(attributes['cy'] || '0')
|
9
9
|
@rx = x_pixels(attributes['rx'])
|
10
10
|
@ry = y_pixels(attributes['ry'])
|
11
11
|
|
@@ -13,11 +13,10 @@ class Prawn::SVG::Elements::Ellipse < Prawn::SVG::Elements::Base
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def apply
|
16
|
-
add_call USE_NEW_ELLIPSE_CALL ?
|
16
|
+
add_call USE_NEW_ELLIPSE_CALL ? 'ellipse' : 'ellipse_at', [@x, @y], @rx, @ry
|
17
17
|
end
|
18
18
|
|
19
19
|
def bounding_box
|
20
20
|
[@x - @rx, @y + @ry, @x + @rx, @y - @ry]
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|