prawn-svg 0.22.1 → 0.23.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.
- 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
|
|