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.
- checksums.yaml +4 -4
- data/.travis.yml +6 -0
- data/README.md +51 -20
- data/lib/prawn/svg/attributes/clip_path.rb +17 -0
- data/lib/prawn/svg/attributes/color.rb +5 -0
- data/lib/prawn/svg/attributes/display.rb +5 -0
- data/lib/prawn/svg/attributes/font.rb +38 -0
- data/lib/prawn/svg/attributes/opacity.rb +15 -0
- data/lib/prawn/svg/attributes/stroke.rb +35 -0
- data/lib/prawn/svg/attributes/transform.rb +50 -0
- data/lib/prawn/svg/attributes.rb +6 -0
- data/lib/prawn/svg/calculators/aspect_ratio.rb +58 -0
- data/lib/prawn/svg/calculators/document_sizing.rb +99 -0
- data/lib/prawn/svg/calculators/pixels.rb +21 -0
- data/lib/prawn/svg/color.rb +197 -12
- data/lib/prawn/svg/css.rb +40 -0
- data/lib/prawn/svg/document.rb +37 -48
- data/lib/prawn/svg/elements/base.rb +238 -0
- data/lib/prawn/svg/elements/circle.rb +25 -0
- data/lib/prawn/svg/elements/container.rb +15 -0
- data/lib/prawn/svg/elements/ellipse.rb +23 -0
- data/lib/prawn/svg/elements/gradient.rb +120 -0
- data/lib/prawn/svg/elements/ignored.rb +5 -0
- data/lib/prawn/svg/elements/image.rb +81 -0
- data/lib/prawn/svg/elements/line.rb +16 -0
- data/lib/prawn/svg/elements/path.rb +405 -0
- data/lib/prawn/svg/elements/polygon.rb +17 -0
- data/lib/prawn/svg/elements/polyline.rb +22 -0
- data/lib/prawn/svg/elements/rect.rb +33 -0
- data/lib/prawn/svg/elements/root.rb +13 -0
- data/lib/prawn/svg/elements/style.rb +10 -0
- data/lib/prawn/svg/elements/text.rb +87 -0
- data/lib/prawn/svg/elements/use.rb +29 -0
- data/lib/prawn/svg/elements.rb +33 -0
- data/lib/prawn/svg/extension.rb +4 -4
- data/lib/prawn/svg/font.rb +10 -92
- data/lib/prawn/svg/font_registry.rb +73 -0
- data/lib/prawn/svg/interface.rb +91 -31
- data/lib/prawn/svg/loaders/data.rb +18 -0
- data/lib/prawn/svg/loaders/file.rb +66 -0
- data/lib/prawn/svg/loaders/web.rb +28 -0
- data/lib/prawn/svg/state.rb +39 -0
- data/lib/prawn/svg/ttf.rb +61 -0
- data/lib/prawn/svg/url_loader.rb +46 -0
- data/lib/prawn/svg/version.rb +2 -2
- data/lib/prawn-svg.rb +20 -6
- data/prawn-svg.gemspec +8 -5
- data/spec/integration_spec.rb +141 -0
- data/spec/prawn/svg/attributes/font_spec.rb +52 -0
- data/spec/prawn/svg/attributes/transform_spec.rb +56 -0
- data/spec/prawn/svg/calculators/aspect_ratio_spec.rb +95 -0
- data/spec/prawn/svg/calculators/document_sizing_spec.rb +128 -0
- data/spec/prawn/svg/color_spec.rb +43 -8
- data/spec/prawn/svg/css_spec.rb +24 -0
- data/spec/prawn/svg/document_spec.rb +48 -19
- data/spec/prawn/svg/elements/base_spec.rb +147 -0
- data/spec/prawn/svg/elements/gradient_spec.rb +61 -0
- data/spec/prawn/svg/elements/path_spec.rb +123 -0
- data/spec/prawn/svg/elements/style_spec.rb +23 -0
- data/spec/prawn/svg/elements/text_spec.rb +61 -0
- data/spec/prawn/svg/font_registry_spec.rb +54 -0
- data/spec/prawn/svg/font_spec.rb +1 -28
- data/spec/prawn/svg/interface_spec.rb +94 -0
- data/spec/prawn/svg/loaders/data_spec.rb +55 -0
- data/spec/prawn/svg/loaders/file_spec.rb +84 -0
- data/spec/prawn/svg/loaders/web_spec.rb +37 -0
- data/spec/prawn/svg/ttf_spec.rb +32 -0
- data/spec/prawn/svg/url_loader_spec.rb +112 -0
- data/spec/sample_images/mushroom-long.jpg +0 -0
- data/spec/sample_images/mushroom-wide.jpg +0 -0
- data/spec/sample_svg/cap_styles.svg +13 -0
- data/spec/sample_svg/display_none.svg +13 -0
- data/spec/sample_svg/gistfile1.svg +36 -0
- data/spec/sample_svg/gradients.svg +40 -0
- data/spec/sample_svg/hidden_paths.svg +6 -0
- data/spec/sample_svg/image01.svg +31 -31
- data/spec/sample_svg/image03.svg +30 -0
- data/spec/sample_svg/negminy.svg +25 -0
- data/spec/sample_svg/no_width_or_height.svg +4 -0
- data/spec/sample_svg/path.svg +5 -0
- data/spec/sample_svg/pie_piece.svg +7 -0
- data/spec/sample_svg/preserve-space.svg +19 -0
- data/spec/sample_svg/rect02.svg +8 -11
- data/spec/sample_svg/tspan03-cc.svg +21 -0
- data/spec/sample_svg/viewbox.svg +4 -0
- data/spec/sample_svg/viewport.svg +23 -0
- data/spec/sample_ttf/OpenSans-SemiboldItalic.ttf +0 -0
- data/spec/spec_helper.rb +24 -2
- metadata +150 -36
- data/lib/prawn/svg/element.rb +0 -237
- data/lib/prawn/svg/parser/image.rb +0 -134
- data/lib/prawn/svg/parser/path.rb +0 -374
- data/lib/prawn/svg/parser/text.rb +0 -66
- data/lib/prawn/svg/parser.rb +0 -233
- data/spec/lib/parser_spec.rb +0 -55
- data/spec/lib/path_spec.rb +0 -54
- data/spec/lib/svg_spec.rb +0 -47
- data/spec/prawn/svg/element_spec.rb +0 -36
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0455251b1d5e7c430660f51c4843d26adb8d9913
|
4
|
+
data.tar.gz: caf87db1d427b7b528b5ae09b826b4dddec7a8a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1068767350107317ce264382ead9432a8ca98118d82301a402ef5fb50f6da65bdc2f547101955911cd24cf291745dd3f7b55f51d2374c96b9442673833df638
|
7
|
+
data.tar.gz: 47b83cd0def9edaec474ec458fa01831801abefadeebe797840b64bd7b3b2f38ff869e135893bb16a9f23b6a9fea28ab46c9f843775fa1c3f72bcd11964d8b33
|
data/.travis.yml
ADDED
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://
|
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("
|
13
|
-
svg
|
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
|
-
|
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
|
-
|
20
|
-
|
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
|
-
|
38
|
+
## Examples
|
23
39
|
|
24
|
-
|
25
|
-
|
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
|
54
|
+
prawn-svg supports most but not all of the full SVG 1.1 specification. It currently supports:
|
30
55
|
|
31
56
|
- <tt><line></tt>, <tt><polyline></tt>, <tt><polygon></tt>, <tt><circle></tt> and <tt><ellipse></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><text></tt> and <tt><tspan></tt> with attributes
|
39
|
-
<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><svg></tt>, <tt><g></tt> and <tt><symbol></tt>
|
42
67
|
|
43
68
|
- <tt><use></tt>
|
44
69
|
|
45
|
-
- <tt><style></tt
|
70
|
+
- <tt><style></tt> plus <tt>id</tt>, <tt>class</tt> and <tt>style</tt> attributes (see CSS section below)
|
46
71
|
|
47
|
-
- <tt><image></tt> with <tt>http:</tt>, <tt>https:</tt> and <tt>data:image
|
72
|
+
- <tt><image></tt> with <tt>http:</tt>, <tt>https:</tt> and <tt>data:image/\*;base64</tt> schemes
|
48
73
|
|
49
74
|
- <tt><clipPath></tt>
|
50
75
|
|
51
|
-
-
|
76
|
+
- <tt><linearGradient></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><svg></tt> tag
|
82
|
+
|
83
|
+
- the <tt>preserveAspectRatio</tt> attribute on the <tt><svg></tt> and <tt><image></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
|
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
|
-
|
64
|
-
so do not expect too much of it.
|
95
|
+
prawn-svg uses the css_parser gem to parse CSS <tt><style></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
|
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::
|
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,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,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
|