prawn-svg 0.15.0.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
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