prawn-svg 0.15.0.0 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -0
  3. data/README.md +51 -20
  4. data/lib/prawn/svg/attributes/clip_path.rb +17 -0
  5. data/lib/prawn/svg/attributes/color.rb +5 -0
  6. data/lib/prawn/svg/attributes/display.rb +5 -0
  7. data/lib/prawn/svg/attributes/font.rb +38 -0
  8. data/lib/prawn/svg/attributes/opacity.rb +15 -0
  9. data/lib/prawn/svg/attributes/stroke.rb +35 -0
  10. data/lib/prawn/svg/attributes/transform.rb +50 -0
  11. data/lib/prawn/svg/attributes.rb +6 -0
  12. data/lib/prawn/svg/calculators/aspect_ratio.rb +58 -0
  13. data/lib/prawn/svg/calculators/document_sizing.rb +99 -0
  14. data/lib/prawn/svg/calculators/pixels.rb +21 -0
  15. data/lib/prawn/svg/color.rb +197 -12
  16. data/lib/prawn/svg/css.rb +40 -0
  17. data/lib/prawn/svg/document.rb +37 -48
  18. data/lib/prawn/svg/elements/base.rb +238 -0
  19. data/lib/prawn/svg/elements/circle.rb +25 -0
  20. data/lib/prawn/svg/elements/container.rb +15 -0
  21. data/lib/prawn/svg/elements/ellipse.rb +23 -0
  22. data/lib/prawn/svg/elements/gradient.rb +120 -0
  23. data/lib/prawn/svg/elements/ignored.rb +5 -0
  24. data/lib/prawn/svg/elements/image.rb +81 -0
  25. data/lib/prawn/svg/elements/line.rb +16 -0
  26. data/lib/prawn/svg/elements/path.rb +405 -0
  27. data/lib/prawn/svg/elements/polygon.rb +17 -0
  28. data/lib/prawn/svg/elements/polyline.rb +22 -0
  29. data/lib/prawn/svg/elements/rect.rb +33 -0
  30. data/lib/prawn/svg/elements/root.rb +13 -0
  31. data/lib/prawn/svg/elements/style.rb +10 -0
  32. data/lib/prawn/svg/elements/text.rb +87 -0
  33. data/lib/prawn/svg/elements/use.rb +29 -0
  34. data/lib/prawn/svg/elements.rb +33 -0
  35. data/lib/prawn/svg/extension.rb +4 -4
  36. data/lib/prawn/svg/font.rb +10 -92
  37. data/lib/prawn/svg/font_registry.rb +73 -0
  38. data/lib/prawn/svg/interface.rb +91 -31
  39. data/lib/prawn/svg/loaders/data.rb +18 -0
  40. data/lib/prawn/svg/loaders/file.rb +66 -0
  41. data/lib/prawn/svg/loaders/web.rb +28 -0
  42. data/lib/prawn/svg/state.rb +39 -0
  43. data/lib/prawn/svg/ttf.rb +61 -0
  44. data/lib/prawn/svg/url_loader.rb +46 -0
  45. data/lib/prawn/svg/version.rb +2 -2
  46. data/lib/prawn-svg.rb +20 -6
  47. data/prawn-svg.gemspec +8 -5
  48. data/spec/integration_spec.rb +141 -0
  49. data/spec/prawn/svg/attributes/font_spec.rb +52 -0
  50. data/spec/prawn/svg/attributes/transform_spec.rb +56 -0
  51. data/spec/prawn/svg/calculators/aspect_ratio_spec.rb +95 -0
  52. data/spec/prawn/svg/calculators/document_sizing_spec.rb +128 -0
  53. data/spec/prawn/svg/color_spec.rb +43 -8
  54. data/spec/prawn/svg/css_spec.rb +24 -0
  55. data/spec/prawn/svg/document_spec.rb +48 -19
  56. data/spec/prawn/svg/elements/base_spec.rb +147 -0
  57. data/spec/prawn/svg/elements/gradient_spec.rb +61 -0
  58. data/spec/prawn/svg/elements/path_spec.rb +123 -0
  59. data/spec/prawn/svg/elements/style_spec.rb +23 -0
  60. data/spec/prawn/svg/elements/text_spec.rb +61 -0
  61. data/spec/prawn/svg/font_registry_spec.rb +54 -0
  62. data/spec/prawn/svg/font_spec.rb +1 -28
  63. data/spec/prawn/svg/interface_spec.rb +94 -0
  64. data/spec/prawn/svg/loaders/data_spec.rb +55 -0
  65. data/spec/prawn/svg/loaders/file_spec.rb +84 -0
  66. data/spec/prawn/svg/loaders/web_spec.rb +37 -0
  67. data/spec/prawn/svg/ttf_spec.rb +32 -0
  68. data/spec/prawn/svg/url_loader_spec.rb +112 -0
  69. data/spec/sample_images/mushroom-long.jpg +0 -0
  70. data/spec/sample_images/mushroom-wide.jpg +0 -0
  71. data/spec/sample_svg/cap_styles.svg +13 -0
  72. data/spec/sample_svg/display_none.svg +13 -0
  73. data/spec/sample_svg/gistfile1.svg +36 -0
  74. data/spec/sample_svg/gradients.svg +40 -0
  75. data/spec/sample_svg/hidden_paths.svg +6 -0
  76. data/spec/sample_svg/image01.svg +31 -31
  77. data/spec/sample_svg/image03.svg +30 -0
  78. data/spec/sample_svg/negminy.svg +25 -0
  79. data/spec/sample_svg/no_width_or_height.svg +4 -0
  80. data/spec/sample_svg/path.svg +5 -0
  81. data/spec/sample_svg/pie_piece.svg +7 -0
  82. data/spec/sample_svg/preserve-space.svg +19 -0
  83. data/spec/sample_svg/rect02.svg +8 -11
  84. data/spec/sample_svg/tspan03-cc.svg +21 -0
  85. data/spec/sample_svg/viewbox.svg +4 -0
  86. data/spec/sample_svg/viewport.svg +23 -0
  87. data/spec/sample_ttf/OpenSans-SemiboldItalic.ttf +0 -0
  88. data/spec/spec_helper.rb +24 -2
  89. metadata +150 -36
  90. data/lib/prawn/svg/element.rb +0 -237
  91. data/lib/prawn/svg/parser/image.rb +0 -134
  92. data/lib/prawn/svg/parser/path.rb +0 -374
  93. data/lib/prawn/svg/parser/text.rb +0 -66
  94. data/lib/prawn/svg/parser.rb +0 -233
  95. data/spec/lib/parser_spec.rb +0 -55
  96. data/spec/lib/path_spec.rb +0 -54
  97. data/spec/lib/svg_spec.rb +0 -47
  98. data/spec/prawn/svg/element_spec.rb +0 -36
  99. data/spec/prawn/svg/parser/text_spec.rb +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 61181b453d5e28bba30c87c430975b21e5e9876d
4
- data.tar.gz: c1c1d6ec4e6bb4d60af290a836ed25574dbca760
3
+ metadata.gz: 0455251b1d5e7c430660f51c4843d26adb8d9913
4
+ data.tar.gz: caf87db1d427b7b528b5ae09b826b4dddec7a8a0
5
5
  SHA512:
6
- metadata.gz: 766a6cb2dc207b26e433509f1dbdce22055106c05806ff327f4203dc478d6ecfe99308502f2b1964c442d8ac2ecba1df5739593144ff2328e3c5e94aad74d440
7
- data.tar.gz: 2281ec02f05246f40f4c68497d23f34da10b51ba16c60e76bd1e6d2467b0e1d80326ff76cc05aa244b794784be855f2996ceeeb6ce28465ffcecf66bee469b50
6
+ metadata.gz: e1068767350107317ce264382ead9432a8ca98118d82301a402ef5fb50f6da65bdc2f547101955911cd24cf291745dd3f7b55f51d2374c96b9442673833df638
7
+ data.tar.gz: 47b83cd0def9edaec474ec458fa01831801abefadeebe797840b64bd7b3b2f38ff869e135893bb16a9f23b6a9fea28ab46c9f843775fa1c3f72bcd11964d8b33
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.6
5
+ - 2.2.2
6
+
data/README.md CHANGED
@@ -1,32 +1,57 @@
1
1
  # prawn-svg
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/prawn-svg.svg)](https://badge.fury.io/rb/prawn-svg)
4
+ [![Build Status](https://travis-ci.org/mogest/prawn-svg.svg?branch=master)](https://travis-ci.org/mogest/prawn-svg)
5
+
3
6
  An SVG renderer for the Prawn PDF library.
4
7
 
5
8
  This will take an SVG file as input and render it into your PDF. Find out more about the Prawn PDF library at:
6
9
 
7
- http://wiki.github.com/sandal/prawn/
10
+ http://github.com/prawnpdf/prawn
11
+
12
+ prawn-svg is compatible with all versions of Prawn from 0.11.1 onwards, including the 1.x and 2.x series.
13
+ The minimum Ruby version required is 2.0.0.
8
14
 
9
15
  ## Using prawn-svg
10
16
 
11
17
  ```ruby
12
- Prawn::Document.generate("svg.pdf") do
13
- svg svg_data, :at => [x, y], :width => w
18
+ Prawn::Document.generate("test.pdf") do
19
+ svg '<svg><rect width="100" height="100" fill="red"></rect></svg>'
14
20
  end
15
21
  ```
16
22
 
17
- <tt>:at</tt> must be specified.
23
+ prawn-svg will do something sensible if you call it with only an SVG document, but you can also
24
+ pass the following options to tailor its operation:
18
25
 
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.
26
+ Option | Data type | Description
27
+ ----------- | --------- | -----------
28
+ :at | [integer, integer] | Specify the location on the page you want the SVG to appear.
29
+ :position | :left, :center, :right, integer | If :at not specified, specifies the horizontal position to show the SVG. Defaults to :left.
30
+ :vposition | :top, :center, :bottom, integer | If :at not specified, specifies the vertical position to show the SVG. Defaults to current cursor position.
31
+ :width | integer | Desired width of the SVG. Defaults to horizontal space available.
32
+ :height | integer | Desired height of the SVG. Defaults to vertical space available.
33
+ :enable_web_requests | boolean | If true, prawn-svg will make http and https requests to fetch images. Defaults to true.
34
+ :enable_file_requests_with_root | string | If not nil, prawn-svg will serve `file:` URLs from your local disk if the file is located under the specified directory. It is very dangerous to specify the root path ("/") if you're not fully in control of your input SVG. Defaults to `nil` (off).
35
+ :cache_images | boolean | If true, prawn-svg will cache the result of all URL requests. Defaults to false.
36
+ :fallback_font_name | string | A font name which will override the default fallback font of Times-Roman. If this value is set to <tt>nil</tt>, prawn-svg will ignore a request for an unknown font and log a warning.
21
37
 
22
- <tt>:cache_images</tt>, if set to true, will cache images per document based on their URL.
38
+ ## Examples
23
39
 
24
- <tt>:fallback_font_name</tt> takes a font name which will override the default fallback font of Times-Roman.
25
- If this value is set to <tt>nil</tt>, prawn-svg will ignore a request for an unknown font and log a warning.
40
+ ```ruby
41
+ # Render the logo contained in the file logo.svg at 100, 100 with a width of 300
42
+ svg IO.read("logo.svg"), at: [100, 100], width: 300
43
+
44
+ # Render the logo at the current Y cursor position, centered in the current bounding box
45
+ svg IO.read("logo.svg"), position: :center
46
+
47
+ # Render the logo at the current Y cursor position, and serve file: links relative to its directory
48
+ root_path = "/apps/myapp/current/images"
49
+ svg IO.read("#{root_path}/logo.svg"), enable_file_requests_with_root: root_path
50
+ ```
26
51
 
27
52
  ## Supported features
28
53
 
29
- prawn-svg does not support the full SVG specification. It currently supports:
54
+ prawn-svg supports most but not all of the full SVG 1.1 specification. It currently supports:
30
55
 
31
56
  - <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
57
 
@@ -36,36 +61,42 @@ prawn-svg does not support the full SVG specification. It currently supports:
36
61
  implementation of elliptical arc is a bit rough at the moment.
37
62
 
38
63
  - <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>
64
+ <tt>text-anchor</tt>, <tt>font-size</tt>, <tt>font-family</tt>, <tt>font-weight</tt>, <tt>font-style</tt>, <tt>letter-spacing</tt>, <tt>dx</tt>, <tt>dy</tt>
40
65
 
41
66
  - <tt>&lt;svg&gt;</tt>, <tt>&lt;g&gt;</tt> and <tt>&lt;symbol&gt;</tt>
42
67
 
43
68
  - <tt>&lt;use&gt;</tt>
44
69
 
45
- - <tt>&lt;style&gt;</tt>, if css_parser gem is installed on the system (see CSS section below)
70
+ - <tt>&lt;style&gt;</tt> plus <tt>id</tt>, <tt>class</tt> and <tt>style</tt> attributes (see CSS section below)
46
71
 
47
- - <tt>&lt;image&gt;</tt> with <tt>http:</tt>, <tt>https:</tt> and <tt>data:image/*;base64</tt> schemes
72
+ - <tt>&lt;image&gt;</tt> with <tt>http:</tt>, <tt>https:</tt> and <tt>data:image/\*;base64</tt> schemes
48
73
 
49
74
  - <tt>&lt;clipPath&gt;</tt>
50
75
 
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>
76
+ - <tt>&lt;linearGradient&gt;</tt> but only with Prawn 2.0.4+. gradientTransform, spreadMethod and stop-opacity are
77
+ unimplemented.
78
+
79
+ - 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>
80
+
81
+ - the <tt>viewBox</tt> attribute on the <tt>&lt;svg&gt;</tt> tag
82
+
83
+ - the <tt>preserveAspectRatio</tt> attribute on the <tt>&lt;svg&gt;</tt> and <tt>&lt;image&gt;</tt> tags
52
84
 
53
85
  - transform methods: <tt>translate</tt>, <tt>rotate</tt>, <tt>scale</tt>, <tt>matrix</tt>
54
86
 
55
87
  - colors: HTML standard names, <tt>#xxx</tt>, <tt>#xxxxxx</tt>, <tt>rgb(1, 2, 3)</tt>, <tt>rgb(1%, 2%, 3%)</tt>
56
88
 
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>
89
+ - 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
90
 
59
- - fonts: generic CSS fonts, built in PDF fonts, and any TTF fonts in your fonts path
91
+ - fonts: generic CSS fonts, built-in PDF fonts, and any TTF fonts in your fonts path
60
92
 
61
93
  ## CSS
62
94
 
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.
95
+ 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
96
 
66
97
  ## Not supported
67
98
 
68
- prawn-svg does not support external references, measurements in <tt>en</tt> or <tt>em</tt>, sub-viewports, gradients/patterns or markers.
99
+ prawn-svg does not support external <tt>url()</tt> references, measurements in <tt>en</tt> or <tt>em</tt>, sub-viewports, radial gradients, patterns or markers.
69
100
 
70
101
  ## Configuration
71
102
 
@@ -75,7 +106,7 @@ By default, prawn-svg has a fonts path of <tt>["/Library/Fonts", "/System/Librar
75
106
  Mac OS X and Debian Linux users. You can add to the font path:
76
107
 
77
108
  ```ruby
78
- Prawn::Svg::Interface.font_path << "/my/font/directory"
109
+ Prawn::SVG::FontRegistry.font_path << "/my/font/directory"
79
110
  ```
80
111
 
81
112
 
@@ -0,0 +1,17 @@
1
+ module Prawn::SVG::Attributes::ClipPath
2
+ def parse_clip_path_attribute_and_call
3
+ return unless clip_path = attributes['clip-path']
4
+
5
+ if (matches = clip_path.strip.match(/\Aurl\(#(.*)\)\z/)).nil?
6
+ document.warnings << "Only clip-path attributes with the form 'url(#xxx)' are supported"
7
+ elsif (clip_path_element = @document.elements_by_id[matches[1]]).nil?
8
+ document.warnings << "clip-path ID '#{matches[1]}' not defined"
9
+ elsif clip_path_element.source.name != "clipPath"
10
+ document.warnings << "clip-path ID '#{matches[1]}' does not point to a clipPath tag"
11
+ else
12
+ add_call_and_enter 'save_graphics_state'
13
+ add_calls_from_element clip_path_element
14
+ add_call "clip"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ module Prawn::SVG::Attributes::Color
2
+ def parse_color_attribute
3
+ state.color = attributes['color'] if attributes['color']
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Prawn::SVG::Attributes::Display
2
+ def parse_display_attribute
3
+ state.display = attributes['display'].strip if attributes['display']
4
+ end
5
+ end
@@ -0,0 +1,38 @@
1
+ module Prawn::SVG::Attributes::Font
2
+ def parse_font_attributes_and_call
3
+ if size = attributes['font-size']
4
+ state.font_size = size.to_f
5
+ end
6
+ if weight = attributes['font-weight']
7
+ font_updated = true
8
+ state.font_weight = Prawn::SVG::Font.weight_for_css_font_weight(weight)
9
+ end
10
+ if style = attributes['font-style']
11
+ font_updated = true
12
+ state.font_style = style == 'italic' ? :italic : nil
13
+ end
14
+ if (family = attributes['font-family']) && family.strip != ""
15
+ font_updated = true
16
+ state.font_family = family
17
+ end
18
+ if (anchor = attributes['text-anchor'])
19
+ state.text_anchor = anchor
20
+ end
21
+
22
+ if state.font_family && font_updated
23
+ usable_font_families = [state.font_family, document.fallback_font_name]
24
+
25
+ font_used = usable_font_families.compact.detect do |name|
26
+ if font = document.font_registry.load(name, state.font_weight, state.font_style)
27
+ state.font_subfamily = font.subfamily
28
+ add_call_and_enter 'font', font.name, :style => state.font_subfamily
29
+ true
30
+ end
31
+ end
32
+
33
+ if font_used.nil?
34
+ warnings << "Font family '#{state.font_family}' style '#{state.font_style || 'normal'}' is not a known font, and the fallback font could not be found."
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,15 @@
1
+ module Prawn::SVG::Attributes::Opacity
2
+ def parse_opacity_attributes_and_call
3
+ # We can't do nested opacities quite like the SVG requires, but this is close enough.
4
+ fill_opacity = stroke_opacity = clamp(attributes['opacity'].to_f, 0, 1) if attributes['opacity']
5
+ fill_opacity = clamp(attributes['fill-opacity'].to_f, 0, 1) if attributes['fill-opacity']
6
+ stroke_opacity = clamp(attributes['stroke-opacity'].to_f, 0, 1) if attributes['stroke-opacity']
7
+
8
+ if fill_opacity || stroke_opacity
9
+ state.fill_opacity *= fill_opacity || 1
10
+ state.stroke_opacity *= stroke_opacity || 1
11
+
12
+ add_call_and_enter 'transparent', state.fill_opacity, state.stroke_opacity
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,35 @@
1
+ module Prawn::SVG::Attributes::Stroke
2
+ CAP_STYLE_TRANSLATIONS = {"butt" => :butt, "round" => :round, "square" => :projecting_square}
3
+
4
+ def parse_stroke_attributes_and_call
5
+ if width = attributes['stroke-width']
6
+ add_call('line_width', distance(width))
7
+ end
8
+
9
+ if (linecap = attribute_value_as_keyword('stroke-linecap')) && linecap != 'inherit'
10
+ add_call('cap_style', CAP_STYLE_TRANSLATIONS.fetch(linecap, :butt))
11
+ end
12
+
13
+ if dasharray = attribute_value_as_keyword('stroke-dasharray')
14
+ case dasharray
15
+ when 'inherit'
16
+ # don't do anything
17
+ when 'none'
18
+ add_call('undash')
19
+ else
20
+ array = dasharray.split(Prawn::SVG::Elements::COMMA_WSP_REGEXP)
21
+ array *= 2 if array.length % 2 == 1
22
+ number_array = array.map {|value| distance(value)}
23
+
24
+ if number_array.any? {|number| number < 0}
25
+ @document.warnings << "stroke-dasharray cannot have negative numbers; treating as 'none'"
26
+ add_call('undash')
27
+ elsif number_array.inject(0) {|a, b| a + b} == 0
28
+ add_call('undash')
29
+ else
30
+ add_call('dash', number_array)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,50 @@
1
+ module Prawn::SVG::Attributes::Transform
2
+ def parse_transform_attribute_and_call
3
+ return unless transform = attributes['transform']
4
+
5
+ parse_css_method_calls(transform).each do |name, arguments|
6
+ case name
7
+ when 'translate'
8
+ x, y = arguments
9
+ add_call_and_enter name, distance(x.to_f, :x), -distance(y.to_f, :y)
10
+
11
+ when 'rotate'
12
+ r, x, y = arguments.collect {|a| a.to_f}
13
+ case arguments.length
14
+ when 1
15
+ add_call_and_enter name, -r, :origin => [0, y('0')]
16
+ when 3
17
+ add_call_and_enter name, -r, :origin => [x(x), y(y)]
18
+ else
19
+ warnings << "transform 'rotate' must have either one or three arguments"
20
+ end
21
+
22
+ when 'scale'
23
+ x_scale = arguments[0].to_f
24
+ y_scale = (arguments[1] || x_scale).to_f
25
+ add_call_and_enter "transformation_matrix", x_scale, 0, 0, y_scale, 0, 0
26
+
27
+ when 'matrix'
28
+ if arguments.length != 6
29
+ warnings << "transform 'matrix' must have six arguments"
30
+ else
31
+ a, b, c, d, e, f = arguments.collect {|argument| argument.to_f}
32
+ add_call_and_enter "transformation_matrix", a, -b, -c, d, distance(e, :x), -distance(f, :y)
33
+ end
34
+
35
+ else
36
+ warnings << "Unknown transformation '#{name}'; ignoring"
37
+ end
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def parse_css_method_calls(string)
44
+ string.scan(/\s*(\w+)\(([^)]+)\)\s*/).collect do |call|
45
+ name, argument_string = call
46
+ arguments = argument_string.strip.split(/\s*[,\s]\s*/)
47
+ [name, arguments]
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,6 @@
1
+ module Prawn::SVG::Attributes
2
+ end
3
+
4
+ %w(transform opacity clip_path stroke font display color).each do |name|
5
+ require "prawn/svg/attributes/#{name}"
6
+ end
@@ -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,99 @@
1
+ module Prawn::SVG::Calculators
2
+ class DocumentSizing
3
+ DEFAULT_WIDTH = 300
4
+ DEFAULT_HEIGHT = 150
5
+ DEFAULT_ASPECT_RATIO = "xMidYMid meet"
6
+
7
+ attr_writer :document_width, :document_height
8
+ attr_writer :view_box, :preserve_aspect_ratio
9
+
10
+ attr_reader :bounds
11
+ attr_reader :x_offset, :y_offset, :x_scale, :y_scale
12
+ attr_reader :viewport_width, :viewport_height, :viewport_diagonal, :output_width, :output_height
13
+
14
+ def initialize(bounds, attributes = nil)
15
+ @bounds = bounds
16
+ set_from_attributes(attributes) if attributes
17
+ end
18
+
19
+ def set_from_attributes(attributes)
20
+ @document_width = attributes['width']
21
+ @document_height = attributes['height']
22
+ @view_box = attributes['viewBox']
23
+ @preserve_aspect_ratio = attributes['preserveAspectRatio'] || DEFAULT_ASPECT_RATIO
24
+ end
25
+
26
+ def calculate
27
+ @x_offset = @y_offset = 0
28
+ @x_scale = @y_scale = 1
29
+
30
+ container_width = @requested_width || @bounds[0]
31
+ container_height = @requested_height || @bounds[1]
32
+
33
+ @output_width = Pixels.to_pixels(@document_width || @requested_width, container_width)
34
+ @output_height = Pixels.to_pixels(@document_height || @requested_height, container_height)
35
+
36
+ if @view_box
37
+ values = @view_box.strip.split(Prawn::SVG::Elements::COMMA_WSP_REGEXP)
38
+ @x_offset, @y_offset, @viewport_width, @viewport_height = values.map {|value| value.to_f}
39
+ @x_offset = -@x_offset
40
+
41
+ if @viewport_width > 0 && @viewport_height > 0
42
+ @output_width ||= container_width
43
+ @output_height ||= @output_width * @viewport_height / @viewport_width
44
+
45
+ aspect = AspectRatio.new(@preserve_aspect_ratio, [@output_width, @output_height], [@viewport_width, @viewport_height])
46
+ @x_scale = aspect.width / @viewport_width
47
+ @y_scale = aspect.height / @viewport_height
48
+ @x_offset -= aspect.x / @x_scale
49
+ @y_offset -= aspect.y / @y_scale
50
+ end
51
+ else
52
+ @output_width ||= Pixels.to_pixels(DEFAULT_WIDTH, container_width)
53
+ @output_height ||= Pixels.to_pixels(DEFAULT_HEIGHT, container_height)
54
+
55
+ @viewport_width = @output_width
56
+ @viewport_height = @output_height
57
+ end
58
+
59
+ return if invalid?
60
+
61
+ # SVG 1.1 section 7.10
62
+ @viewport_diagonal = Math.sqrt(@viewport_width**2 + @viewport_height**2) / Math.sqrt(2)
63
+
64
+ if @requested_width
65
+ scale = @requested_width / @output_width
66
+ @output_width = @requested_width
67
+ @output_height *= scale
68
+ @x_scale *= scale
69
+ @y_scale *= scale
70
+
71
+ elsif @requested_height
72
+ scale = @requested_height / @output_height
73
+ @output_height = @requested_height
74
+ @output_width *= scale
75
+ @x_scale *= scale
76
+ @y_scale *= scale
77
+ end
78
+
79
+ self
80
+ end
81
+
82
+ def invalid?
83
+ @viewport_width <= 0 ||
84
+ @viewport_height <= 0 ||
85
+ @output_width <= 0 ||
86
+ @output_height <= 0 ||
87
+ (@requested_width && @requested_width <= 0) ||
88
+ (@requested_height && @requested_height <= 0)
89
+ end
90
+
91
+ def requested_width=(value)
92
+ @requested_width = (value.to_f if value)
93
+ end
94
+
95
+ def requested_height=(value)
96
+ @requested_height = (value.to_f if value)
97
+ end
98
+ end
99
+ 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