prawn-svg 0.22.1 → 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/README.md +29 -13
- data/lib/prawn-svg.rb +7 -0
- data/lib/prawn/svg/attributes.rb +1 -1
- data/lib/prawn/svg/attributes/color.rb +5 -0
- data/lib/prawn/svg/attributes/display.rb +1 -1
- data/lib/prawn/svg/attributes/font.rb +11 -11
- data/lib/prawn/svg/attributes/opacity.rb +3 -3
- data/lib/prawn/svg/css.rb +40 -0
- data/lib/prawn/svg/document.rb +23 -8
- data/lib/prawn/svg/elements/base.rb +20 -10
- data/lib/prawn/svg/elements/container.rb +1 -1
- data/lib/prawn/svg/elements/gradient.rb +7 -4
- data/lib/prawn/svg/elements/image.rb +2 -6
- data/lib/prawn/svg/elements/root.rb +4 -0
- data/lib/prawn/svg/elements/text.rb +14 -14
- data/lib/prawn/svg/font.rb +9 -91
- data/lib/prawn/svg/font_registry.rb +73 -0
- data/lib/prawn/svg/interface.rb +9 -22
- 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 +35 -23
- data/lib/prawn/svg/version.rb +1 -1
- data/spec/integration_spec.rb +7 -6
- data/spec/prawn/svg/attributes/font_spec.rb +8 -5
- data/spec/prawn/svg/css_spec.rb +24 -0
- data/spec/prawn/svg/document_spec.rb +35 -10
- data/spec/prawn/svg/elements/base_spec.rb +32 -10
- data/spec/prawn/svg/elements/gradient_spec.rb +1 -1
- data/spec/prawn/svg/elements/text_spec.rb +4 -4
- data/spec/prawn/svg/font_registry_spec.rb +54 -0
- data/spec/prawn/svg/font_spec.rb +0 -27
- 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 +90 -24
- data/spec/sample_svg/image03.svg +30 -0
- data/spec/sample_svg/tspan03-cc.svg +21 -0
- data/spec/sample_ttf/OpenSans-SemiboldItalic.ttf +0 -0
- data/spec/spec_helper.rb +17 -2
- metadata +28 -2
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/README.md
CHANGED
@@ -7,7 +7,7 @@ An SVG renderer for the Prawn PDF library.
|
|
7
7
|
|
8
8
|
This will take an SVG file as input and render it into your PDF. Find out more about the Prawn PDF library at:
|
9
9
|
|
10
|
-
http://
|
10
|
+
http://github.com/prawnpdf/prawn
|
11
11
|
|
12
12
|
prawn-svg is compatible with all versions of Prawn from 0.11.1 onwards, including the 1.x and 2.x series.
|
13
13
|
The minimum Ruby version required is 2.0.0.
|
@@ -15,23 +15,39 @@ The minimum Ruby version required is 2.0.0.
|
|
15
15
|
## Using prawn-svg
|
16
16
|
|
17
17
|
```ruby
|
18
|
-
Prawn::Document.generate("
|
19
|
-
svg
|
18
|
+
Prawn::Document.generate("test.pdf") do
|
19
|
+
svg '<svg><rect width="100" height="100" fill="red"></rect></svg>'
|
20
20
|
end
|
21
21
|
```
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
of <tt>:top</tt>, <tt>:center</tt>, <tt>:bottom</tt> or a number to specify its Y position too.
|
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:
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
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.
|
30
37
|
|
31
|
-
|
38
|
+
## Examples
|
32
39
|
|
33
|
-
|
34
|
-
|
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
|
+
```
|
35
51
|
|
36
52
|
## Supported features
|
37
53
|
|
@@ -90,7 +106,7 @@ By default, prawn-svg has a fonts path of <tt>["/Library/Fonts", "/System/Librar
|
|
90
106
|
Mac OS X and Debian Linux users. You can add to the font path:
|
91
107
|
|
92
108
|
```ruby
|
93
|
-
Prawn::SVG::
|
109
|
+
Prawn::SVG::FontRegistry.font_path << "/my/font/directory"
|
94
110
|
```
|
95
111
|
|
96
112
|
|
data/lib/prawn-svg.rb
CHANGED
@@ -3,17 +3,24 @@ require 'rexml/document'
|
|
3
3
|
require 'prawn'
|
4
4
|
require 'prawn/svg/version'
|
5
5
|
|
6
|
+
require 'prawn/svg/font_registry'
|
6
7
|
require 'prawn/svg/calculators/aspect_ratio'
|
7
8
|
require 'prawn/svg/calculators/document_sizing'
|
8
9
|
require 'prawn/svg/calculators/pixels'
|
9
10
|
require 'prawn/svg/url_loader'
|
11
|
+
require 'prawn/svg/loaders/data'
|
12
|
+
require 'prawn/svg/loaders/file'
|
13
|
+
require 'prawn/svg/loaders/web'
|
10
14
|
require 'prawn/svg/color'
|
11
15
|
require 'prawn/svg/attributes'
|
12
16
|
require 'prawn/svg/elements'
|
13
17
|
require 'prawn/svg/extension'
|
14
18
|
require 'prawn/svg/interface'
|
19
|
+
require 'prawn/svg/css'
|
20
|
+
require 'prawn/svg/ttf'
|
15
21
|
require 'prawn/svg/font'
|
16
22
|
require 'prawn/svg/document'
|
23
|
+
require 'prawn/svg/state'
|
17
24
|
|
18
25
|
module Prawn
|
19
26
|
Svg = SVG # backwards compatibility
|
data/lib/prawn/svg/attributes.rb
CHANGED
@@ -1,37 +1,37 @@
|
|
1
1
|
module Prawn::SVG::Attributes::Font
|
2
2
|
def parse_font_attributes_and_call
|
3
3
|
if size = attributes['font-size']
|
4
|
-
|
4
|
+
state.font_size = size.to_f
|
5
5
|
end
|
6
6
|
if weight = attributes['font-weight']
|
7
7
|
font_updated = true
|
8
|
-
|
8
|
+
state.font_weight = Prawn::SVG::Font.weight_for_css_font_weight(weight)
|
9
9
|
end
|
10
10
|
if style = attributes['font-style']
|
11
11
|
font_updated = true
|
12
|
-
|
12
|
+
state.font_style = style == 'italic' ? :italic : nil
|
13
13
|
end
|
14
14
|
if (family = attributes['font-family']) && family.strip != ""
|
15
15
|
font_updated = true
|
16
|
-
|
16
|
+
state.font_family = family
|
17
17
|
end
|
18
18
|
if (anchor = attributes['text-anchor'])
|
19
|
-
|
19
|
+
state.text_anchor = anchor
|
20
20
|
end
|
21
21
|
|
22
|
-
if
|
23
|
-
usable_font_families = [
|
22
|
+
if state.font_family && font_updated
|
23
|
+
usable_font_families = [state.font_family, document.fallback_font_name]
|
24
24
|
|
25
25
|
font_used = usable_font_families.compact.detect do |name|
|
26
|
-
if font =
|
27
|
-
|
28
|
-
add_call_and_enter 'font', font.name, :style =>
|
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
29
|
true
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
33
|
if font_used.nil?
|
34
|
-
warnings << "Font family '#{
|
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
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -6,10 +6,10 @@ module Prawn::SVG::Attributes::Opacity
|
|
6
6
|
stroke_opacity = clamp(attributes['stroke-opacity'].to_f, 0, 1) if attributes['stroke-opacity']
|
7
7
|
|
8
8
|
if fill_opacity || stroke_opacity
|
9
|
-
state
|
10
|
-
state
|
9
|
+
state.fill_opacity *= fill_opacity || 1
|
10
|
+
state.stroke_opacity *= stroke_opacity || 1
|
11
11
|
|
12
|
-
add_call_and_enter 'transparent', state
|
12
|
+
add_call_and_enter 'transparent', state.fill_opacity, state.stroke_opacity
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Prawn::SVG::CSS
|
2
|
+
class << self
|
3
|
+
def parse_font_family_string(string)
|
4
|
+
in_quote = nil
|
5
|
+
in_escape = false
|
6
|
+
current = nil
|
7
|
+
fonts = []
|
8
|
+
|
9
|
+
string.chars.each do |char|
|
10
|
+
if in_escape
|
11
|
+
in_escape = false
|
12
|
+
if current.nil?
|
13
|
+
current = char
|
14
|
+
fonts << current
|
15
|
+
else
|
16
|
+
current << char
|
17
|
+
end
|
18
|
+
elsif char == ',' && in_quote.nil?
|
19
|
+
current = nil
|
20
|
+
elsif char == in_quote
|
21
|
+
in_quote = nil
|
22
|
+
elsif in_quote.nil? && (char == '"' || char == "'")
|
23
|
+
in_quote = char
|
24
|
+
elsif char == '\\'
|
25
|
+
in_escape = true
|
26
|
+
elsif current.nil?
|
27
|
+
if char.match(/\s/).nil?
|
28
|
+
current = char
|
29
|
+
fonts << current
|
30
|
+
end
|
31
|
+
else
|
32
|
+
current << char
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
fonts.map(&:rstrip)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
data/lib/prawn/svg/document.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
class Prawn::SVG::Document
|
2
|
+
Error = Class.new(StandardError)
|
3
|
+
InvalidSVGData = Class.new(Error)
|
4
|
+
|
2
5
|
begin
|
3
6
|
require 'css_parser'
|
4
7
|
CSS_PARSER_LOADED = true
|
@@ -10,23 +13,39 @@ class Prawn::SVG::Document
|
|
10
13
|
|
11
14
|
# An +Array+ of warnings that occurred while parsing the SVG data.
|
12
15
|
attr_reader :warnings
|
13
|
-
attr_writer :url_cache
|
14
16
|
|
15
17
|
attr_reader :root,
|
16
18
|
:sizing,
|
17
|
-
:
|
19
|
+
:fallback_font_name,
|
20
|
+
:font_registry,
|
21
|
+
:url_loader,
|
18
22
|
:css_parser, :elements_by_id, :gradients
|
19
23
|
|
20
|
-
def initialize(data, bounds, options)
|
24
|
+
def initialize(data, bounds, options, font_registry: nil)
|
21
25
|
@css_parser = CssParser::Parser.new if CSS_PARSER_LOADED
|
22
26
|
|
23
27
|
@root = REXML::Document.new(data).root
|
28
|
+
|
29
|
+
if @root.nil?
|
30
|
+
if data.respond_to?(:end_with?) && data.end_with?(".svg")
|
31
|
+
raise InvalidSVGData, "The data supplied is not a valid SVG document. It looks like you've supplied a filename instead; use IO.read(filename) to get the data before you pass it to prawn-svg."
|
32
|
+
else
|
33
|
+
raise InvalidSVGData, "The data supplied is not a valid SVG document."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
24
37
|
@warnings = []
|
25
38
|
@options = options
|
26
39
|
@elements_by_id = {}
|
27
40
|
@gradients = {}
|
28
|
-
@cache_images = options[:cache_images]
|
29
41
|
@fallback_font_name = options.fetch(:fallback_font_name, DEFAULT_FALLBACK_FONT_NAME)
|
42
|
+
@font_registry = font_registry
|
43
|
+
|
44
|
+
@url_loader = Prawn::SVG::UrlLoader.new(
|
45
|
+
enable_cache: options[:cache_images],
|
46
|
+
enable_web: options.fetch(:enable_web_requests, true),
|
47
|
+
enable_file_with_root: options[:enable_file_requests_with_root]
|
48
|
+
)
|
30
49
|
|
31
50
|
@sizing = Prawn::SVG::Calculators::DocumentSizing.new(bounds, @root.attributes)
|
32
51
|
sizing.requested_width = options[:width]
|
@@ -53,8 +72,4 @@ class Prawn::SVG::Document
|
|
53
72
|
def points(value, axis = nil)
|
54
73
|
Prawn::SVG::Calculators::Pixels.to_pixels(value, @axis_to_size.fetch(axis, sizing.viewport_diagonal))
|
55
74
|
end
|
56
|
-
|
57
|
-
def url_loader
|
58
|
-
@url_loader ||= Prawn::SVG::UrlLoader.new(:enable_cache => cache_images)
|
59
|
-
end
|
60
75
|
end
|
@@ -7,6 +7,7 @@ class Prawn::SVG::Elements::Base
|
|
7
7
|
include Prawn::SVG::Attributes::Stroke
|
8
8
|
include Prawn::SVG::Attributes::Font
|
9
9
|
include Prawn::SVG::Attributes::Display
|
10
|
+
include Prawn::SVG::Attributes::Color
|
10
11
|
|
11
12
|
COMMA_WSP_REGEXP = Prawn::SVG::Elements::COMMA_WSP_REGEXP
|
12
13
|
|
@@ -33,6 +34,7 @@ class Prawn::SVG::Elements::Base
|
|
33
34
|
|
34
35
|
def process
|
35
36
|
combine_attributes_and_style_declarations
|
37
|
+
parse_standard_attributes
|
36
38
|
parse
|
37
39
|
|
38
40
|
apply_calls_from_standard_attributes
|
@@ -85,7 +87,7 @@ class Prawn::SVG::Elements::Base
|
|
85
87
|
if element_class = Prawn::SVG::Elements::TAG_CLASS_MAPPING[elem.name.to_sym]
|
86
88
|
add_call "save"
|
87
89
|
|
88
|
-
child = element_class.new(@document, elem, @calls,
|
90
|
+
child = element_class.new(@document, elem, @calls, state.dup)
|
89
91
|
child.process
|
90
92
|
|
91
93
|
add_call "restore"
|
@@ -107,8 +109,8 @@ class Prawn::SVG::Elements::Base
|
|
107
109
|
end
|
108
110
|
|
109
111
|
def apply_drawing_call(draw_types)
|
110
|
-
if
|
111
|
-
if draw_types.empty? ||
|
112
|
+
if !state.disable_drawing && !container?
|
113
|
+
if draw_types.empty? || state.display == "none"
|
112
114
|
add_call_and_enter("end_path")
|
113
115
|
else
|
114
116
|
add_call_and_enter(draw_types.join("_and_"))
|
@@ -116,30 +118,38 @@ class Prawn::SVG::Elements::Base
|
|
116
118
|
end
|
117
119
|
end
|
118
120
|
|
121
|
+
def parse_standard_attributes
|
122
|
+
parse_color_attribute
|
123
|
+
end
|
124
|
+
|
119
125
|
def parse_fill_and_stroke_attributes_and_call
|
120
126
|
["fill", "stroke"].select do |type|
|
121
127
|
case keyword = attribute_value_as_keyword(type)
|
122
128
|
when nil
|
123
129
|
when 'inherit'
|
124
130
|
when 'none'
|
125
|
-
state
|
131
|
+
state.disable_draw_type(type)
|
126
132
|
else
|
127
|
-
state
|
128
|
-
|
129
|
-
|
133
|
+
state.disable_draw_type(type)
|
134
|
+
|
135
|
+
if keyword == 'currentcolor'
|
136
|
+
color = state.color
|
137
|
+
else
|
138
|
+
color = @attributes[type]
|
139
|
+
end
|
130
140
|
|
131
141
|
results = Prawn::SVG::Color.parse(color, document.gradients)
|
132
142
|
|
133
143
|
results.each do |result|
|
134
144
|
case result
|
135
145
|
when Prawn::SVG::Color::Hex
|
136
|
-
state
|
146
|
+
state.enable_draw_type(type)
|
137
147
|
add_call "#{type}_color", result.value
|
138
148
|
break
|
139
149
|
when Prawn::SVG::Elements::Gradient
|
140
150
|
arguments = result.gradient_arguments(self)
|
141
151
|
if arguments
|
142
|
-
state
|
152
|
+
state.enable_draw_type(type)
|
143
153
|
add_call "#{type}_gradient", **arguments
|
144
154
|
break
|
145
155
|
end
|
@@ -147,7 +157,7 @@ class Prawn::SVG::Elements::Base
|
|
147
157
|
end
|
148
158
|
end
|
149
159
|
|
150
|
-
state
|
160
|
+
state.draw_type(type)
|
151
161
|
end
|
152
162
|
end
|
153
163
|
|
@@ -42,9 +42,12 @@ class Prawn::SVG::Elements::Gradient < Prawn::SVG::Elements::Base
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def assert_compatible_prawn_version
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
# At the moment, the patch required for this functionality to work in prawn has not been merged.
|
46
|
+
raise SkipElementError, "We are unfortunately still waiting on the Prawn project to merge a pull request that is required for this feature to correctly function"
|
47
|
+
|
48
|
+
# if (Prawn::VERSION.split(".").map(&:to_i) <=> [2, 0, 4]) == -1
|
49
|
+
# raise SkipElementError, "Prawn 2.0.4+ must be used if you'd like prawn-svg to render gradients"
|
50
|
+
# end
|
48
51
|
end
|
49
52
|
|
50
53
|
def load_gradient_configuration
|
@@ -80,7 +83,7 @@ class Prawn::SVG::Elements::Gradient < Prawn::SVG::Elements::Base
|
|
80
83
|
|
81
84
|
def load_stops
|
82
85
|
stop_elements = source.elements.map do |child|
|
83
|
-
element = Prawn::SVG::Elements::Base.new(document, child, [],
|
86
|
+
element = Prawn::SVG::Elements::Base.new(document, child, [], Prawn::SVG::State.new)
|
84
87
|
element.process
|
85
88
|
element
|
86
89
|
end.select do |element|
|
@@ -13,17 +13,13 @@ class Prawn::SVG::Elements::Image < Prawn::SVG::Elements::Base
|
|
13
13
|
def parse
|
14
14
|
require_attributes 'width', 'height'
|
15
15
|
|
16
|
-
raise SkipElementQuietly if state
|
16
|
+
raise SkipElementQuietly if state.display == "none"
|
17
17
|
|
18
18
|
@url = attributes['xlink:href'] || attributes['href']
|
19
19
|
if @url.nil?
|
20
20
|
raise SkipElementError, "image tag must have an xlink:href"
|
21
21
|
end
|
22
22
|
|
23
|
-
if !@document.url_loader.valid?(@url)
|
24
|
-
raise SkipElementError, "image tag xlink:href attribute must use http, https or data scheme"
|
25
|
-
end
|
26
|
-
|
27
23
|
x = x(attributes['x'] || 0)
|
28
24
|
y = y(attributes['y'] || 0)
|
29
25
|
width = distance(attributes['width'])
|
@@ -34,7 +30,7 @@ class Prawn::SVG::Elements::Image < Prawn::SVG::Elements::Base
|
|
34
30
|
|
35
31
|
@image = begin
|
36
32
|
@document.url_loader.load(@url)
|
37
|
-
rescue => e
|
33
|
+
rescue Prawn::SVG::UrlLoader::Error => e
|
38
34
|
raise SkipElementError, "Error retrieving URL #{@url}: #{e.message}"
|
39
35
|
end
|
40
36
|
|