prawn-svg 0.27.1 → 0.28.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -2
- data/lib/prawn-svg.rb +3 -1
- data/lib/prawn/svg/{css.rb → css/font_family_parser.rb} +3 -4
- data/lib/prawn/svg/css/selector_parser.rb +174 -0
- data/lib/prawn/svg/css/stylesheets.rb +125 -0
- data/lib/prawn/svg/document.rb +3 -15
- data/lib/prawn/svg/elements.rb +2 -2
- data/lib/prawn/svg/elements/base.rb +20 -23
- data/lib/prawn/svg/elements/clip_path.rb +12 -0
- data/lib/prawn/svg/elements/container.rb +1 -3
- data/lib/prawn/svg/elements/text.rb +1 -1
- data/lib/prawn/svg/elements/text_component.rb +46 -13
- data/lib/prawn/svg/font_registry.rb +4 -3
- data/lib/prawn/svg/interface.rb +3 -1
- data/lib/prawn/svg/properties.rb +1 -0
- data/lib/prawn/svg/state.rb +6 -3
- data/lib/prawn/svg/version.rb +1 -1
- data/prawn-svg.gemspec +2 -2
- data/spec/prawn/svg/{css_spec.rb → css/font_family_parser_spec.rb} +3 -3
- data/spec/prawn/svg/css/selector_parser_spec.rb +33 -0
- data/spec/prawn/svg/css/stylesheets_spec.rb +113 -0
- data/spec/prawn/svg/document_spec.rb +0 -33
- data/spec/prawn/svg/elements/base_spec.rb +56 -0
- data/spec/prawn/svg/elements/text_spec.rb +59 -16
- data/spec/prawn/svg/font_registry_spec.rb +30 -0
- data/spec/prawn/svg/interface_spec.rb +33 -1
- data/spec/sample_output/{directory → .keep} +0 -0
- data/spec/sample_svg/text_stroke.svg +41 -0
- data/spec/sample_svg/warning-radioactive.svg +98 -0
- data/spec/spec_helper.rb +2 -2
- metadata +108 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f5f1b932d2ff278ac37b5957a18b57d51013345
|
4
|
+
data.tar.gz: 7502fc93ebe947b1955d478292bf71c7b5dd40c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 770335f6cd4fbff5a30d6d6ff965ff2148e66543342de66a320583f4f24ec040d341054749c619c7a9ef1491e1e0155158ba63ea407b440406791dec60fc57f2
|
7
|
+
data.tar.gz: 8e1d9b9a00984309065c1ffc93b7695aafb97d4fbc91c3b6b76d7cc9ee4fe106365b23672f7a87fe0edc97e2612a7d7d3adfe557d5aaa1af4a0c90fb15652717
|
data/README.md
CHANGED
@@ -81,7 +81,7 @@ prawn-svg supports most but not all of the full SVG 1.1 specification. It curre
|
|
81
81
|
- `<switch>` and `<foreignObject>`, although prawn-svg cannot handle any data that is not SVG so `<foreignObject>`
|
82
82
|
tags are always ignored.
|
83
83
|
|
84
|
-
- properties: `clip-path`, `color`, `display`, `fill-opacity`, `fill`, `opacity`, `overflow`, `stroke`, `stroke-dasharray`, `stroke-linecap`, `stroke-opacity`, `stroke-width`
|
84
|
+
- properties: `clip-path`, `color`, `display`, `fill`, `fill-opacity`, `fill-rule`, `opacity`, `overflow`, `stroke`, `stroke-dasharray`, `stroke-linecap`, `stroke-opacity`, `stroke-width`
|
85
85
|
|
86
86
|
- properties on lines, polylines, polygons and paths: `marker-end`, `marker-mid`, `marker-start`
|
87
87
|
|
@@ -101,12 +101,21 @@ prawn-svg supports most but not all of the full SVG 1.1 specification. It curre
|
|
101
101
|
|
102
102
|
## CSS
|
103
103
|
|
104
|
-
prawn-svg
|
104
|
+
prawn-svg supports CSS, both in `<style>` blocks and `style` attributes.
|
105
|
+
|
106
|
+
In CSS selectors you can use element names, IDs, classes, attributes (existence, `=`, `^=`, `$=`, `*=`, `~=`, `|=`)
|
107
|
+
and all combinators (` `, `>`, `+`, `~`).
|
108
|
+
The pseudo-classes `:first-child`, `:last-child` and `:nth-child(n)` (where n is a number) also work.
|
109
|
+
|
110
|
+
Pseudo-elements and the other pseudo-classes are not supported. Specificity ordering is
|
111
|
+
implemented, but `!important` is not.
|
105
112
|
|
106
113
|
## Not supported
|
107
114
|
|
108
115
|
prawn-svg does not support radial gradients, patterns or filters.
|
109
116
|
|
117
|
+
It does not support text in the clip area, but you can clip shapes and text by any shape.
|
118
|
+
|
110
119
|
## Configuration
|
111
120
|
|
112
121
|
### Fonts
|
data/lib/prawn-svg.rb
CHANGED
@@ -21,7 +21,9 @@ require 'prawn/svg/pathable'
|
|
21
21
|
require 'prawn/svg/elements'
|
22
22
|
require 'prawn/svg/extension'
|
23
23
|
require 'prawn/svg/interface'
|
24
|
-
require 'prawn/svg/css'
|
24
|
+
require 'prawn/svg/css/font_family_parser'
|
25
|
+
require 'prawn/svg/css/selector_parser'
|
26
|
+
require 'prawn/svg/css/stylesheets'
|
25
27
|
require 'prawn/svg/ttf'
|
26
28
|
require 'prawn/svg/font'
|
27
29
|
require 'prawn/svg/document'
|
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
|
-
class
|
3
|
-
def
|
1
|
+
module Prawn::SVG::CSS
|
2
|
+
class FontFamilyParser
|
3
|
+
def self.parse(string)
|
4
4
|
in_quote = nil
|
5
5
|
in_escape = false
|
6
6
|
current = nil
|
@@ -37,4 +37,3 @@ class Prawn::SVG::CSS
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
40
|
-
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module Prawn::SVG::CSS
|
2
|
+
class SelectorParser
|
3
|
+
def self.parse(selector)
|
4
|
+
tokens = tokenise_css_selector(selector) or return
|
5
|
+
|
6
|
+
result = [{}]
|
7
|
+
part = nil
|
8
|
+
|
9
|
+
tokens.each do |token|
|
10
|
+
case token
|
11
|
+
when Modifier
|
12
|
+
part = token.type
|
13
|
+
result.last[part] ||= part == :name ? "" : []
|
14
|
+
when Identifier
|
15
|
+
return unless part
|
16
|
+
result.last[part] << token.name
|
17
|
+
when Attribute
|
18
|
+
return unless ["=", "*=", "~=", "^=", "|=", "$=", nil].include?(token.operator)
|
19
|
+
(result.last[:attribute] ||= []) << [token.key, token.operator, token.value]
|
20
|
+
when Combinator
|
21
|
+
result << {combinator: token.type}
|
22
|
+
part = nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
result
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
VALID_CSS_IDENTIFIER_CHAR = /[a-zA-Z0-9_\u00a0-\uffff-]/
|
32
|
+
Identifier = Struct.new(:name)
|
33
|
+
Modifier = Struct.new(:type)
|
34
|
+
Combinator = Struct.new(:type)
|
35
|
+
Attribute = Struct.new(:key, :operator, :value)
|
36
|
+
|
37
|
+
def self.tokenise_css_selector(selector)
|
38
|
+
result = []
|
39
|
+
brackets = false
|
40
|
+
attribute = false
|
41
|
+
quote = false
|
42
|
+
|
43
|
+
selector.strip.chars do |char|
|
44
|
+
if brackets
|
45
|
+
result.last.name << char
|
46
|
+
brackets = false if char == ')'
|
47
|
+
elsif attribute
|
48
|
+
case attribute
|
49
|
+
when :pre_key
|
50
|
+
if VALID_CSS_IDENTIFIER_CHAR.match(char)
|
51
|
+
result.last.key = char
|
52
|
+
attribute = :key
|
53
|
+
elsif char != " " && char != "\t"
|
54
|
+
return
|
55
|
+
end
|
56
|
+
|
57
|
+
when :key
|
58
|
+
if VALID_CSS_IDENTIFIER_CHAR.match(char)
|
59
|
+
result.last.key << char
|
60
|
+
elsif char == "]"
|
61
|
+
attribute = nil
|
62
|
+
elsif "=*~^|$".include?(char)
|
63
|
+
result.last.operator = char
|
64
|
+
attribute = :operator
|
65
|
+
elsif char == " " || char == "\t"
|
66
|
+
attribute = :pre_operator
|
67
|
+
else
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
when :pre_operator
|
72
|
+
if "=*~^|$".include?(char)
|
73
|
+
result.last.operator = char
|
74
|
+
attribute = :operator
|
75
|
+
elsif char != " " && char != "\t"
|
76
|
+
return
|
77
|
+
end
|
78
|
+
|
79
|
+
when :operator
|
80
|
+
if "=*~^|$".include?(char)
|
81
|
+
result.last.operator << char
|
82
|
+
elsif char == " " || char == "\t"
|
83
|
+
attribute = :pre_value
|
84
|
+
elsif char == '"' || char == "'"
|
85
|
+
result.last.value = ''
|
86
|
+
attribute = char
|
87
|
+
else
|
88
|
+
result.last.value = char
|
89
|
+
attribute = :value
|
90
|
+
end
|
91
|
+
|
92
|
+
when :pre_value
|
93
|
+
if char == '"' || char == "'"
|
94
|
+
result.last.value = ''
|
95
|
+
attribute = char
|
96
|
+
elsif char != " " && char != "\t"
|
97
|
+
result.last.value = char
|
98
|
+
attribute = :value
|
99
|
+
end
|
100
|
+
|
101
|
+
when :value
|
102
|
+
if char == "]"
|
103
|
+
result.last.value = result.last.value.rstrip
|
104
|
+
attribute = nil
|
105
|
+
else
|
106
|
+
result.last.value << char
|
107
|
+
end
|
108
|
+
|
109
|
+
when '"', "'"
|
110
|
+
if char == "\\" && !quote
|
111
|
+
quote = true
|
112
|
+
elsif char == attribute && !quote
|
113
|
+
attribute = :post_string
|
114
|
+
else
|
115
|
+
quote = false
|
116
|
+
result.last.value << char
|
117
|
+
end
|
118
|
+
|
119
|
+
when :post_string
|
120
|
+
if char == "]"
|
121
|
+
attribute = nil
|
122
|
+
elsif char != " " && char != "\t"
|
123
|
+
return
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
elsif VALID_CSS_IDENTIFIER_CHAR.match(char)
|
128
|
+
case result.last
|
129
|
+
when Identifier
|
130
|
+
result.last.name << char
|
131
|
+
else
|
132
|
+
result << Modifier.new(:name) if !result.last.is_a?(Modifier)
|
133
|
+
result << Identifier.new(char)
|
134
|
+
end
|
135
|
+
else
|
136
|
+
case char
|
137
|
+
when "."
|
138
|
+
result << Modifier.new(:class)
|
139
|
+
when "#"
|
140
|
+
result << Modifier.new(:id)
|
141
|
+
when ":"
|
142
|
+
result << Modifier.new(:pseudo_class)
|
143
|
+
when " ", "\t"
|
144
|
+
result << Combinator.new(:descendant) unless result.last.is_a?(Combinator)
|
145
|
+
when ">"
|
146
|
+
result.pop if result.last == Combinator.new(:descendant)
|
147
|
+
result << Combinator.new(:child)
|
148
|
+
when "+"
|
149
|
+
result.pop if result.last == Combinator.new(:descendant)
|
150
|
+
result << Combinator.new(:adjacent)
|
151
|
+
when "~"
|
152
|
+
result.pop if result.last == Combinator.new(:descendant)
|
153
|
+
result << Combinator.new(:siblings)
|
154
|
+
when "*"
|
155
|
+
return unless result.empty? || result.last.is_a?(Combinator)
|
156
|
+
result << Modifier.new(:name)
|
157
|
+
result << Identifier.new("*")
|
158
|
+
when "(" # e.g. :nth-child(3n+4)
|
159
|
+
return unless result.last.is_a?(Identifier) && result[-2] && result[-2].is_a?(Modifier) && result[-2].type == :pseudo_class
|
160
|
+
result.last.name << "("
|
161
|
+
brackets = true
|
162
|
+
when "["
|
163
|
+
result << Attribute.new
|
164
|
+
attribute = :pre_key
|
165
|
+
else
|
166
|
+
return # unsupported Combinator
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
result unless brackets || attribute
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module Prawn::SVG::CSS
|
2
|
+
class Stylesheets
|
3
|
+
attr_reader :css_parser, :root, :media
|
4
|
+
|
5
|
+
def initialize(css_parser, root, media = :all)
|
6
|
+
@css_parser = css_parser
|
7
|
+
@root = root
|
8
|
+
@media = media
|
9
|
+
end
|
10
|
+
|
11
|
+
def load
|
12
|
+
load_style_elements
|
13
|
+
xpath_styles = gather_xpath_styles
|
14
|
+
associate_xpath_styles_with_elements(xpath_styles)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def load_style_elements
|
20
|
+
REXML::XPath.match(root, '//style').each do |source|
|
21
|
+
data = source.texts.map(&:value).join
|
22
|
+
css_parser.add_block!(data)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def gather_xpath_styles
|
27
|
+
xpath_styles = []
|
28
|
+
order = 0
|
29
|
+
|
30
|
+
css_parser.each_rule_set(media) do |rule_set, _|
|
31
|
+
declarations = []
|
32
|
+
rule_set.each_declaration { |*data| declarations << data }
|
33
|
+
|
34
|
+
rule_set.selectors.each do |selector_text|
|
35
|
+
selector = Prawn::SVG::CSS::SelectorParser.parse(selector_text)
|
36
|
+
xpath = css_selector_to_xpath(selector)
|
37
|
+
specificity = calculate_specificity(selector)
|
38
|
+
specificity << order
|
39
|
+
order += 1
|
40
|
+
|
41
|
+
xpath_styles << [xpath, declarations, specificity]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
xpath_styles.sort_by(&:last)
|
46
|
+
end
|
47
|
+
|
48
|
+
def associate_xpath_styles_with_elements(xpath_styles)
|
49
|
+
element_styles = {}
|
50
|
+
|
51
|
+
xpath_styles.each do |xpath, declarations, _|
|
52
|
+
REXML::XPath.match(root, xpath).each do |element|
|
53
|
+
(element_styles[element] ||= []).concat declarations
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
element_styles
|
58
|
+
end
|
59
|
+
|
60
|
+
def xpath_quote(value)
|
61
|
+
%{"#{value.gsub('\\', '\\\\').gsub('"', '\\"')}"} if value
|
62
|
+
end
|
63
|
+
|
64
|
+
def css_selector_to_xpath(selector)
|
65
|
+
selector.map do |element|
|
66
|
+
pseudo_classes = Set.new(element[:pseudo_class])
|
67
|
+
|
68
|
+
result = case element[:combinator]
|
69
|
+
when :child
|
70
|
+
'/'
|
71
|
+
when :adjacent
|
72
|
+
pseudo_classes << 'first-child'
|
73
|
+
'/following-sibling::'
|
74
|
+
when :siblings
|
75
|
+
'/following-sibling::'
|
76
|
+
else
|
77
|
+
'//'
|
78
|
+
end
|
79
|
+
|
80
|
+
result << element[:name] if element[:name]
|
81
|
+
result << ((element[:class] || []).map { |name| "[contains(concat(' ',@class,' '), ' #{name} ')]" }.join)
|
82
|
+
result << ((element[:id] || []).map { |name| "[@id='#{name}']" }.join)
|
83
|
+
|
84
|
+
(element[:attribute] || []).each do |key, operator, value|
|
85
|
+
case operator
|
86
|
+
when nil
|
87
|
+
result << "[@#{key}]"
|
88
|
+
when "="
|
89
|
+
result << "[@#{key}=#{xpath_quote value}]"
|
90
|
+
when "^="
|
91
|
+
result << "[starts-with(@#{key}, #{xpath_quote value})]"
|
92
|
+
when "$="
|
93
|
+
result << "[substring(@#{key}, string-length(@#{key}) - #{value.length - 1}) = #{xpath_quote value})]"
|
94
|
+
when "*="
|
95
|
+
result << "[contains(@#{key}, #{xpath_quote value})]"
|
96
|
+
when "~="
|
97
|
+
result << "[contains(concat(' ',@#{key},' '), #{xpath_quote " #{value} "})]"
|
98
|
+
when "|="
|
99
|
+
result << "[contains(concat('-',@#{key},'-'), #{xpath_quote "-#{value}-"})]"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
pseudo_classes.each do |pc|
|
104
|
+
case pc
|
105
|
+
when "first-child" then result << "[1]"
|
106
|
+
when "last-child" then result << "[last()]"
|
107
|
+
when /^nth-child\((\d+)\)$/ then result << "[#{$1}]"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
result
|
112
|
+
end.join
|
113
|
+
end
|
114
|
+
|
115
|
+
def calculate_specificity(selector)
|
116
|
+
selector.reduce([0, 0, 0]) do |(a, b, c), element|
|
117
|
+
[
|
118
|
+
a + (element[:id] || []).length,
|
119
|
+
b + (element[:class] || []).length + (element[:attribute] || []).length + (element[:pseudo_class] || []).length,
|
120
|
+
c + (element[:name] && element[:name] != "*" ? 1 : 0)
|
121
|
+
]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/prawn/svg/document.rb
CHANGED
@@ -12,7 +12,8 @@ class Prawn::SVG::Document
|
|
12
12
|
:fallback_font_name,
|
13
13
|
:font_registry,
|
14
14
|
:url_loader,
|
15
|
-
:
|
15
|
+
:elements_by_id, :gradients,
|
16
|
+
:element_styles
|
16
17
|
|
17
18
|
def initialize(data, bounds, options, font_registry: nil, css_parser: CssParser::Parser.new)
|
18
19
|
@root = REXML::Document.new(data).root
|
@@ -31,7 +32,6 @@ class Prawn::SVG::Document
|
|
31
32
|
@gradients = {}
|
32
33
|
@fallback_font_name = options.fetch(:fallback_font_name, DEFAULT_FALLBACK_FONT_NAME)
|
33
34
|
@font_registry = font_registry
|
34
|
-
@css_parser = css_parser
|
35
35
|
|
36
36
|
@url_loader = Prawn::SVG::UrlLoader.new(
|
37
37
|
enable_cache: options[:cache_images],
|
@@ -42,7 +42,7 @@ class Prawn::SVG::Document
|
|
42
42
|
@sizing = Prawn::SVG::Calculators::DocumentSizing.new(bounds, @root.attributes)
|
43
43
|
calculate_sizing(requested_width: options[:width], requested_height: options[:height])
|
44
44
|
|
45
|
-
|
45
|
+
@element_styles = Prawn::SVG::CSS::Stylesheets.new(css_parser, root).load
|
46
46
|
|
47
47
|
yield self if block_given?
|
48
48
|
end
|
@@ -52,16 +52,4 @@ class Prawn::SVG::Document
|
|
52
52
|
sizing.requested_height = requested_height
|
53
53
|
sizing.calculate
|
54
54
|
end
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
# <style> elements specified anywhere in the document apply to the entire
|
59
|
-
# document. Because of this, we load all <style> elements before parsing
|
60
|
-
# the rest of the document.
|
61
|
-
def parse_style_elements
|
62
|
-
REXML::XPath.match(root, '//style').each do |source|
|
63
|
-
data = source.texts.map(&:value).join
|
64
|
-
css_parser.add_block!(data)
|
65
|
-
end
|
66
|
-
end
|
67
55
|
end
|
data/lib/prawn/svg/elements.rb
CHANGED
@@ -4,7 +4,7 @@ end
|
|
4
4
|
|
5
5
|
require 'prawn/svg/elements/call_duplicator'
|
6
6
|
|
7
|
-
%w(base depth_first_base root container viewport text text_component line polyline polygon circle ellipse rect path use image gradient marker ignored).each do |filename|
|
7
|
+
%w(base depth_first_base root container clip_path viewport text text_component line polyline polygon circle ellipse rect path use image gradient marker ignored).each do |filename|
|
8
8
|
require "prawn/svg/elements/#{filename}"
|
9
9
|
end
|
10
10
|
|
@@ -13,7 +13,7 @@ module Prawn::SVG::Elements
|
|
13
13
|
g: Prawn::SVG::Elements::Container,
|
14
14
|
symbol: Prawn::SVG::Elements::Container,
|
15
15
|
defs: Prawn::SVG::Elements::Container,
|
16
|
-
clipPath: Prawn::SVG::Elements::
|
16
|
+
clipPath: Prawn::SVG::Elements::ClipPath,
|
17
17
|
switch: Prawn::SVG::Elements::Container,
|
18
18
|
svg: Prawn::SVG::Elements::Viewport,
|
19
19
|
text: Prawn::SVG::Elements::Text,
|
@@ -157,14 +157,23 @@ class Prawn::SVG::Elements::Base
|
|
157
157
|
end
|
158
158
|
|
159
159
|
def apply_drawing_call
|
160
|
-
if
|
161
|
-
draw_types = PAINT_TYPES.select { |property| computed_properties.send(property) != 'none' }
|
160
|
+
return if state.disable_drawing || !drawable?
|
162
161
|
|
163
|
-
|
164
|
-
|
162
|
+
fill = computed_properties.fill != 'none'
|
163
|
+
stroke = computed_properties.stroke != 'none'
|
164
|
+
|
165
|
+
if fill
|
166
|
+
command = stroke ? 'fill_and_stroke' : 'fill'
|
167
|
+
|
168
|
+
if computed_properties.fill_rule == 'evenodd'
|
169
|
+
add_call_and_enter(command, {fill_rule: :even_odd})
|
165
170
|
else
|
166
|
-
add_call_and_enter(
|
171
|
+
add_call_and_enter(command)
|
167
172
|
end
|
173
|
+
elsif stroke
|
174
|
+
add_call_and_enter('stroke')
|
175
|
+
else
|
176
|
+
add_call_and_enter('end_path')
|
168
177
|
end
|
169
178
|
end
|
170
179
|
|
@@ -207,32 +216,20 @@ class Prawn::SVG::Elements::Base
|
|
207
216
|
end
|
208
217
|
|
209
218
|
def extract_attributes_and_properties
|
210
|
-
if
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
if classes = source.attributes["class"]
|
215
|
-
class_styles = classes.split(' ').collect do |class_name|
|
216
|
-
@document.css_parser.find_by_selector(".#{class_name}")
|
217
|
-
end
|
219
|
+
if styles = document.element_styles[source]
|
220
|
+
# TODO : implement !important, at the moment it's just ignored
|
221
|
+
styles.each do |name, value, _important|
|
222
|
+
@properties.set(name, value)
|
218
223
|
end
|
219
|
-
|
220
|
-
element_style = source.attributes['style']
|
221
|
-
|
222
|
-
style = [tag_style, class_styles, id_style, element_style].flatten.collect do |s|
|
223
|
-
s.nil? || s.strip == "" ? "" : "#{s}#{";" unless s.match(/;\s*\z/)}"
|
224
|
-
end.join
|
225
|
-
else
|
226
|
-
style = source.attributes['style'] || ""
|
227
224
|
end
|
228
225
|
|
226
|
+
@properties.load_hash(parse_css_declarations(source.attributes['style'] || ''))
|
227
|
+
|
229
228
|
source.attributes.each do |name, value|
|
230
229
|
# Properties#set returns nil if it's not a recognised property name
|
231
230
|
@properties.set(name, value) or @attributes[name] = value
|
232
231
|
end
|
233
232
|
|
234
|
-
@properties.load_hash(parse_css_declarations(style))
|
235
|
-
|
236
233
|
state.computed_properties.compute_properties(@properties)
|
237
234
|
end
|
238
235
|
|