prawn-svg 0.15.0.0 → 0.19.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -12
  3. data/lib/prawn/svg/calculators/aspect_ratio.rb +58 -0
  4. data/lib/prawn/svg/calculators/document_sizing.rb +75 -0
  5. data/lib/prawn/svg/calculators/pixels.rb +21 -0
  6. data/lib/prawn/svg/document.rb +16 -43
  7. data/lib/prawn/svg/element.rb +71 -18
  8. data/lib/prawn/svg/extension.rb +3 -3
  9. data/lib/prawn/svg/interface.rb +80 -19
  10. data/lib/prawn/svg/parser/image.rb +9 -62
  11. data/lib/prawn/svg/parser/path.rb +19 -1
  12. data/lib/prawn/svg/parser/text.rb +2 -0
  13. data/lib/prawn/svg/parser.rb +53 -18
  14. data/lib/prawn/svg/url_loader.rb +34 -0
  15. data/lib/prawn/svg/version.rb +1 -1
  16. data/lib/prawn-svg.rb +4 -0
  17. data/prawn-svg.gemspec +7 -4
  18. data/spec/integration_spec.rb +83 -0
  19. data/spec/prawn/svg/calculators/aspect_ratio_spec.rb +95 -0
  20. data/spec/prawn/svg/calculators/document_sizing_spec.rb +73 -0
  21. data/spec/prawn/svg/document_spec.rb +21 -17
  22. data/spec/prawn/svg/element_spec.rb +1 -1
  23. data/spec/prawn/svg/interface_spec.rb +59 -0
  24. data/spec/prawn/svg/parser/path_spec.rb +89 -0
  25. data/spec/prawn/svg/url_loader_spec.rb +46 -0
  26. data/spec/sample_images/mushroom-long.jpg +0 -0
  27. data/spec/sample_images/mushroom-wide.jpg +0 -0
  28. data/spec/sample_svg/cap_styles.svg +13 -0
  29. data/spec/sample_svg/display_none.svg +13 -0
  30. data/spec/sample_svg/gistfile1.svg +36 -0
  31. data/spec/sample_svg/hidden_paths.svg +6 -0
  32. data/spec/sample_svg/image01.svg +31 -31
  33. data/spec/sample_svg/negminy.svg +25 -0
  34. data/spec/sample_svg/path.svg +5 -0
  35. data/spec/sample_svg/pie_piece.svg +7 -0
  36. data/spec/sample_svg/viewbox.svg +4 -0
  37. data/spec/sample_svg/viewport.svg +23 -0
  38. data/spec/spec_helper.rb +7 -0
  39. metadata +81 -25
  40. data/spec/lib/path_spec.rb +0 -54
  41. data/spec/lib/svg_spec.rb +0 -47
  42. /data/spec/{lib → prawn/svg}/parser_spec.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 61181b453d5e28bba30c87c430975b21e5e9876d
4
- data.tar.gz: c1c1d6ec4e6bb4d60af290a836ed25574dbca760
3
+ metadata.gz: dcccb8c6d35a2f33debf7f17d04dc1a8b3099398
4
+ data.tar.gz: 3ac53d2c35c41f1de03582e46175a9bf296a6cd0
5
5
  SHA512:
6
- metadata.gz: 766a6cb2dc207b26e433509f1dbdce22055106c05806ff327f4203dc478d6ecfe99308502f2b1964c442d8ac2ecba1df5739593144ff2328e3c5e94aad74d440
7
- data.tar.gz: 2281ec02f05246f40f4c68497d23f34da10b51ba16c60e76bd1e6d2467b0e1d80326ff76cc05aa244b794784be855f2996ceeeb6ce28465ffcecf66bee469b50
6
+ metadata.gz: ea4c6fb099669924881a72e4d983a5b890a4e74d38eb5bc7e1173223485acc35a69b0dabf95c41329bc1c254a6be53771d98f5e13fcf5d5c118c2fa1b2bd85ce
7
+ data.tar.gz: d3440e0c86365ad251beec2011f1f243dfc35e24ee82af3684c925e2e9c08d4b44e416e366cbd036343908241567cfd75878929bc5ce98fa9e4f81cdde963317
data/README.md CHANGED
@@ -6,6 +6,8 @@ This will take an SVG file as input and render it into your PDF. Find out more
6
6
 
7
7
  http://wiki.github.com/sandal/prawn/
8
8
 
9
+ prawn-svg is compatible with all versions of Prawn from 0.8.4 onwards, including the 1.x and 2.x series.
10
+
9
11
  ## Using prawn-svg
10
12
 
11
13
  ```ruby
@@ -14,10 +16,13 @@ Prawn::Document.generate("svg.pdf") do
14
16
  end
15
17
  ```
16
18
 
17
- <tt>:at</tt> must be specified.
19
+ Supply <tt>:at</tt> if you want to render it at a specific location on the page.
20
+ Use <tt>:position</tt> with a value of <tt>:left</tt>, <tt>:center</tt>, <tt>:right</tt> or a number to render it at the current cursor position, or use <tt>:vposition</tt> with a value
21
+ of <tt>:top</tt>, <tt>:center</tt>, <tt>:bottom</tt> or a number to specify its Y position too.
18
22
 
19
- <tt>:width</tt>, <tt>:height</tt>, or neither may be specified; if neither is present,
20
- the resolution specified in the SVG will be used.
23
+ Either <tt>:width</tt>, <tt>:height</tt>, or neither may be specified; if neither is present,
24
+ the dimensions specified in the SVG will be used, or if the dimensions aren't specified, it'll
25
+ fit to the space available on the page.
21
26
 
22
27
  <tt>:cache_images</tt>, if set to true, will cache images per document based on their URL.
23
28
 
@@ -26,7 +31,7 @@ If this value is set to <tt>nil</tt>, prawn-svg will ignore a request for an unk
26
31
 
27
32
  ## Supported features
28
33
 
29
- prawn-svg does not support the full SVG specification. It currently supports:
34
+ prawn-svg supports most but not all of the full SVG 1.1 specification. It currently supports:
30
35
 
31
36
  - <tt>&lt;line&gt;</tt>, <tt>&lt;polyline&gt;</tt>, <tt>&lt;polygon&gt;</tt>, <tt>&lt;circle&gt;</tt> and <tt>&lt;ellipse&gt;</tt>
32
37
 
@@ -36,36 +41,39 @@ prawn-svg does not support the full SVG specification. It currently supports:
36
41
  implementation of elliptical arc is a bit rough at the moment.
37
42
 
38
43
  - <tt>&lt;text&gt;</tt> and <tt>&lt;tspan&gt;</tt> with attributes
39
- <tt>size</tt>, <tt>text-anchor</tt>, <tt>font-family</tt>, <tt>font-weight</tt>, <tt>dx</tt>, <tt>dy</tt>
44
+ <tt>text-anchor</tt>, <tt>font-size</tt>, <tt>font-family</tt>, <tt>font-weight</tt>, <tt>font-style</tt>, <tt>dx</tt>, <tt>dy</tt>
40
45
 
41
46
  - <tt>&lt;svg&gt;</tt>, <tt>&lt;g&gt;</tt> and <tt>&lt;symbol&gt;</tt>
42
47
 
43
48
  - <tt>&lt;use&gt;</tt>
44
49
 
45
- - <tt>&lt;style&gt;</tt>, if css_parser gem is installed on the system (see CSS section below)
50
+ - <tt>&lt;style&gt;</tt> plus <tt>id</tt>, <tt>class</tt> and <tt>style</tt> attributes (see CSS section below)
46
51
 
47
52
  - <tt>&lt;image&gt;</tt> with <tt>http:</tt>, <tt>https:</tt> and <tt>data:image/*;base64</tt> schemes
48
53
 
49
54
  - <tt>&lt;clipPath&gt;</tt>
50
55
 
51
- - 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
+ - attributes/styles: <tt>fill</tt>, <tt>stroke</tt>, <tt>stroke-width</tt>, <tt>stroke-linecap</tt>, <tt>stroke-dasharray</tt>, <tt>opacity</tt>, <tt>fill-opacity</tt>, <tt>stroke-opacity</tt>, <tt>transform</tt>, <tt>clip-path</tt>, <tt>display</tt>
57
+
58
+ - the <tt>viewBox</tt> attribute on the <tt>&lt;svg&gt;</tt> tag
59
+
60
+ - the <tt>preserveAspectRatio</tt> attribute on the <tt>&lt;svg&gt;</tt> and <tt>&lt;image&gt;</tt> tags
52
61
 
53
62
  - transform methods: <tt>translate</tt>, <tt>rotate</tt>, <tt>scale</tt>, <tt>matrix</tt>
54
63
 
55
64
  - colors: HTML standard names, <tt>#xxx</tt>, <tt>#xxxxxx</tt>, <tt>rgb(1, 2, 3)</tt>, <tt>rgb(1%, 2%, 3%)</tt>
56
65
 
57
- - measurements specified in <tt>pt</tt>, <tt>cm</tt>, <tt>dm</tt>, <tt>ft</tt>, <tt>in</tt>, <tt>m</tt>, <tt>mm</tt>, <tt>yd</tt>, <tt>%</tt>
66
+ - measurements specified in <tt>pt</tt>, <tt>cm</tt>, <tt>dm</tt>, <tt>ft</tt>, <tt>in</tt>, <tt>m</tt>, <tt>mm</tt>, <tt>yd</tt>, <tt>pc</tt>, <tt>%</tt>
58
67
 
59
- - fonts: generic CSS fonts, built in PDF fonts, and any TTF fonts in your fonts path
68
+ - fonts: generic CSS fonts, built-in PDF fonts, and any TTF fonts in your fonts path
60
69
 
61
70
  ## CSS
62
71
 
63
- If the css_parser gem is installed, it will handle CSS style definitions, but only simple tag, class or id definitions. It's very basic
64
- so do not expect too much of it.
72
+ prawn-svg uses the css_parser gem to parse CSS <tt>&lt;style&gt;</tt> blocks. It only handles simple tag, class or id selectors; attribute and other advanced selectors are not supported.
65
73
 
66
74
  ## Not supported
67
75
 
68
- prawn-svg does not support external references, measurements in <tt>en</tt> or <tt>em</tt>, sub-viewports, gradients/patterns or markers.
76
+ prawn-svg does not support external <tt>url()</tt> references, measurements in <tt>en</tt> or <tt>em</tt>, sub-viewports, gradients/patterns or markers.
69
77
 
70
78
  ## Configuration
71
79
 
@@ -0,0 +1,58 @@
1
+ module Prawn::Svg::Calculators
2
+ class AspectRatio
3
+ attr_reader :align, :defer
4
+ attr_reader :width, :height, :x, :y
5
+
6
+ def initialize(value, container_dimensions, object_dimensions)
7
+ values = (value || "xMidYMid meet").strip.split(/\s+/)
8
+ @x = @y = 0
9
+
10
+ if values.first == "defer"
11
+ @defer = true
12
+ values.shift
13
+ end
14
+
15
+ @align, @meet_or_slice = values
16
+
17
+ w_container, h_container = container_dimensions
18
+ w_object, h_object = object_dimensions
19
+
20
+ container_ratio = w_container / h_container.to_f
21
+ object_ratio = w_object / h_object.to_f
22
+
23
+ if @align == "none"
24
+ @width, @height = container_dimensions
25
+ else
26
+ matches = @align.to_s.strip.match(/\Ax(Min|Mid|Max)Y(Min|Mid|Max)\z/i) || [nil, "Mid", "Mid"]
27
+
28
+ if (container_ratio > object_ratio) == slice?
29
+ @width, @height = [w_container, w_container / object_ratio]
30
+ @y = case matches[2].downcase
31
+ when "min" then 0
32
+ when "mid" then (h_container - w_container/object_ratio)/2
33
+ when "max" then h_container - w_container/object_ratio
34
+ end
35
+ else
36
+ @width, @height = [h_container * object_ratio, h_container]
37
+ @x = case matches[1].downcase
38
+ when "min" then 0
39
+ when "mid" then (w_container - h_container*object_ratio)/2
40
+ when "max" then w_container - h_container*object_ratio
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ def slice?
47
+ @meet_or_slice == "slice"
48
+ end
49
+
50
+ def meet?
51
+ @meet_or_slice != "slice"
52
+ end
53
+
54
+ def inspect
55
+ "[AspectRatio: #{@width},#{@height} offset #{@x},#{@y}]"
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,75 @@
1
+ module Prawn::Svg::Calculators
2
+ class DocumentSizing
3
+ attr_writer :document_width, :document_height
4
+ attr_writer :view_box, :preserve_aspect_ratio
5
+ attr_writer :requested_width, :requested_height
6
+
7
+ attr_reader :bounds
8
+ attr_reader :x_offset, :y_offset, :x_scale, :y_scale
9
+ attr_reader :viewport_width, :viewport_height, :viewport_diagonal, :output_width, :output_height
10
+
11
+ def initialize(bounds, attributes = nil)
12
+ @bounds = bounds
13
+ set_from_attributes(attributes) if attributes
14
+ end
15
+
16
+ def set_from_attributes(attributes)
17
+ @document_width = attributes['width']
18
+ @document_height = attributes['height']
19
+ @view_box = attributes['viewBox']
20
+ @preserve_aspect_ratio = attributes['preserveAspectRatio']
21
+ end
22
+
23
+ def calculate
24
+ @x_offset = @y_offset = 0
25
+ @x_scale = @y_scale = 1
26
+
27
+ width = Prawn::Svg::Calculators::Pixels.to_pixels(@document_width, @bounds[0])
28
+ height = Prawn::Svg::Calculators::Pixels.to_pixels(@document_height, @bounds[1])
29
+
30
+ default_aspect_ratio = "x#{width ? "Mid" : "Min"}Y#{height ? "Mid" : "Min"} meet"
31
+
32
+ width ||= @bounds[0]
33
+ height ||= @bounds[1]
34
+
35
+ if @view_box
36
+ values = @view_box.strip.split(/\s+/)
37
+ @x_offset, @y_offset, @viewport_width, @viewport_height = values.map {|value| value.to_f}
38
+ @x_offset = -@x_offset
39
+
40
+ @preserve_aspect_ratio ||= default_aspect_ratio
41
+ aspect = Prawn::Svg::Calculators::AspectRatio.new(@preserve_aspect_ratio, [width, height], [@viewport_width, @viewport_height])
42
+ @x_scale = aspect.width / @viewport_width
43
+ @y_scale = aspect.height / @viewport_height
44
+ @x_offset -= aspect.x
45
+ @y_offset -= aspect.y
46
+ end
47
+
48
+ @viewport_width ||= width
49
+ @viewport_height ||= height
50
+
51
+ # SVG 1.1 section 7.10
52
+ @viewport_diagonal = Math.sqrt(@viewport_width**2 + @viewport_height**2) / Math.sqrt(2)
53
+
54
+ if @requested_width
55
+ scale = @requested_width / width
56
+ width = @requested_width
57
+ height *= scale
58
+ @x_scale *= scale
59
+ @y_scale *= scale
60
+
61
+ elsif @requested_height
62
+ scale = @requested_height / height
63
+ height = @requested_height
64
+ width *= scale
65
+ @x_scale *= scale
66
+ @y_scale *= scale
67
+ end
68
+
69
+ @output_width = width
70
+ @output_height = height
71
+
72
+ self
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,21 @@
1
+ module Prawn::Svg::Calculators
2
+ class Pixels
3
+ extend Prawn::Measurements
4
+
5
+ def self.to_pixels(value, axis_length)
6
+ if value.is_a?(String)
7
+ if match = value.match(/\d(cm|dm|ft|in|m|mm|yd)$/)
8
+ send("#{match[1]}2pt", value.to_f)
9
+ elsif match = value.match(/\dpc$/)
10
+ value.to_f * 15 # according to http://www.w3.org/TR/SVG11/coords.html
11
+ elsif value[-1..-1] == "%"
12
+ value.to_f * axis_length / 100.0
13
+ else
14
+ value.to_f
15
+ end
16
+ elsif value
17
+ value.to_f
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,6 +1,4 @@
1
1
  class Prawn::Svg::Document
2
- include Prawn::Measurements
3
-
4
2
  begin
5
3
  require 'css_parser'
6
4
  CSS_PARSER_LOADED = true
@@ -8,18 +6,14 @@ class Prawn::Svg::Document
8
6
  CSS_PARSER_LOADED = false
9
7
  end
10
8
 
11
- DEFAULT_WIDTH = 640
12
- DEFAULT_HEIGHT = 480
13
9
  DEFAULT_FALLBACK_FONT_NAME = "Times-Roman"
14
10
 
15
11
  # An +Array+ of warnings that occurred while parsing the SVG data.
16
12
  attr_reader :warnings
17
-
18
- # The scaling factor, as determined by the :width or :height options.
19
- attr_accessor :scale
13
+ attr_writer :url_cache
20
14
 
21
15
  attr_reader :root,
22
- :actual_width, :actual_height, :width, :height, :x_offset, :y_offset,
16
+ :sizing,
23
17
  :cache_images, :fallback_font_name,
24
18
  :css_parser, :elements_by_id
25
19
 
@@ -32,55 +26,34 @@ class Prawn::Svg::Document
32
26
  @elements_by_id = {}
33
27
  @cache_images = options[:cache_images]
34
28
  @fallback_font_name = options.fetch(:fallback_font_name, DEFAULT_FALLBACK_FONT_NAME)
35
- @actual_width, @actual_height = bounds # set this first so % width/heights can be used
36
29
 
37
- if vb = @root.attributes['viewBox']
38
- x1, y1, x2, y2 = vb.strip.split(/\s+/)
39
- @x_offset, @y_offset = [x1.to_f, y1.to_f]
40
- @actual_width, @actual_height = [x2.to_f - x1.to_f, y2.to_f - y1.to_f]
41
- else
42
- @x_offset, @y_offset = [0, 0]
43
- @actual_width = points(@root.attributes['width'] || DEFAULT_WIDTH, :x)
44
- @actual_height = points(@root.attributes['height'] || DEFAULT_HEIGHT, :y)
45
- end
30
+ @sizing = Prawn::Svg::Calculators::DocumentSizing.new(bounds, @root.attributes)
31
+ sizing.requested_width = options[:width]
32
+ sizing.requested_height = options[:height]
33
+ sizing.calculate
46
34
 
47
- if @options[:width]
48
- @width = @options[:width]
49
- @scale = @options[:width] / @actual_width.to_f
50
- elsif @options[:height]
51
- @height = @options[:height]
52
- @scale = @options[:height] / @actual_height.to_f
53
- else
54
- @scale = 1
55
- end
35
+ @axis_to_size = {:x => sizing.viewport_width, :y => sizing.viewport_height}
56
36
 
57
- @width ||= @actual_width * @scale
58
- @height ||= @actual_height * @scale
37
+ yield self if block_given?
59
38
  end
60
39
 
61
40
  def x(value)
62
- (points(value, :x) - @x_offset) * scale
41
+ points(value, :x)
63
42
  end
64
43
 
65
44
  def y(value)
66
- (@actual_height - (points(value, :y) - @y_offset)) * scale
45
+ sizing.output_height - points(value, :y)
67
46
  end
68
47
 
69
48
  def distance(value, axis = nil)
70
- value && (points(value, axis) * scale)
49
+ value && points(value, axis)
71
50
  end
72
51
 
73
52
  def points(value, axis = nil)
74
- if value.is_a?(String)
75
- if match = value.match(/\d(cm|dm|ft|in|m|mm|yd)$/)
76
- send("#{match[1]}2pt", value.to_f)
77
- elsif value[-1..-1] == "%"
78
- value.to_f * (axis == :y ? @actual_height : @actual_width) / 100.0
79
- else
80
- value.to_f
81
- end
82
- else
83
- value.to_f
84
- end
53
+ Prawn::Svg::Calculators::Pixels.to_pixels(value, @axis_to_size.fetch(axis, sizing.viewport_diagonal))
54
+ end
55
+
56
+ def url_loader
57
+ @url_loader ||= Prawn::Svg::UrlLoader.new(:enable_cache => cache_images)
85
58
  end
86
59
  end
@@ -55,14 +55,26 @@ class Prawn::Svg::Element
55
55
  parse_opacity_attributes_and_call
56
56
  parse_clip_path_attribute_and_call
57
57
  draw_types = parse_fill_and_stroke_attributes_and_call
58
- parse_stroke_width_attribute_and_call
58
+ parse_stroke_attributes_and_call
59
59
  parse_font_attributes_and_call
60
+ parse_display_attribute
61
+ apply_drawing_call(draw_types)
62
+ end
60
63
 
61
- if draw_types.length > 0 && !@state[:disable_drawing] && !Prawn::Svg::Parser::CONTAINER_TAGS.include?(element.name)
62
- add_call_and_enter(draw_types.join("_and_"))
64
+ def apply_drawing_call(draw_types)
65
+ if !@state[:disable_drawing] && !container?
66
+ if draw_types.empty? || @state[:display] == "none"
67
+ add_call_and_enter("end_path")
68
+ else
69
+ add_call_and_enter(draw_types.join("_and_"))
70
+ end
63
71
  end
64
72
  end
65
73
 
74
+ def container?
75
+ Prawn::Svg::Parser::CONTAINER_TAGS.include?(name)
76
+ end
77
+
66
78
  def parse_transform_attribute_and_call
67
79
  return unless transform = @attributes['transform']
68
80
 
@@ -70,7 +82,7 @@ class Prawn::Svg::Element
70
82
  case name
71
83
  when 'translate'
72
84
  x, y = arguments
73
- add_call_and_enter name, @document.distance(x), -@document.distance(y)
85
+ add_call_and_enter name, @document.distance(x, :x), -@document.distance(y, :y)
74
86
 
75
87
  when 'rotate'
76
88
  r, x, y = arguments.collect {|a| a.to_f}
@@ -90,7 +102,7 @@ class Prawn::Svg::Element
90
102
  @document.warnings << "transform 'matrix' must have six arguments"
91
103
  else
92
104
  a, b, c, d, e, f = arguments.collect {|argument| argument.to_f}
93
- add_call_and_enter "transformation_matrix", a, -b, -c, d, @document.distance(e), -@document.distance(f)
105
+ add_call_and_enter "transformation_matrix", a, -b, -c, d, @document.distance(e, :x), -@document.distance(f, :y)
94
106
  end
95
107
  else
96
108
  @document.warnings << "Unknown transformation '#{name}'; ignoring"
@@ -129,30 +141,58 @@ class Prawn::Svg::Element
129
141
  end
130
142
 
131
143
  def parse_fill_and_stroke_attributes_and_call
132
- draw_types = []
133
- [:fill, :stroke].each do |type|
134
- dec = @attributes[type.to_s]
144
+ ["fill", "stroke"].select do |type|
145
+ dec = @attributes[type]
135
146
  if dec == "none"
136
- state[type] = false
147
+ state[type.to_sym] = false
137
148
  elsif dec
138
- state[type] = true
149
+ state[type.to_sym] = true
139
150
  if color = Prawn::Svg::Color.color_to_hex(dec)
140
151
  add_call "#{type}_color", color
141
152
  end
142
153
  end
143
154
 
144
- draw_types << type.to_s if state[type]
155
+ state[type.to_sym]
145
156
  end
146
- draw_types
147
157
  end
148
158
 
149
- def parse_stroke_width_attribute_and_call
150
- add_call('line_width', @document.distance(@attributes['stroke-width'])) if @attributes['stroke-width']
159
+ CAP_STYLE_TRANSLATIONS = {"butt" => :butt, "round" => :round, "square" => :projecting_square}
160
+
161
+ def parse_stroke_attributes_and_call
162
+ if width = @attributes['stroke-width']
163
+ add_call('line_width', @document.distance(width))
164
+ end
165
+
166
+ if (linecap = attribute_value_as_keyword('stroke-linecap')) && linecap != 'inherit'
167
+ add_call('cap_style', CAP_STYLE_TRANSLATIONS.fetch(linecap, :butt))
168
+ end
169
+
170
+ if dasharray = attribute_value_as_keyword('stroke-dasharray')
171
+ case dasharray
172
+ when 'inherit'
173
+ # don't do anything
174
+ when 'none'
175
+ add_call('undash')
176
+ else
177
+ array = dasharray.split(Prawn::Svg::Parser::COMMA_WSP_REGEXP)
178
+ array *= 2 if array.length % 2 == 1
179
+ number_array = array.map {|value| @document.distance(value)}
180
+
181
+ if number_array.any? {|number| number < 0}
182
+ @document.warnings << "stroke-dasharray cannot have negative numbers; treating as 'none'"
183
+ add_call('undash')
184
+ elsif number_array.inject(0) {|a, b| a + b} == 0
185
+ add_call('undash')
186
+ else
187
+ add_call('dash', number_array)
188
+ end
189
+ end
190
+ end
151
191
  end
152
192
 
153
193
  def parse_font_attributes_and_call
154
194
  if size = @attributes['font-size']
155
- @state[:font_size] = size.to_f * @document.scale
195
+ @state[:font_size] = size.to_f
156
196
  end
157
197
  if weight = @attributes['font-weight']
158
198
  font_updated = true
@@ -184,6 +224,9 @@ class Prawn::Svg::Element
184
224
  end
185
225
  end
186
226
 
227
+ def parse_display_attribute
228
+ @state[:display] = @attributes['display'].strip if @attributes['display']
229
+ end
187
230
 
188
231
  def parse_css_method_calls(string)
189
232
  string.scan(/\s*(\w+)\(([^)]+)\)\s*/).collect do |call|
@@ -218,7 +261,11 @@ class Prawn::Svg::Element
218
261
  end
219
262
 
220
263
  @attributes = parse_css_declarations(style)
221
- element.attributes.each {|n,v| @attributes[n] = v unless @attributes[n]}
264
+
265
+ element.attributes.each do |name, value|
266
+ name = name.downcase
267
+ @attributes[name] = value unless @attributes[name]
268
+ end
222
269
  end
223
270
 
224
271
  def parse_css_declarations(declarations)
@@ -228,10 +275,16 @@ class Prawn::Svg::Element
228
275
  output = {}
229
276
  declarations.split(/[\;$]+/m).each do |decs|
230
277
  if matches = decs.match(/\s*(.[^:]*)\s*\:\s*(.[^;]*)\s*(;|\Z)/i)
231
- property, value, end_of_declaration = matches.captures
232
- output[property] = value
278
+ property, value, _ = matches.captures
279
+ output[property.downcase] = value
233
280
  end
234
281
  end
235
282
  output
236
283
  end
284
+
285
+ def attribute_value_as_keyword(name)
286
+ if value = @attributes[name]
287
+ value.strip.downcase
288
+ end
289
+ end
237
290
  end
@@ -14,10 +14,10 @@ module Prawn
14
14
  #
15
15
  # svg IO.read("example.svg"), :at => [100, 300], :width => 600
16
16
  #
17
- def svg(data, options={})
18
- svg = Prawn::Svg::Interface.new(data, self, options)
17
+ def svg(data, options = {}, &block)
18
+ svg = Prawn::Svg::Interface.new(data, self, options, &block)
19
19
  svg.draw
20
- {:warnings => svg.document.warnings, :width => svg.document.width, :height => svg.document.height}
20
+ {:warnings => svg.document.warnings, :width => svg.document.sizing.output_width, :height => svg.document.sizing.output_height}
21
21
  end
22
22
  end
23
23
  end