prawn-svg 0.20.0 → 0.21.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb790776a1eaf53a702045a42ea22fce3e8afa51
4
- data.tar.gz: d90d2245d141bc749ec04d0b6e9239a9e2d8b48c
3
+ metadata.gz: 40c0c884ffef68848fa7314e085647ffc907a003
4
+ data.tar.gz: 87341061bdfcb99e9cf8157cfb3c84f72ec3bb2b
5
5
  SHA512:
6
- metadata.gz: a0a11f3a0a93c36d838ed2bcb012cfa010f4968de481795e8f46fde61460c71c1f491586e477c24d1c55792f9293d2d52f4dfb9f28c7447cd4c3f87623e17c7f
7
- data.tar.gz: 5885cb0acea63c8edd59fe563e4b6859f0d536537324026e8e18bb390fe1a0e335be7c2eaf07ef1d529ea752804a4fa8387a0f61d3b7ea362f76df60b13f0d5c
6
+ metadata.gz: 002745c7063d0013609343b4e383372058cde46835e240496f813855ea489287d6aad117ab98d89c3367e02d64a502deb038cdf00a7d9d381238e9d02f66149f
7
+ data.tar.gz: ce3f448a22be98027ad0be5e154924839e080969cfeeb27322c57e017d249c767b4a073b2faed164a290d102cda0e18c80aa9665840385a6cbfc124714f76808
@@ -1,17 +1,164 @@
1
1
  class Prawn::Svg::Color
2
- # TODO : use http://www.w3.org/TR/SVG11/types.html#ColorKeywords
2
+ UnresolvableURLWithNoFallbackError = Class.new(StandardError)
3
+
3
4
  HTML_COLORS = {
4
- 'black' => "000000", 'green' => "008000", 'silver' => "c0c0c0", 'lime' => "00ff00",
5
- 'gray' => "808080", 'olive' => "808000", 'white' => "ffffff", 'yellow' => "ffff00",
6
- 'maroon' => "800000", 'navy' => "000080", 'red' => "ff0000", 'blue' => "0000ff",
7
- 'purple' => "800080", 'teal' => "008080", 'fuchsia' => "ff00ff", 'aqua' => "00ffff"
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
- color_string.scan(/([^(\s]+(\([^)]*\))?)/).detect do |color, *_|
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
@@ -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
- if arguments.length == 3
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
- add_call_and_enter name, -r, :origin => [0, @document.y('0')]
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
- dec = @attributes[type]
146
- if dec == "none"
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
- elsif dec
149
- state[type.to_sym] = true
150
- if color = Prawn::Svg::Color.color_to_hex(dec)
151
- add_call "#{type}_color", color
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
 
@@ -1,5 +1,5 @@
1
1
  module Prawn
2
2
  module Svg
3
- VERSION = '0.20.0'
3
+ VERSION = '0.21.0'
4
4
  end
5
5
  end
@@ -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
- before :each do
5
- e = double(:attributes => {}, :name => "path")
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
- @document = Struct.new(:fallback_font_name, :css_parser, :warnings).new("Courier", nil, [])
8
- @element = Prawn::Svg::Element.new(@document, e, [], {})
9
- end
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.20.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-20 00:00:00.000000000 Z
11
+ date: 2015-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prawn