prawn-svg 0.12.0.3 → 0.12.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -50,9 +50,11 @@ prawn-svg is in its infancy and does not support the full SVG specifications. I
50
50
 
51
51
  - <tt>style</tt> tag, if css_parser gem is installed on the system (see CSS section below)
52
52
 
53
- - <tt>image</tt> tag, only with http/https schemes; does not support 'slice' preserveAspectRatio
54
-
55
- - attributes/styles: <tt>fill</tt>, <tt>stroke</tt>, <tt>stroke-width</tt>, <tt>opacity</tt>, <tt>fill-opacity</tt>, <tt>stroke-opacity</tt>, <tt>transform</tt>
53
+ - <tt>image</tt> tag, only with http/https schemes
54
+
55
+ - <tt>clipPath</tt> tag
56
+
57
+ - attributes/styles: <tt>fill</tt>, <tt>stroke</tt>, <tt>stroke-width</tt>, <tt>opacity</tt>, <tt>fill-opacity</tt>, <tt>stroke-opacity</tt>, <tt>transform</tt>, <tt>clip-path</tt>
56
58
 
57
59
  - transform methods: <tt>translate</tt>, <tt>rotate</tt>, <tt>scale</tt>, <tt>matrix</tt>
58
60
 
@@ -66,7 +68,7 @@ By default, prawn-svg has a fonts path of <tt>["/Library/Fonts", "/usr/share/fon
66
68
  Mac OS X and Debian Linux users. You can add to the font path:
67
69
 
68
70
  ```ruby
69
- Prawn::Svg.font_path << "/my/font/directory"
71
+ Prawn::Svg::Interface.font_path << "/my/font/directory"
70
72
  ```
71
73
 
72
74
  CSS
@@ -6,11 +6,11 @@ class Prawn::Svg::Document
6
6
  CSS_PARSER_LOADED = true
7
7
  rescue LoadError
8
8
  CSS_PARSER_LOADED = false
9
- end
10
-
9
+ end
10
+
11
11
  DEFAULT_WIDTH = 640
12
12
  DEFAULT_HEIGHT = 480
13
-
13
+
14
14
  # An +Array+ of warnings that occurred while parsing the SVG data.
15
15
  attr_reader :warnings
16
16
 
@@ -19,14 +19,15 @@ class Prawn::Svg::Document
19
19
 
20
20
  attr_reader :root,
21
21
  :actual_width, :actual_height, :width, :height, :x_offset, :y_offset, :cache_images,
22
- :css_parser
23
-
22
+ :css_parser, :elements_by_id
23
+
24
24
  def initialize(data, bounds, options)
25
25
  @css_parser = CssParser::Parser.new if CSS_PARSER_LOADED
26
26
 
27
27
  @root = REXML::Document.new(data).root
28
28
  @warnings = []
29
29
  @options = options
30
+ @elements_by_id = {}
30
31
  @cache_images = options[:cache_images]
31
32
  @actual_width, @actual_height = bounds # set this first so % width/heights can be used
32
33
 
@@ -39,9 +40,9 @@ class Prawn::Svg::Document
39
40
  @actual_width = points(@root.attributes['width'] || DEFAULT_WIDTH, :x)
40
41
  @actual_height = points(@root.attributes['height'] || DEFAULT_HEIGHT, :y)
41
42
  end
42
-
43
+
43
44
  if @options[:width]
44
- @width = @options[:width]
45
+ @width = @options[:width]
45
46
  @scale = @options[:width] / @actual_width.to_f
46
47
  elsif @options[:height]
47
48
  @height = @options[:height]
@@ -51,9 +52,9 @@ class Prawn::Svg::Document
51
52
  end
52
53
 
53
54
  @width ||= @actual_width * @scale
54
- @height ||= @actual_height * @scale
55
+ @height ||= @actual_height * @scale
55
56
  end
56
-
57
+
57
58
  def x(value)
58
59
  (points(value, :x) - @x_offset) * scale
59
60
  end
@@ -1,50 +1,54 @@
1
1
  class Prawn::Svg::Element
2
2
  attr_reader :document, :element, :parent_calls, :base_calls, :state, :attributes
3
3
  attr_accessor :calls
4
-
4
+
5
5
  def initialize(document, element, parent_calls, state)
6
6
  @document = document
7
7
  @element = element
8
8
  @parent_calls = parent_calls
9
9
  @state = state
10
10
  @base_calls = @calls = []
11
-
11
+
12
12
  combine_attributes_and_style_declarations
13
13
  apply_styles
14
-
14
+
15
15
  if id = @attributes["id"]
16
- state[:ids][id] = @base_calls
16
+ document.elements_by_id[id] = self
17
17
  end
18
18
  end
19
-
19
+
20
20
  def name
21
21
  @name ||= element.name
22
22
  end
23
-
23
+
24
24
  def each_child_element
25
25
  element.elements.each do |e|
26
26
  yield self.class.new(@document, e, @calls, @state.dup)
27
27
  end
28
28
  end
29
-
29
+
30
30
  def warnings
31
31
  @document.warnings
32
32
  end
33
-
33
+
34
34
  def add_call(name, *arguments)
35
35
  @calls << [name.to_s, arguments, []]
36
36
  end
37
-
37
+
38
38
  def add_call_and_enter(name, *arguments)
39
39
  @calls << [name.to_s, arguments, []]
40
40
  @calls = @calls.last.last
41
- end
42
-
41
+ end
42
+
43
43
  def append_calls_to_parent
44
44
  @parent_calls.concat(@base_calls)
45
45
  end
46
-
47
-
46
+
47
+ def add_calls_from_element(other)
48
+ @calls.concat other.base_calls
49
+ end
50
+
51
+
48
52
  protected
49
53
  def apply_styles
50
54
  # Transform
@@ -87,15 +91,30 @@ class Prawn::Svg::Element
87
91
  fill_opacity = clamp(@attributes['fill-opacity'].to_f, 0, 1) if @attributes['fill-opacity']
88
92
  stroke_opacity = clamp(@attributes['stroke-opacity'].to_f, 0, 1) if @attributes['stroke-opacity']
89
93
 
90
- if fill_opacity || stroke_opacity
94
+ if fill_opacity || stroke_opacity
91
95
  state[:fill_opacity] = (state[:fill_opacity] || 1) * (fill_opacity || 1)
92
96
  state[:stroke_opacity] = (state[:stroke_opacity] || 1) * (stroke_opacity || 1)
93
97
 
94
98
  add_call_and_enter 'transparent', state[:fill_opacity], state[:stroke_opacity]
95
99
  end
96
100
 
101
+ # Clip path
102
+ if clip_path = @attributes['clip-path']
103
+ if (matches = clip_path.strip.match(/\Aurl\(#(.*)\)\z/)).nil?
104
+ document.warnings << "Only clip-path attributes with the form 'url(#xxx)' are supported"
105
+ elsif (clip_path_element = @document.elements_by_id[matches[1]]).nil?
106
+ document.warnings << "clip-path ID '#{matches[1]}' not defined"
107
+ elsif clip_path_element.element.name != "clipPath"
108
+ document.warnings << "clip-path ID '#{matches[1]}' does not point to a clipPath tag"
109
+ else
110
+ add_call_and_enter 'save_graphics_state'
111
+ add_calls_from_element clip_path_element
112
+ add_call "clip"
113
+ end
114
+ end
115
+
97
116
  # Fill and stroke
98
- draw_types = []
117
+ draw_types = []
99
118
  [:fill, :stroke].each do |type|
100
119
  dec = @attributes[type.to_s]
101
120
  if dec == "none"
@@ -109,11 +128,11 @@ class Prawn::Svg::Element
109
128
 
110
129
  draw_types << type.to_s if state[type]
111
130
  end
112
-
131
+
113
132
  # Stroke width
114
- add_call('line_width', @document.distance(@attributes['stroke-width'])) if @attributes['stroke-width']
133
+ add_call('line_width', @document.distance(@attributes['stroke-width'])) if @attributes['stroke-width']
115
134
 
116
- # Fonts
135
+ # Fonts
117
136
  if size = @attributes['font-size']
118
137
  @state[:font_size] = size.to_f * @document.scale
119
138
  end
@@ -125,20 +144,20 @@ class Prawn::Svg::Element
125
144
  font_updated = true
126
145
  @state[:font_family] = family
127
146
  end
128
-
147
+
129
148
  if @state[:font_family] && font_updated
130
149
  if pdf_font = Prawn::Svg::Font.map_font_family_to_pdf_font(@state[:font_family], @state[:font_style])
131
150
  add_call_and_enter 'font', pdf_font
132
151
  else
133
152
  @document.warnings << "Font family '#{@state[:font_family]}' style '#{@state[:font_style] || 'normal'}' is not a known font."
134
153
  end
135
- end
136
-
154
+ end
155
+
137
156
  # Call fill, stroke, or both
138
157
  draw_type = draw_types.join("_and_")
139
- if draw_type != "" && !Prawn::Svg::Parser::CONTAINER_TAGS.include?(element.name)
140
- add_call_and_enter draw_type
141
- end
158
+ if draw_type != "" && !@state[:disable_drawing] && !Prawn::Svg::Parser::CONTAINER_TAGS.include?(element.name)
159
+ add_call_and_enter(draw_type)
160
+ end
142
161
  end
143
162
 
144
163
  def parse_css_method_calls(string)
@@ -150,7 +169,7 @@ class Prawn::Svg::Element
150
169
  end
151
170
 
152
171
  # TODO : use http://www.w3.org/TR/SVG11/types.html#ColorKeywords
153
- HTML_COLORS = {
172
+ HTML_COLORS = {
154
173
  'black' => "000000", 'green' => "008000", 'silver' => "c0c0c0", 'lime' => "00ff00",
155
174
  'gray' => "808080", 'olive' => "808000", 'white' => "ffffff", 'yellow' => "ffff00",
156
175
  'maroon' => "800000", 'navy' => "000080", 'red' => "ff0000", 'blue' => "0000ff",
@@ -173,26 +192,26 @@ class Prawn::Svg::Element
173
192
  value = m[n].to_f
174
193
  value *= 2.55 if m[n][-1..-1] == '%'
175
194
  "%02x" % clamp(value.round, 0, 255)
176
- end.join
177
- end
195
+ end.join
196
+ end
178
197
  end
179
198
  end
180
199
 
181
200
  def clamp(value, min_value, max_value)
182
201
  [[value, min_value].max, max_value].min
183
- end
184
-
202
+ end
203
+
185
204
  def combine_attributes_and_style_declarations
186
205
  if @document && @document.css_parser
187
206
  tag_style = @document.css_parser.find_by_selector(element.name)
188
207
  id_style = @document.css_parser.find_by_selector("##{element.attributes["id"]}") if element.attributes["id"]
189
-
208
+
190
209
  if classes = element.attributes["class"]
191
210
  class_styles = classes.strip.split(/\s+/).collect do |class_name|
192
211
  @document.css_parser.find_by_selector(".#{class_name}")
193
212
  end
194
213
  end
195
-
214
+
196
215
  element_style = element.attributes['style']
197
216
 
198
217
  style = [tag_style, class_styles, id_style, element_style].flatten.collect do |s|
@@ -204,7 +223,7 @@ class Prawn::Svg::Element
204
223
 
205
224
  @attributes = parse_css_declarations(style)
206
225
  element.attributes.each {|n,v| @attributes[n] = v unless @attributes[n]}
207
- end
226
+ end
208
227
 
209
228
  def parse_css_declarations(declarations)
210
229
  # copied from css_parser
@@ -6,7 +6,7 @@ module Prawn
6
6
  #
7
7
  # +options+ must contain the key :at, which takes a tuple of x and y co-ordinates.
8
8
  #
9
- # +options+ can optionally contain the key :width or :height. If both are
9
+ # +options+ can optionally contain the key :width or :height. If both are
10
10
  # specified, only :width will be used. If neither are specified, the resolution
11
11
  # given in the SVG will be used.
12
12
  #
@@ -18,7 +18,7 @@ module Prawn
18
18
  svg = Prawn::Svg::Interface.new(data, self, options)
19
19
  svg.draw
20
20
  {:warnings => svg.document.warnings, :width => svg.document.width, :height => svg.document.height}
21
- end
21
+ end
22
22
  end
23
23
  end
24
24
  end
@@ -7,7 +7,7 @@ class Prawn::Svg::Font
7
7
  "cursive" => "Times-Roman",
8
8
  "fantasy" => "Times-Roman",
9
9
  "monospace" => "Courier"}
10
-
10
+
11
11
  def self.map_font_family_to_pdf_font(font_family, font_style = nil)
12
12
  font_family.split(",").detect do |font|
13
13
  font = font.gsub(/['"]/, '').gsub(/\s{2,}/, ' ').strip.downcase
@@ -17,25 +17,25 @@ class Prawn::Svg::Font
17
17
 
18
18
  generic_font = GENERIC_CSS_FONT_MAPPING[font]
19
19
  break generic_font if generic_font
20
-
20
+
21
21
  break font.downcase if font_installed?(font, font_style)
22
22
  end
23
23
  end
24
-
24
+
25
25
  def self.font_path(font_family, font_style = nil)
26
26
  font_style = :normal if font_style.nil?
27
27
  if installed_styles = installed_fonts[font_family.downcase]
28
28
  installed_styles[font_style]
29
29
  end
30
30
  end
31
-
31
+
32
32
  def self.font_installed?(font_family, font_style = nil)
33
33
  !font_path(font_family, font_style).nil?
34
34
  end
35
35
 
36
36
  def self.installed_fonts
37
37
  return @installed_fonts if @installed_fonts
38
-
38
+
39
39
  fonts = {}
40
40
  Prawn::Svg::Interface.font_path.uniq.collect {|path| Dir["#{path}/*"]}.flatten.each do |filename|
41
41
  information = font_information(filename) rescue nil
@@ -46,14 +46,14 @@ class Prawn::Svg::Font
46
46
  when 'Bold Italic' then :bold_italic
47
47
  else :normal
48
48
  end
49
-
49
+
50
50
  (fonts[font_name.downcase] ||= {})[font_style] = filename
51
51
  end
52
52
  end
53
-
53
+
54
54
  @installed_fonts = fonts
55
55
  end
56
-
56
+
57
57
  def self.font_information(filename)
58
58
  File.open(filename, "r") do |f|
59
59
  x = f.read(12)
@@ -66,22 +66,22 @@ class Prawn::Svg::Font
66
66
  break tables[start+8..start+15].unpack("NN")
67
67
  end
68
68
  end
69
-
69
+
70
70
  return unless length
71
71
  f.seek(offset)
72
72
  data = f.read(length)
73
-
73
+
74
74
  format, name_count, string_offset = data[0..5].unpack("nnn")
75
-
75
+
76
76
  names = {}
77
77
  name_count.times do |index|
78
78
  start = 6 + index * 12
79
- platform_id, platform_specific_id, language_id, name_id, length, offset = data[start..start+11].unpack("nnnnnn")
79
+ platform_id, platform_specific_id, language_id, name_id, length, offset = data[start..start+11].unpack("nnnnnn")
80
80
  next unless language_id == 0 # English
81
81
  next unless name_id == 1 || name_id == 2
82
-
82
+
83
83
  offset += string_offset
84
- field = data[offset..offset+length-1]
84
+ field = data[offset..offset+length-1]
85
85
  names[name_id] = if platform_id == 0
86
86
  begin
87
87
  if field.respond_to?(:encode)
@@ -9,11 +9,11 @@ module Prawn
9
9
 
10
10
  @font_path = []
11
11
  DEFAULT_FONT_PATHS.each {|path| @font_path << path if File.exists?(path)}
12
-
12
+
13
13
  class << self; attr_accessor :font_path; end
14
-
14
+
15
15
  attr_reader :data, :prawn, :document, :options
16
-
16
+
17
17
  #
18
18
  # Creates a Prawn::Svg object.
19
19
  #
@@ -21,19 +21,19 @@ module Prawn
21
21
  #
22
22
  # +options+ must contain the key :at, which takes a tuple of x and y co-ordinates.
23
23
  #
24
- # +options+ can optionally contain the key :width or :height. If both are
24
+ # +options+ can optionally contain the key :width or :height. If both are
25
25
  # specified, only :width will be used.
26
26
  #
27
27
  def initialize(data, prawn, options)
28
28
  @data = data
29
29
  @prawn = prawn
30
30
  @options = options
31
-
31
+
32
32
  @options[:at] or raise "options[:at] must be specified"
33
33
 
34
34
  prawn.font_families.update(Prawn::Svg::Font.installed_fonts)
35
35
 
36
- @document = Document.new(data, [prawn.bounds.width, prawn.bounds.height], options)
36
+ @document = Document.new(data, [prawn.bounds.width, prawn.bounds.height], options)
37
37
  end
38
38
 
39
39
  #
@@ -47,12 +47,12 @@ module Prawn
47
47
  end
48
48
  end
49
49
 
50
-
51
- private
50
+
51
+ private
52
52
  def proc_creator(prawn, calls)
53
53
  Proc.new {issue_prawn_command(prawn, calls)}
54
54
  end
55
-
55
+
56
56
  def issue_prawn_command(prawn, calls)
57
57
  calls.each do |call, arguments, children|
58
58
  if rewrite_call_arguments(prawn, call, arguments) == false
@@ -66,21 +66,21 @@ module Prawn
66
66
  end
67
67
  end
68
68
  end
69
-
69
+
70
70
  def rewrite_call_arguments(prawn, call, arguments)
71
71
  if call == 'relative_draw_text'
72
72
  call.replace "draw_text"
73
73
  arguments.last[:at][0] = @relative_text_position if @relative_text_position
74
74
  end
75
-
75
+
76
76
  case call
77
77
  when 'text_group'
78
78
  @relative_text_position = nil
79
79
  false
80
-
80
+
81
81
  when 'draw_text'
82
82
  text, options = arguments
83
-
83
+
84
84
  width = prawn.width_of(text, options.merge(:kerning => true))
85
85
 
86
86
  if (anchor = options.delete(:text_anchor)) && %w(middle end).include?(anchor)
@@ -90,17 +90,21 @@ module Prawn
90
90
 
91
91
  space_width = prawn.width_of("n", options)
92
92
  @relative_text_position = options[:at][0] + width + space_width
93
-
93
+
94
94
  when 'transformation_matrix'
95
95
  x = prawn.bounds.absolute_left
96
96
  y = prawn.bounds.absolute_top
97
97
  arguments[4] += x - (x * arguments[0] - y * arguments[1])
98
98
  arguments[5] += y - (x * arguments[1] + y * arguments[0])
99
-
99
+
100
+ when 'clip'
101
+ prawn.add_content "W n" # clip to path
102
+ false
103
+
100
104
  when 'save'
101
105
  prawn.save_graphics_state
102
106
  false
103
-
107
+
104
108
  when 'restore'
105
109
  prawn.restore_graphics_state
106
110
  false
@@ -12,12 +12,12 @@ require 'rexml/document'
12
12
  # SVG to another format.
13
13
  #
14
14
  class Prawn::Svg::Parser
15
- CONTAINER_TAGS = %w(g svg symbol defs)
16
-
15
+ CONTAINER_TAGS = %w(g svg symbol defs clipPath)
16
+
17
17
  #
18
- # Construct a Parser object.
18
+ # Construct a Parser object.
19
19
  #
20
- # The +data+ argument is SVG data.
20
+ # The +data+ argument is SVG data.
21
21
  #
22
22
  # +bounds+ is a tuple [width, height] that specifies the bounds of the drawing space in points.
23
23
  #
@@ -40,16 +40,16 @@ class Prawn::Svg::Parser
40
40
  #
41
41
  def parse
42
42
  @document.warnings.clear
43
-
43
+
44
44
  calls = [['fill_color', '000000', []]]
45
45
  root_element = Prawn::Svg::Element.new(@document, @document.root, calls, :ids => {}, :fill => true)
46
-
46
+
47
47
  parse_element(root_element)
48
48
  calls
49
49
  end
50
50
 
51
51
 
52
- private
52
+ private
53
53
  REQUIRED_ATTRIBUTES = {
54
54
  "polyline" => %w(points),
55
55
  "polygon" => %w(points),
@@ -57,9 +57,9 @@ class Prawn::Svg::Parser
57
57
  "ellipse" => %w(rx ry),
58
58
  "rect" => %w(width height),
59
59
  "path" => %w(d),
60
- "image" => %w(width height)
60
+ "image" => %w(width height)
61
61
  }
62
-
62
+
63
63
  USE_NEW_CIRCLE_CALL = Prawn::Document.new.respond_to?(:circle)
64
64
  USE_NEW_ELLIPSE_CALL = Prawn::Document.new.respond_to?(:ellipse)
65
65
 
@@ -69,17 +69,18 @@ class Prawn::Svg::Parser
69
69
  if required_attributes = REQUIRED_ATTRIBUTES[element.name]
70
70
  return unless check_attrs_present(element, required_attributes)
71
71
  end
72
-
72
+
73
73
  case element.name
74
74
  when *CONTAINER_TAGS
75
+ do_not_append_calls = %w(symbol defs clipPath).include?(element.name)
76
+ element.state[:disable_drawing] = true if element.name == "clipPath"
77
+
75
78
  element.each_child_element do |child|
76
79
  element.add_call "save"
77
80
  parse_element(child)
78
81
  element.add_call "restore"
79
82
  end
80
-
81
- do_not_append_calls = %w(symbol defs).include?(element.name)
82
-
83
+
83
84
  when 'style'
84
85
  load_css_styles(element)
85
86
 
@@ -107,20 +108,20 @@ class Prawn::Svg::Parser
107
108
  [x(x), y(y)]
108
109
  end
109
110
  element.add_call "polygon", *points
110
-
111
+
111
112
  when 'circle'
112
113
  if USE_NEW_CIRCLE_CALL
113
- element.add_call "circle",
114
+ element.add_call "circle",
114
115
  [x(attrs['cx'] || "0"), y(attrs['cy'] || "0")], distance(attrs['r'])
115
116
  else
116
- element.add_call "circle_at",
117
+ element.add_call "circle_at",
117
118
  [x(attrs['cx'] || "0"), y(attrs['cy'] || "0")], :radius => distance(attrs['r'])
118
119
  end
119
-
120
+
120
121
  when 'ellipse'
121
- element.add_call USE_NEW_ELLIPSE_CALL ? "ellipse" : "ellipse_at",
122
+ element.add_call USE_NEW_ELLIPSE_CALL ? "ellipse" : "ellipse_at",
122
123
  [x(attrs['cx'] || "0"), y(attrs['cy'] || "0")], distance(attrs['rx']), distance(attrs['ry'])
123
-
124
+
124
125
  when 'rect'
125
126
  radius = distance(attrs['rx'] || attrs['ry'])
126
127
  args = [[x(attrs['x'] || '0'), y(attrs['y'] || '0')], distance(attrs['width']), distance(attrs['height'])]
@@ -130,18 +131,18 @@ class Prawn::Svg::Parser
130
131
  else
131
132
  element.add_call "rectangle", *args
132
133
  end
133
-
134
+
134
135
  when 'path'
135
136
  parse_path(element)
136
-
137
+
137
138
  when 'use'
138
139
  parse_use(element)
139
140
 
140
141
  when 'title', 'desc', 'metadata'
141
142
  # ignore
142
143
  do_not_append_calls = true
143
-
144
- when 'font-face', 'clipPath'
144
+
145
+ when 'font-face'
145
146
  # not supported
146
147
  do_not_append_calls = true
147
148
 
@@ -152,11 +153,11 @@ class Prawn::Svg::Parser
152
153
  else
153
154
  @document.warnings << "Unknown tag '#{element.name}'; ignoring"
154
155
  end
155
-
156
+
156
157
  element.append_calls_to_parent unless do_not_append_calls
157
158
  end
158
159
 
159
-
160
+
160
161
  def parse_path(element)
161
162
  @svg_path ||= Path.new
162
163
 
@@ -179,21 +180,20 @@ class Prawn::Svg::Parser
179
180
  else
180
181
  element.add_call command
181
182
  end
182
- end
183
+ end
183
184
  end
184
-
185
+
185
186
  def parse_use(element)
186
187
  if href = element.attributes['xlink:href']
187
188
  if href[0..0] == '#'
188
189
  id = href[1..-1]
189
- if id_calls = element.state[:ids][id]
190
+ if definition_element = @document.elements_by_id[id]
190
191
  x = element.attributes['x']
191
192
  y = element.attributes['y']
192
193
  if x || y
193
194
  element.add_call_and_enter "translate", distance(x || 0), -distance(y || 0)
194
195
  end
195
-
196
- element.calls.concat(id_calls)
196
+ element.add_calls_from_element definition_element
197
197
  else
198
198
  @document.warnings << "no tag with ID '#{id}' was found, referenced by use tag"
199
199
  end
@@ -203,7 +203,7 @@ class Prawn::Svg::Parser
203
203
  else
204
204
  @document.warnings << "no xlink:href specified on use tag"
205
205
  end
206
- end
206
+ end
207
207
 
208
208
  ####################################################################################################################
209
209
 
@@ -215,8 +215,8 @@ class Prawn::Svg::Parser
215
215
  element.element.text
216
216
  end
217
217
 
218
- @document.css_parser.add_block!(data)
219
- end
218
+ @document.css_parser.add_block!(data)
219
+ end
220
220
  end
221
221
 
222
222
  def check_attrs_present(element, attrs)
@@ -226,7 +226,7 @@ class Prawn::Svg::Parser
226
226
  end
227
227
  missing_attrs.empty?
228
228
  end
229
-
229
+
230
230
  %w(x y distance).each do |method|
231
231
  define_method(method) {|*a| @document.send(method, *a)}
232
232
  end
@@ -44,20 +44,30 @@ class Prawn::Svg::Parser::Image
44
44
  par = (attrs['preserveAspectRatio'] || "xMidYMid meet").strip.split(/\s+/)
45
45
  par.shift if par.first == "defer"
46
46
  align, meet_or_slice = par
47
- raise Error, "slice preserveAspectRatio not supported by Prawn" if meet_or_slice == "slice"
47
+ slice = meet_or_slice == "slice"
48
+
49
+ if slice
50
+ element.add_call "save"
51
+ element.add_call "rectangle", [x, y], width, height
52
+ element.add_call "clip"
53
+ end
48
54
 
49
55
  options = {}
50
56
  case align
51
57
  when /\Ax(Min|Mid|Max)Y(Min|Mid|Max)\z/
52
- options[:fit] = [width, height]
53
58
  ratio = image_ratio(image)
54
- if width/height < ratio
59
+
60
+ options[:fit] = [width, height] unless slice
61
+
62
+ if (width/height > ratio) == slice
63
+ options[:width] = width if slice
55
64
  y -= case $2
56
65
  when "Min" then 0
57
66
  when "Mid" then (height - width/ratio)/2
58
67
  when "Max" then height - width/ratio
59
68
  end
60
69
  else
70
+ options[:height] = height if slice
61
71
  x += case $1
62
72
  when "Min" then 0
63
73
  when "Mid" then (width - height*ratio)/2
@@ -74,6 +84,7 @@ class Prawn::Svg::Parser::Image
74
84
  options[:at] = [x, y]
75
85
 
76
86
  element.add_call "image", FakeIO.new(image), options
87
+ element.add_call "restore" if slice
77
88
  rescue Error => e
78
89
  @document.warnings << e.message
79
90
  end
@@ -38,19 +38,19 @@ module Prawn
38
38
  raise InvalidError, "Invalid character '#{c}' in SVG path data"
39
39
  end
40
40
  end
41
-
41
+
42
42
  values << value.to_f if value != ""
43
43
  run_path_command(cmd, values) if cmd
44
-
44
+
45
45
  @calls
46
46
  end
47
-
48
-
47
+
48
+
49
49
  private
50
50
  def run_path_command(command, values)
51
51
  upcase_command = command.upcase
52
52
  relative = command != upcase_command
53
-
53
+
54
54
  case upcase_command
55
55
  when 'M' # moveto
56
56
  x = values.shift
@@ -60,19 +60,19 @@ module Prawn
60
60
  x += @last_point.first
61
61
  y += @last_point.last
62
62
  end
63
-
63
+
64
64
  @last_point = @subpath_initial_point = [x, y]
65
65
  @calls << ["move_to", @last_point]
66
-
66
+
67
67
  return run_path_command('L', values) if values.any?
68
-
68
+
69
69
  when 'Z' # closepath
70
70
  if @subpath_initial_point
71
71
  #@calls << ["line_to", @subpath_initial_point]
72
72
  @calls << ["close_path"]
73
73
  @last_point = @subpath_initial_point
74
74
  end
75
-
75
+
76
76
  when 'L' # lineto
77
77
  while values.any?
78
78
  x = values.shift
@@ -84,7 +84,7 @@ module Prawn
84
84
  @last_point = [x, y]
85
85
  @calls << ["line_to", @last_point]
86
86
  end
87
-
87
+
88
88
  when 'H' # horizontal lineto
89
89
  while values.any?
90
90
  x = values.shift
@@ -100,89 +100,89 @@ module Prawn
100
100
  @last_point = [@last_point.first, y]
101
101
  @calls << ["line_to", @last_point]
102
102
  end
103
-
103
+
104
104
  when 'C' # curveto
105
105
  while values.any?
106
106
  x1, y1, x2, y2, x, y = (1..6).collect {values.shift}
107
107
  if relative && @last_point
108
108
  x += @last_point.first
109
109
  x1 += @last_point.first
110
- x2 += @last_point.first
111
- y += @last_point.last
112
- y1 += @last_point.last
113
- y2 += @last_point.last
110
+ x2 += @last_point.first
111
+ y += @last_point.last
112
+ y1 += @last_point.last
113
+ y2 += @last_point.last
114
114
  end
115
-
115
+
116
116
  @last_point = [x, y]
117
117
  @previous_control_point = [x2, y2]
118
118
  @calls << ["curve_to", [x, y, x1, y1, x2, y2]]
119
- end
119
+ end
120
120
 
121
121
  when 'S' # shorthand/smooth curveto
122
122
  while values.any?
123
123
  x2, y2, x, y = (1..4).collect {values.shift}
124
124
  if relative && @last_point
125
125
  x += @last_point.first
126
- x2 += @last_point.first
127
- y += @last_point.last
128
- y2 += @last_point.last
126
+ x2 += @last_point.first
127
+ y += @last_point.last
128
+ y2 += @last_point.last
129
129
  end
130
-
130
+
131
131
  if @previous_control_point
132
132
  x1 = 2 * @last_point.first - @previous_control_point.first
133
133
  y1 = 2 * @last_point.last - @previous_control_point.last
134
134
  else
135
135
  x1, y1 = @last_point
136
136
  end
137
-
137
+
138
138
  @last_point = [x, y]
139
- @previous_control_point = [x2, y2]
139
+ @previous_control_point = [x2, y2]
140
140
  @calls << ["curve_to", [x, y, x1, y1, x2, y2]]
141
141
  end
142
-
142
+
143
143
  when 'Q', 'T' # quadratic curveto
144
144
  while values.any?
145
145
  if shorthand = upcase_command == 'T'
146
- x, y = (1..2).collect {values.shift}
147
- else
146
+ x, y = (1..2).collect {values.shift}
147
+ else
148
148
  x1, y1, x, y = (1..4).collect {values.shift}
149
149
  end
150
-
150
+
151
151
  if relative && @last_point
152
152
  x += @last_point.first
153
153
  x1 += @last_point.first if x1
154
- y += @last_point.last
154
+ y += @last_point.last
155
155
  y1 += @last_point.last if y1
156
156
  end
157
-
158
- if shorthand
157
+
158
+ if shorthand
159
159
  if @previous_quadratic_control_point
160
160
  x1 = 2 * @last_point.first - @previous_quadratic_control_point.first
161
161
  y1 = 2 * @last_point.last - @previous_quadratic_control_point.last
162
162
  else
163
163
  x1, y1 = @last_point
164
164
  end
165
- end
166
-
165
+ end
166
+
167
167
  # convert from quadratic to cubic
168
168
  cx1 = @last_point.first + (x1 - @last_point.first) * 2 / 3.0
169
169
  cy1 = @last_point.last + (y1 - @last_point.last) * 2 / 3.0
170
170
  cx2 = cx1 + (x - @last_point.first) / 3.0
171
- cy2 = cy1 + (y - @last_point.last) / 3.0
171
+ cy2 = cy1 + (y - @last_point.last) / 3.0
172
172
 
173
173
  @last_point = [x, y]
174
174
  @previous_quadratic_control_point = [x1, y1]
175
-
175
+
176
176
  @calls << ["curve_to", [x, y, cx1, cy1, cx2, cy2]]
177
- end
178
-
177
+ end
178
+
179
179
  when 'A'
180
- # unsupported
180
+ # unsupported
181
181
  end
182
-
182
+
183
183
  @previous_control_point = nil unless %w(C S).include?(upcase_command)
184
184
  @previous_quadratic_control_point = nil unless %w(Q T).include?(upcase_command)
185
185
  end
186
186
  end
187
- end
187
+ end
188
188
  end
@@ -3,17 +3,17 @@ class Prawn::Svg::Parser::Text
3
3
  element.add_call_and_enter "text_group"
4
4
  internal_parse(element, [element.document.x(0)], [element.document.y(0)], false)
5
5
  end
6
-
6
+
7
7
  protected
8
8
  def internal_parse(element, x_positions, y_positions, relative)
9
9
  attrs = element.attributes
10
-
10
+
11
11
  if attrs['x'] || attrs['y']
12
12
  relative = false
13
13
  x_positions = attrs['x'].split(/[\s,]+/).collect {|n| element.document.x(n)} if attrs['x']
14
14
  y_positions = attrs['y'].split(/[\s,]+/).collect {|n| element.document.y(n)} if attrs['y']
15
15
  end
16
-
16
+
17
17
  if attrs['dx'] || attrs['dy']
18
18
  element.add_call_and_enter "translate", element.document.distance(attrs['dx'] || 0), -element.document.distance(attrs['dy'] || 0)
19
19
  end
@@ -25,18 +25,18 @@ class Prawn::Svg::Parser::Text
25
25
  if style = element.state[:font_style]
26
26
  opts[:style] = style
27
27
  end
28
-
28
+
29
29
  # This is not a prawn option but we can't work out how to render it here -
30
30
  # it's handled by Svg#rewrite_call_arguments
31
31
  if anchor = attrs['text-anchor']
32
- opts[:text_anchor] = anchor
32
+ opts[:text_anchor] = anchor
33
33
  end
34
34
 
35
35
  element.element.children.each do |child|
36
36
  if child.node_type == :text
37
- text = child.to_s.strip.gsub(/\s+/, " ")
37
+ text = child.value.strip.gsub(/\s+/, " ")
38
38
 
39
- while text != ""
39
+ while text != ""
40
40
  opts[:at] = [x_positions.first, y_positions.first]
41
41
 
42
42
  if x_positions.length > 1 || y_positions.length > 1
@@ -59,10 +59,10 @@ class Prawn::Svg::Parser::Text
59
59
  internal_parse(child_element, x_positions, y_positions, relative)
60
60
  child_element.append_calls_to_parent
61
61
  element.add_call 'restore'
62
-
62
+
63
63
  else
64
64
  element.warnings << "Unknown tag '#{child.name}' inside text tag; ignoring"
65
65
  end
66
- end
66
+ end
67
67
  end
68
68
  end
@@ -1,5 +1,5 @@
1
1
  module Prawn
2
2
  module Svg
3
- VERSION = '0.12.0.3'
3
+ VERSION = '0.12.0.4'
4
4
  end
5
5
  end
@@ -8,7 +8,7 @@ describe Prawn::Svg::Parser do
8
8
  <line x1="10%" y1="10%" x2="90%" y2="90%" />
9
9
  </svg>
10
10
  SVG
11
-
11
+
12
12
  document = Prawn::Svg::Document.new(svg, [2000, 2000], {})
13
13
  Prawn::Svg::Parser.new(document).parse[-2][-1].should == [["line", [100.0, 900.0, 900.0, 100.0], []]]
14
14
  end
@@ -19,18 +19,18 @@ describe Prawn::Svg::Parser do
19
19
  <line x1="1in" y1="1in" x2="9in" y2="9in" />
20
20
  </svg>
21
21
  SVG
22
-
22
+
23
23
  document = Prawn::Svg::Document.new(svg, [2000, 2000], {})
24
24
  Prawn::Svg::Parser.new(document).parse[-2][-1].should == [["line", [72.0, 720.0 - 72.0, 720.0 - 72.0, 72.0], []]]
25
25
  end
26
26
  end
27
-
27
+
28
28
  describe :parse_element do
29
29
  before(:each) do
30
30
  @document = Prawn::Svg::Document.new("<svg></svg>", [100, 100], {})
31
31
  @parser = Prawn::Svg::Parser.new(@document)
32
32
  end
33
-
33
+
34
34
  def mock_element(name, attributes = {})
35
35
  e = mock
36
36
  e.stub!(:name).and_return(name)
@@ -38,7 +38,7 @@ describe Prawn::Svg::Parser do
38
38
 
39
39
  Prawn::Svg::Element.new(@document, e, [], {})
40
40
  end
41
-
41
+
42
42
  it "ignores tags it doesn't know about" do
43
43
  calls = []
44
44
  @parser.send :parse_element, mock_element("unknown")
@@ -46,7 +46,7 @@ describe Prawn::Svg::Parser do
46
46
  @document.warnings.length.should == 1
47
47
  @document.warnings.first.should include("Unknown tag")
48
48
  end
49
-
49
+
50
50
  it "ignores tags that don't have all required attributes set" do
51
51
  calls = []
52
52
  @parser.send :parse_element, mock_element("ellipse", "rx" => "1")
@@ -4,13 +4,13 @@ describe Prawn::Svg::Parser::Path do
4
4
  before :each do
5
5
  @path = Prawn::Svg::Parser::Path.new
6
6
  end
7
-
8
- describe "command parsing" do
7
+
8
+ describe "command parsing" do
9
9
  it "correctly parses a valid path" do
10
10
  calls = []
11
11
  @path.stub!(:run_path_command) {|*args| calls << args}
12
12
  @path.parse("A12.34 -56.78 89B4 5 C 6,7 T QX 0 Z")
13
-
13
+
14
14
  calls.should == [
15
15
  ["A", [12.34, -56.78, 89]],
16
16
  ["B", [4, 5]],
@@ -19,19 +19,19 @@ describe Prawn::Svg::Parser::Path do
19
19
  ["Q", []],
20
20
  ["X", [0]],
21
21
  ["Z", []]
22
- ]
22
+ ]
23
23
  end
24
-
24
+
25
25
  it "correctly parses an empty path" do
26
26
  @path.should_not_receive(:run_path_command)
27
27
  @path.parse("").should == []
28
28
  @path.parse(" ").should == []
29
29
  end
30
-
30
+
31
31
  it "raises on invalid characters in the path" do
32
32
  lambda {@path.parse("M 10 % 20")}.should raise_error(Prawn::Svg::Parser::Path::InvalidError)
33
33
  end
34
-
34
+
35
35
  it "raises on numerical data before a command letter" do
36
36
  lambda {@path.parse("10 P")}.should raise_error(Prawn::Svg::Parser::Path::InvalidError)
37
37
  end
@@ -1,14 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Prawn::Svg::Interface do
3
+ describe Prawn::Svg::Interface do
4
4
  describe "sample file rendering" do
5
5
  root = "#{File.dirname(__FILE__)}/../.."
6
6
  files = Dir["#{root}/spec/sample_svg/*.svg"]
7
-
7
+
8
8
  it "has at least 10 SVG sample files to test" do
9
9
  files.length.should >= 10
10
10
  end
11
-
11
+
12
12
  files.each do |file|
13
13
  it "renders the #{File.basename file} sample file without warnings or crashing" do
14
14
  warnings = nil
@@ -0,0 +1,10 @@
1
+ <svg width="120" height="120" viewPort="0 0 120 120" version="1.1" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <clipPath id="myClip">
4
+ <circle cx="30" cy="30" r="20"/>
5
+ <circle cx="70" cy="70" r="20"/>
6
+ </clipPath>
7
+ </defs>
8
+
9
+ <rect x="10" y="10" width="100" height="100" fill="red" clip-path="url(#myClip)"/>
10
+ </svg>
@@ -4,7 +4,19 @@
4
4
  <svg width="210mm" height="297mm" viewBox="0 0 1050 1485"
5
5
  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
6
6
 
7
+ <g transform="translate(0 50)">
8
+ <text y="50" font-size="12pt">mid/mid</text>
9
+ <text y="200" font-size="12pt">mid/mid</text>
10
+ <text y="350" font-size="12pt">mid/mid</text>
11
+ <text y="550" font-size="12pt">min/min</text>
12
+ <text y="700" font-size="12pt">min/min</text>
13
+ <text y="900" font-size="12pt">max/max</text>
14
+ <text y="1050" font-size="12pt">max/max</text>
15
+ </g>
16
+
7
17
  <g transform="translate(50 0)">
18
+ <text y="25" font-size="12pt">wide image, aspect ratio meet</text>
19
+
8
20
  <rect y="50" width="100" height="100" fill="none" stroke="blue" stroke-width="2"/>
9
21
  <image y="50" width="100" height="100" preserveAspectRatio="xMidYMid" xlink:href="http://files.myopera.com/baby2u/albums/423302/smiley-cool.jpg"></image>
10
22
 
@@ -28,6 +40,33 @@
28
40
  </g>
29
41
 
30
42
  <g transform="translate(250 0)">
43
+ <text y="25" font-size="12pt">wide image, aspect ratio slice</text>
44
+
45
+ <rect y="50" width="100" height="100" fill="none" stroke="blue" stroke-width="2"/>
46
+ <image y="50" width="100" height="100" preserveAspectRatio="xMidYMid slice" xlink:href="http://files.myopera.com/baby2u/albums/423302/smiley-cool.jpg"></image>
47
+
48
+ <rect y="200" width="150" height="100" fill="none" stroke="blue" stroke-width="2"/>
49
+ <image y="200" width="150" height="100" preserveAspectRatio="xMidYMid slice" xlink:href="http://files.myopera.com/baby2u/albums/423302/smiley-cool.jpg"></image>
50
+
51
+ <rect y="350" width="100" height="150" fill="none" stroke="blue" stroke-width="2"/>
52
+ <image y="350" width="100" height="150" preserveAspectRatio="xMidYMid slice" xlink:href="http://files.myopera.com/baby2u/albums/423302/smiley-cool.jpg"></image>
53
+
54
+ <rect y="550" width="150" height="100" fill="none" stroke="blue" stroke-width="2"/>
55
+ <image y="550" width="150" height="100" preserveAspectRatio="xMinYMin slice" xlink:href="http://files.myopera.com/baby2u/albums/423302/smiley-cool.jpg"></image>
56
+
57
+ <rect y="700" width="100" height="150" fill="none" stroke="blue" stroke-width="2"/>
58
+ <image y="700" width="100" height="150" preserveAspectRatio="xMinYMin slice" xlink:href="http://files.myopera.com/baby2u/albums/423302/smiley-cool.jpg"></image>
59
+
60
+ <rect y="900" width="150" height="100" fill="none" stroke="blue" stroke-width="2"/>
61
+ <image y="900" width="150" height="100" preserveAspectRatio="xMaxYMax slice" xlink:href="http://files.myopera.com/baby2u/albums/423302/smiley-cool.jpg"></image>
62
+
63
+ <rect y="1050" width="100" height="150" fill="none" stroke="blue" stroke-width="2"/>
64
+ <image y="1050" width="100" height="150" preserveAspectRatio="xMaxYMax slice" xlink:href="http://files.myopera.com/baby2u/albums/423302/smiley-cool.jpg"></image>
65
+ </g>
66
+
67
+ <g transform="translate(450 0)">
68
+ <text y="25" font-size="12pt">long image, aspect ratio meet</text>
69
+
31
70
  <rect y="50" width="100" height="100" fill="none" stroke="blue" stroke-width="2"/>
32
71
  <image y="50" width="100" height="100" xlink:href="http://imalbum.aufeminin.com/album/D20090809/583007_KWWH1ZQ8FU5ATJXSR84C5MRBUWW33L_989174388_H143954_L.jpg"></image>
33
72
 
@@ -50,7 +89,34 @@
50
89
  <image y="1050" width="100" height="200" preserveAspectRatio="xMaxYMax" xlink:href="http://imalbum.aufeminin.com/album/D20090809/583007_KWWH1ZQ8FU5ATJXSR84C5MRBUWW33L_989174388_H143954_L.jpg"></image>
51
90
  </g>
52
91
 
53
- <g transform="translate(450 0)">
92
+ <g transform="translate(650 0)">
93
+ <text y="25" font-size="12pt">long image, aspect ratio slice</text>
94
+
95
+ <rect y="50" width="100" height="100" fill="none" stroke="blue" stroke-width="2"/>
96
+ <image y="50" width="100" height="100" preserveAspectRatio="xMidYMid slice" xlink:href="http://imalbum.aufeminin.com/album/D20090809/583007_KWWH1ZQ8FU5ATJXSR84C5MRBUWW33L_989174388_H143954_L.jpg"></image>
97
+
98
+ <rect y="200" width="150" height="100" fill="none" stroke="blue" stroke-width="2"/>
99
+ <image y="200" width="150" height="100" preserveAspectRatio="xMidYMid slice" xlink:href="http://imalbum.aufeminin.com/album/D20090809/583007_KWWH1ZQ8FU5ATJXSR84C5MRBUWW33L_989174388_H143954_L.jpg"></image>
100
+
101
+ <rect y="350" width="100" height="200" fill="none" stroke="blue" stroke-width="2"/>
102
+ <image y="350" width="100" height="200" preserveAspectRatio="xMidYMid slice" xlink:href="http://imalbum.aufeminin.com/album/D20090809/583007_KWWH1ZQ8FU5ATJXSR84C5MRBUWW33L_989174388_H143954_L.jpg"></image>
103
+
104
+ <rect y="550" width="150" height="100" fill="none" stroke="blue" stroke-width="2"/>
105
+ <image y="550" width="150" height="100" preserveAspectRatio="xMinYMin slice" xlink:href="http://imalbum.aufeminin.com/album/D20090809/583007_KWWH1ZQ8FU5ATJXSR84C5MRBUWW33L_989174388_H143954_L.jpg"></image>
106
+
107
+ <rect y="700" width="100" height="200" fill="none" stroke="blue" stroke-width="2"/>
108
+ <image y="700" width="100" height="200" preserveAspectRatio="xMinYMin slice" xlink:href="http://imalbum.aufeminin.com/album/D20090809/583007_KWWH1ZQ8FU5ATJXSR84C5MRBUWW33L_989174388_H143954_L.jpg"></image>
109
+
110
+ <rect y="900" width="150" height="100" fill="none" stroke="blue" stroke-width="2"/>
111
+ <image y="900" width="150" height="100" preserveAspectRatio="xMaxYMax slice" xlink:href="http://imalbum.aufeminin.com/album/D20090809/583007_KWWH1ZQ8FU5ATJXSR84C5MRBUWW33L_989174388_H143954_L.jpg"></image>
112
+
113
+ <rect y="1050" width="100" height="200" fill="none" stroke="blue" stroke-width="2"/>
114
+ <image y="1050" width="100" height="200" preserveAspectRatio="xMaxYMax slice" xlink:href="http://imalbum.aufeminin.com/album/D20090809/583007_KWWH1ZQ8FU5ATJXSR84C5MRBUWW33L_989174388_H143954_L.jpg"></image>
115
+ </g>
116
+
117
+ <g transform="translate(850 0)">
118
+ <text y="25" font-size="12pt">aspect ratio preservation disabled</text>
119
+
54
120
  <rect y="50" width="100" height="100" fill="none" stroke="blue" stroke-width="2"/>
55
121
  <image y="50" width="100" height="100" preserveAspectRatio="none" xlink:href="http://imalbum.aufeminin.com/album/D20090809/583007_KWWH1ZQ8FU5ATJXSR84C5MRBUWW33L_989174388_H143954_L.jpg"></image>
56
122
 
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg width="10cm" height="3cm" viewBox="0 0 1000 300" xmlns="http://www.w3.org/2000/svg" version="1.1">
4
+ <g font-family="Verdana" font-size="24">
5
+ <text x="200" y="150" fill="blue">
6
+ More &amp; more people say &quot;Less is &lt; and
7
+ more is &gt;.&quot;
8
+ </text>
9
+ </g>
10
+ <rect x="1" y="1" width="998" height="298" fill="none" stroke="blue" stroke-width="2" />
11
+ </svg>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prawn-svg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0.3
4
+ version: 0.12.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-18 00:00:00.000000000 Z
12
+ date: 2013-04-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: prawn
@@ -93,6 +93,7 @@ files:
93
93
  - spec/sample_output/directory
94
94
  - spec/sample_svg/arcs01.svg
95
95
  - spec/sample_svg/circle01.svg
96
+ - spec/sample_svg/clip_path.svg
96
97
  - spec/sample_svg/close_path.svg
97
98
  - spec/sample_svg/cubic01.svg
98
99
  - spec/sample_svg/cubic01a.svg
@@ -112,6 +113,7 @@ files:
112
113
  - spec/sample_svg/rect02.svg
113
114
  - spec/sample_svg/rotate_scale.svg
114
115
  - spec/sample_svg/scruffy_graph.svg
116
+ - spec/sample_svg/text_entities.svg
115
117
  - spec/sample_svg/triangle01.svg
116
118
  - spec/sample_svg/tspan01.svg
117
119
  - spec/sample_svg/tspan02.svg
@@ -154,6 +156,7 @@ test_files:
154
156
  - spec/sample_output/directory
155
157
  - spec/sample_svg/arcs01.svg
156
158
  - spec/sample_svg/circle01.svg
159
+ - spec/sample_svg/clip_path.svg
157
160
  - spec/sample_svg/close_path.svg
158
161
  - spec/sample_svg/cubic01.svg
159
162
  - spec/sample_svg/cubic01a.svg
@@ -173,6 +176,7 @@ test_files:
173
176
  - spec/sample_svg/rect02.svg
174
177
  - spec/sample_svg/rotate_scale.svg
175
178
  - spec/sample_svg/scruffy_graph.svg
179
+ - spec/sample_svg/text_entities.svg
176
180
  - spec/sample_svg/triangle01.svg
177
181
  - spec/sample_svg/tspan01.svg
178
182
  - spec/sample_svg/tspan02.svg