prawn-svg 0.20.0 → 0.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/prawn/svg/color.rb +161 -6
- data/lib/prawn/svg/element.rb +23 -9
- data/lib/prawn/svg/parser/text.rb +2 -1
- data/lib/prawn/svg/version.rb +1 -1
- data/spec/prawn/svg/color_spec.rb +14 -0
- data/spec/prawn/svg/element_spec.rb +97 -6
- data/spec/prawn/svg/parser/text_spec.rb +9 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40c0c884ffef68848fa7314e085647ffc907a003
|
4
|
+
data.tar.gz: 87341061bdfcb99e9cf8157cfb3c84f72ec3bb2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 002745c7063d0013609343b4e383372058cde46835e240496f813855ea489287d6aad117ab98d89c3367e02d64a502deb038cdf00a7d9d381238e9d02f66149f
|
7
|
+
data.tar.gz: ce3f448a22be98027ad0be5e154924839e080969cfeeb27322c57e017d249c767b4a073b2faed164a290d102cda0e18c80aa9665840385a6cbfc124714f76808
|
data/lib/prawn/svg/color.rb
CHANGED
@@ -1,17 +1,164 @@
|
|
1
1
|
class Prawn::Svg::Color
|
2
|
-
|
2
|
+
UnresolvableURLWithNoFallbackError = Class.new(StandardError)
|
3
|
+
|
3
4
|
HTML_COLORS = {
|
4
|
-
'
|
5
|
-
'
|
6
|
-
'
|
7
|
-
'
|
5
|
+
'aliceblue' => 'f0f8ff',
|
6
|
+
'antiquewhite' => 'faebd7',
|
7
|
+
'aqua' => '00ffff',
|
8
|
+
'aquamarine' => '7fffd4',
|
9
|
+
'azure' => 'f0ffff',
|
10
|
+
'beige' => 'f5f5dc',
|
11
|
+
'bisque' => 'ffe4c4',
|
12
|
+
'black' => '000000',
|
13
|
+
'blanchedalmond' => 'ffebcd',
|
14
|
+
'blue' => '0000ff',
|
15
|
+
'blueviolet' => '8a2be2',
|
16
|
+
'brown' => 'a52a2a',
|
17
|
+
'burlywood' => 'deb887',
|
18
|
+
'cadetblue' => '5f9ea0',
|
19
|
+
'chartreuse' => '7fff00',
|
20
|
+
'chocolate' => 'd2691e',
|
21
|
+
'coral' => 'ff7f50',
|
22
|
+
'cornflowerblue' => '6495ed',
|
23
|
+
'cornsilk' => 'fff8dc',
|
24
|
+
'crimson' => 'dc143c',
|
25
|
+
'cyan' => '00ffff',
|
26
|
+
'darkblue' => '00008b',
|
27
|
+
'darkcyan' => '008b8b',
|
28
|
+
'darkgoldenrod' => 'b8860b',
|
29
|
+
'darkgray' => 'a9a9a9',
|
30
|
+
'darkgreen' => '006400',
|
31
|
+
'darkgrey' => 'a9a9a9',
|
32
|
+
'darkkhaki' => 'bdb76b',
|
33
|
+
'darkmagenta' => '8b008b',
|
34
|
+
'darkolivegreen' => '556b2f',
|
35
|
+
'darkorange' => 'ff8c00',
|
36
|
+
'darkorchid' => '9932cc',
|
37
|
+
'darkred' => '8b0000',
|
38
|
+
'darksalmon' => 'e9967a',
|
39
|
+
'darkseagreen' => '8fbc8f',
|
40
|
+
'darkslateblue' => '483d8b',
|
41
|
+
'darkslategray' => '2f4f4f',
|
42
|
+
'darkslategrey' => '2f4f4f',
|
43
|
+
'darkturquoise' => '00ced1',
|
44
|
+
'darkviolet' => '9400d3',
|
45
|
+
'deeppink' => 'ff1493',
|
46
|
+
'deepskyblue' => '00bfff',
|
47
|
+
'dimgray' => '696969',
|
48
|
+
'dimgrey' => '696969',
|
49
|
+
'dodgerblue' => '1e90ff',
|
50
|
+
'firebrick' => 'b22222',
|
51
|
+
'floralwhite' => 'fffaf0',
|
52
|
+
'forestgreen' => '228b22',
|
53
|
+
'fuchsia' => 'ff00ff',
|
54
|
+
'gainsboro' => 'dcdcdc',
|
55
|
+
'ghostwhite' => 'f8f8ff',
|
56
|
+
'gold' => 'ffd700',
|
57
|
+
'goldenrod' => 'daa520',
|
58
|
+
'gray' => '808080',
|
59
|
+
'grey' => '808080',
|
60
|
+
'green' => '008000',
|
61
|
+
'greenyellow' => 'adff2f',
|
62
|
+
'honeydew' => 'f0fff0',
|
63
|
+
'hotpink' => 'ff69b4',
|
64
|
+
'indianred' => 'cd5c5c',
|
65
|
+
'indigo' => '4b0082',
|
66
|
+
'ivory' => 'fffff0',
|
67
|
+
'khaki' => 'f0e68c',
|
68
|
+
'lavender' => 'e6e6fa',
|
69
|
+
'lavenderblush' => 'fff0f5',
|
70
|
+
'lawngreen' => '7cfc00',
|
71
|
+
'lemonchiffon' => 'fffacd',
|
72
|
+
'lightblue' => 'add8e6',
|
73
|
+
'lightcoral' => 'f08080',
|
74
|
+
'lightcyan' => 'e0ffff',
|
75
|
+
'lightgoldenrodyellow' => 'fafad2',
|
76
|
+
'lightgray' => 'd3d3d3',
|
77
|
+
'lightgreen' => '90ee90',
|
78
|
+
'lightgrey' => 'd3d3d3',
|
79
|
+
'lightpink' => 'ffb6c1',
|
80
|
+
'lightsalmon' => 'ffa07a',
|
81
|
+
'lightseagreen' => '20b2aa',
|
82
|
+
'lightskyblue' => '87cefa',
|
83
|
+
'lightslategray' => '778899',
|
84
|
+
'lightslategrey' => '778899',
|
85
|
+
'lightsteelblue' => 'b0c4de',
|
86
|
+
'lightyellow' => 'ffffe0',
|
87
|
+
'lime' => '00ff00',
|
88
|
+
'limegreen' => '32cd32',
|
89
|
+
'linen' => 'faf0e6',
|
90
|
+
'magenta' => 'ff00ff',
|
91
|
+
'maroon' => '800000',
|
92
|
+
'mediumaquamarine' => '66cdaa',
|
93
|
+
'mediumblue' => '0000cd',
|
94
|
+
'mediumorchid' => 'ba55d3',
|
95
|
+
'mediumpurple' => '9370db',
|
96
|
+
'mediumseagreen' => '3cb371',
|
97
|
+
'mediumslateblue' => '7b68ee',
|
98
|
+
'mediumspringgreen' => '00fa9a',
|
99
|
+
'mediumturquoise' => '48d1cc',
|
100
|
+
'mediumvioletred' => 'c71585',
|
101
|
+
'midnightblue' => '191970',
|
102
|
+
'mintcream' => 'f5fffa',
|
103
|
+
'mistyrose' => 'ffe4e1',
|
104
|
+
'moccasin' => 'ffe4b5',
|
105
|
+
'navajowhite' => 'ffdead',
|
106
|
+
'navy' => '000080',
|
107
|
+
'oldlace' => 'fdf5e6',
|
108
|
+
'olive' => '808000',
|
109
|
+
'olivedrab' => '6b8e23',
|
110
|
+
'orange' => 'ffa500',
|
111
|
+
'orangered' => 'ff4500',
|
112
|
+
'orchid' => 'da70d6',
|
113
|
+
'palegoldenrod' => 'eee8aa',
|
114
|
+
'palegreen' => '98fb98',
|
115
|
+
'paleturquoise' => 'afeeee',
|
116
|
+
'palevioletred' => 'db7093',
|
117
|
+
'papayawhip' => 'ffefd5',
|
118
|
+
'peachpuff' => 'ffdab9',
|
119
|
+
'peru' => 'cd853f',
|
120
|
+
'pink' => 'ffc0cb',
|
121
|
+
'plum' => 'dda0dd',
|
122
|
+
'powderblue' => 'b0e0e6',
|
123
|
+
'purple' => '800080',
|
124
|
+
'red' => 'ff0000',
|
125
|
+
'rosybrown' => 'bc8f8f',
|
126
|
+
'royalblue' => '4169e1',
|
127
|
+
'saddlebrown' => '8b4513',
|
128
|
+
'salmon' => 'fa8072',
|
129
|
+
'sandybrown' => 'f4a460',
|
130
|
+
'seagreen' => '2e8b57',
|
131
|
+
'seashell' => 'fff5ee',
|
132
|
+
'sienna' => 'a0522d',
|
133
|
+
'silver' => 'c0c0c0',
|
134
|
+
'skyblue' => '87ceeb',
|
135
|
+
'slateblue' => '6a5acd',
|
136
|
+
'slategray' => '708090',
|
137
|
+
'slategrey' => '708090',
|
138
|
+
'snow' => 'fffafa',
|
139
|
+
'springgreen' => '00ff7f',
|
140
|
+
'steelblue' => '4682b4',
|
141
|
+
'tan' => 'd2b48c',
|
142
|
+
'teal' => '008080',
|
143
|
+
'thistle' => 'd8bfd8',
|
144
|
+
'tomato' => 'ff6347',
|
145
|
+
'turquoise' => '40e0d0',
|
146
|
+
'violet' => 'ee82ee',
|
147
|
+
'wheat' => 'f5deb3',
|
148
|
+
'white' => 'ffffff',
|
149
|
+
'whitesmoke' => 'f5f5f5',
|
150
|
+
'yellow' => 'ffff00',
|
151
|
+
'yellowgreen' => '9acd32'
|
8
152
|
}.freeze
|
9
153
|
|
10
154
|
RGB_VALUE_REGEXP = "\s*(-?[0-9.]+%?)\s*"
|
11
155
|
RGB_REGEXP = /\Argb\(#{RGB_VALUE_REGEXP},#{RGB_VALUE_REGEXP},#{RGB_VALUE_REGEXP}\)\z/i
|
156
|
+
URL_REGEXP = /\Aurl\([^)]*\)\z/i
|
12
157
|
|
13
158
|
def self.color_to_hex(color_string)
|
14
|
-
|
159
|
+
url_specified = false
|
160
|
+
|
161
|
+
result = color_string.strip.scan(/([^(\s]+(\([^)]*\))?)/).detect do |color, *_|
|
15
162
|
if m = color.match(/\A#([0-9a-f])([0-9a-f])([0-9a-f])\z/i)
|
16
163
|
break "#{m[1] * 2}#{m[2] * 2}#{m[3] * 2}"
|
17
164
|
elsif color.match(/\A#[0-9a-f]{6}\z/i)
|
@@ -24,8 +171,16 @@ class Prawn::Svg::Color
|
|
24
171
|
value *= 2.55 if m[n][-1..-1] == '%'
|
25
172
|
"%02x" % clamp(value.round, 0, 255)
|
26
173
|
end.join
|
174
|
+
elsif color.match(URL_REGEXP)
|
175
|
+
url_specified = true
|
176
|
+
nil # we can't handle these so we find the next thing that matches
|
27
177
|
end
|
28
178
|
end
|
179
|
+
|
180
|
+
# http://www.w3.org/TR/SVG/painting.html section 11.2
|
181
|
+
raise UnresolvableURLWithNoFallbackError if result.nil? && url_specified
|
182
|
+
|
183
|
+
result
|
29
184
|
end
|
30
185
|
|
31
186
|
protected
|
data/lib/prawn/svg/element.rb
CHANGED
@@ -82,14 +82,17 @@ class Prawn::Svg::Element
|
|
82
82
|
case name
|
83
83
|
when 'translate'
|
84
84
|
x, y = arguments
|
85
|
-
add_call_and_enter name, @document.distance(x, :x), -@document.distance(y, :y)
|
85
|
+
add_call_and_enter name, @document.distance(x.to_f, :x), -@document.distance(y.to_f, :y)
|
86
86
|
|
87
87
|
when 'rotate'
|
88
88
|
r, x, y = arguments.collect {|a| a.to_f}
|
89
|
-
|
89
|
+
case arguments.length
|
90
|
+
when 1
|
91
|
+
add_call_and_enter name, -r, :origin => [0, @document.y('0')]
|
92
|
+
when 3
|
90
93
|
add_call_and_enter name, -r, :origin => [@document.x(x), @document.y(y)]
|
91
94
|
else
|
92
|
-
|
95
|
+
@document.warnings << "transform 'rotate' must have either one or three arguments"
|
93
96
|
end
|
94
97
|
|
95
98
|
when 'scale'
|
@@ -142,13 +145,21 @@ class Prawn::Svg::Element
|
|
142
145
|
|
143
146
|
def parse_fill_and_stroke_attributes_and_call
|
144
147
|
["fill", "stroke"].select do |type|
|
145
|
-
|
146
|
-
|
148
|
+
case keyword = attribute_value_as_keyword(type)
|
149
|
+
when nil
|
150
|
+
when 'inherit'
|
151
|
+
when 'none'
|
147
152
|
state[type.to_sym] = false
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
153
|
+
else
|
154
|
+
color_attribute = keyword == 'currentcolor' ? 'color' : type
|
155
|
+
color = @attributes[color_attribute]
|
156
|
+
|
157
|
+
begin
|
158
|
+
hex = Prawn::Svg::Color.color_to_hex(color)
|
159
|
+
state[type.to_sym] = true
|
160
|
+
add_call "#{type}_color", hex || '000000'
|
161
|
+
rescue Prawn::Svg::Color::UnresolvableURLWithNoFallbackError
|
162
|
+
state[type.to_sym] = false
|
152
163
|
end
|
153
164
|
end
|
154
165
|
|
@@ -206,6 +217,9 @@ class Prawn::Svg::Element
|
|
206
217
|
font_updated = true
|
207
218
|
@state[:font_family] = family
|
208
219
|
end
|
220
|
+
if (anchor = @attributes['text-anchor'])
|
221
|
+
@state[:text_anchor] = anchor
|
222
|
+
end
|
209
223
|
|
210
224
|
if @state[:font_family] && font_updated
|
211
225
|
usable_font_families = [@state[:font_family], document.fallback_font_name]
|
@@ -35,7 +35,8 @@ class Prawn::Svg::Parser::Text
|
|
35
35
|
|
36
36
|
# This is not a prawn option but we can't work out how to render it here -
|
37
37
|
# it's handled by Svg#rewrite_call_arguments
|
38
|
-
if anchor = attrs['text-anchor']
|
38
|
+
if (anchor = attrs['text-anchor'] || element.state[:text_anchor]) &&
|
39
|
+
['start', 'middle', 'end'].include?(anchor)
|
39
40
|
opts[:text_anchor] = anchor
|
40
41
|
end
|
41
42
|
|
data/lib/prawn/svg/version.rb
CHANGED
@@ -22,5 +22,19 @@ describe Prawn::Svg::Color do
|
|
22
22
|
it "scans the string and finds the first colour it can parse" do
|
23
23
|
Prawn::Svg::Color.color_to_hex("function(#someurl, 0) nonexistent rgb( 3 ,4,5 ) white").should == "030405"
|
24
24
|
end
|
25
|
+
|
26
|
+
it "ignores url()s" do
|
27
|
+
expect(Prawn::Svg::Color.color_to_hex("url(#someplace) red")).to eq 'ff0000'
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns nil if the color doesn't exist" do
|
31
|
+
expect(Prawn::Svg::Color.color_to_hex("blurble")).to be nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "raises UnresolvableURLWithNoFallbackError if there's no fallback after a url()" do
|
35
|
+
expect {
|
36
|
+
Prawn::Svg::Color.color_to_hex("url(#someplace)")
|
37
|
+
}.to raise_error(Prawn::Svg::Color::UnresolvableURLWithNoFallbackError)
|
38
|
+
end
|
25
39
|
end
|
26
40
|
end
|
@@ -1,14 +1,16 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
2
|
|
3
3
|
describe Prawn::Svg::Element do
|
4
|
-
|
5
|
-
|
4
|
+
let(:document) { double(css_parser: nil) }
|
5
|
+
let(:e) { double(:attributes => {}, :name => "path") }
|
6
|
+
let(:element) { Prawn::Svg::Element.new(document, e, [], {}) }
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
describe "#parse_font_attributes_and_call" do
|
9
|
+
before do
|
10
|
+
@document = Struct.new(:fallback_font_name, :css_parser, :warnings).new("Courier", nil, [])
|
11
|
+
@element = Prawn::Svg::Element.new(@document, e, [], {})
|
12
|
+
end
|
10
13
|
|
11
|
-
describe :parse_font_attributes_and_call do
|
12
14
|
it "uses a font if it can find it" do
|
13
15
|
@element.should_receive(:add_call_and_enter).with('font', 'Helvetica', :style => :normal)
|
14
16
|
|
@@ -33,4 +35,93 @@ describe Prawn::Svg::Element do
|
|
33
35
|
@document.warnings.length.should == 1
|
34
36
|
end
|
35
37
|
end
|
38
|
+
|
39
|
+
describe "#parse_fill_and_stroke_attributes_and_call" do
|
40
|
+
subject { element.send :parse_fill_and_stroke_attributes_and_call }
|
41
|
+
|
42
|
+
it "doesn't change anything if no fill attribute provided" do
|
43
|
+
subject
|
44
|
+
expect(element.state[:fill]).to be nil
|
45
|
+
end
|
46
|
+
|
47
|
+
it "doesn't change anything if 'inherit' fill attribute provided" do
|
48
|
+
element.attributes['fill'] = 'inherit'
|
49
|
+
subject
|
50
|
+
expect(element.state[:fill]).to be nil
|
51
|
+
end
|
52
|
+
|
53
|
+
it "turns off filling if 'none' fill attribute provided" do
|
54
|
+
element.attributes['fill'] = 'none'
|
55
|
+
subject
|
56
|
+
expect(element.state[:fill]).to be false
|
57
|
+
end
|
58
|
+
|
59
|
+
it "uses the fill attribute's color" do
|
60
|
+
expect(element).to receive(:add_call).with('fill_color', 'ff0000')
|
61
|
+
element.attributes['fill'] = 'red'
|
62
|
+
subject
|
63
|
+
expect(element.state[:fill]).to be true
|
64
|
+
end
|
65
|
+
|
66
|
+
it "uses black if the fill attribute's color is unparseable" do
|
67
|
+
expect(element).to receive(:add_call).with('fill_color', '000000')
|
68
|
+
element.attributes['fill'] = 'blarble'
|
69
|
+
subject
|
70
|
+
expect(element.state[:fill]).to be true
|
71
|
+
end
|
72
|
+
|
73
|
+
it "uses the color attribute if 'currentColor' fill attribute provided" do
|
74
|
+
expect(element).to receive(:add_call).with('fill_color', 'ff0000')
|
75
|
+
element.attributes['fill'] = 'currentColor'
|
76
|
+
element.attributes['color'] = 'red'
|
77
|
+
subject
|
78
|
+
expect(element.state[:fill]).to be true
|
79
|
+
end
|
80
|
+
|
81
|
+
it "turns off filling if UnresolvableURLWithNoFallbackError is raised" do
|
82
|
+
element.attributes['fill'] = 'url()'
|
83
|
+
subject
|
84
|
+
expect(element.state[:fill]).to be false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "#parse_transform_attribute_and_call" do
|
89
|
+
subject { element.send :parse_transform_attribute_and_call }
|
90
|
+
|
91
|
+
describe "translate" do
|
92
|
+
it "handles a missing y argument" do
|
93
|
+
expect(element).to receive(:add_call_and_enter).with('translate', -5.5, 0)
|
94
|
+
expect(document).to receive(:distance).with(-5.5, :x).and_return(-5.5)
|
95
|
+
expect(document).to receive(:distance).with(0.0, :y).and_return(0.0)
|
96
|
+
|
97
|
+
element.attributes['transform'] = 'translate(-5.5)'
|
98
|
+
subject
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "rotate" do
|
103
|
+
it "handles a single angle argument" do
|
104
|
+
expect(element).to receive(:add_call_and_enter).with('rotate', -5.5, :origin => [0, 0])
|
105
|
+
expect(document).to receive(:y).with('0').and_return(0)
|
106
|
+
|
107
|
+
element.attributes['transform'] = 'rotate(5.5)'
|
108
|
+
subject
|
109
|
+
end
|
110
|
+
|
111
|
+
it "handles three arguments" do
|
112
|
+
expect(element).to receive(:add_call_and_enter).with('rotate', -5.5, :origin => [1.0, 2.0])
|
113
|
+
expect(document).to receive(:x).with(1.0).and_return(1.0)
|
114
|
+
expect(document).to receive(:y).with(2.0).and_return(2.0)
|
115
|
+
|
116
|
+
element.attributes['transform'] = 'rotate(5.5 1 2)'
|
117
|
+
subject
|
118
|
+
end
|
119
|
+
|
120
|
+
it "does nothing and warns if two arguments" do
|
121
|
+
expect(document).to receive(:warnings).and_return([])
|
122
|
+
element.attributes['transform'] = 'rotate(5.5 1)'
|
123
|
+
subject
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
36
127
|
end
|
@@ -33,6 +33,15 @@ describe Prawn::Svg::Parser::Text do
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
describe "when text-anchor is specified" do
|
37
|
+
let(:svg) { '<g text-anchor="middle" font-family="sans-serif" font-size="12"><text x="50" y="14">Text</text></g>' }
|
38
|
+
|
39
|
+
it "should inherit text-anchor from parent element" do
|
40
|
+
parser.parse(element)
|
41
|
+
expect(element.state[:text_anchor]).to eq 'middle'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
36
45
|
describe "letter-spacing" do
|
37
46
|
let(:svg) { '<text letter-spacing="5">spaced</text>' }
|
38
47
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prawn-svg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.21.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roger Nesbitt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prawn
|