prawn-svg 0.23.1 → 0.24.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 +3 -3
- data/README.md +15 -9
- data/lib/prawn-svg.rb +3 -0
- data/lib/prawn/svg/attributes.rb +1 -1
- data/lib/prawn/svg/attributes/clip_path.rb +6 -7
- data/lib/prawn/svg/attributes/opacity.rb +3 -3
- data/lib/prawn/svg/attributes/stroke.rb +6 -4
- data/lib/prawn/svg/calculators/arc_to_bezier_curve.rb +114 -0
- data/lib/prawn/svg/elements.rb +4 -1
- data/lib/prawn/svg/elements/base.rb +76 -69
- data/lib/prawn/svg/elements/container.rb +5 -6
- data/lib/prawn/svg/elements/gradient.rb +4 -4
- data/lib/prawn/svg/elements/image.rb +1 -1
- data/lib/prawn/svg/elements/line.rb +15 -7
- data/lib/prawn/svg/elements/marker.rb +72 -0
- data/lib/prawn/svg/elements/path.rb +23 -147
- data/lib/prawn/svg/elements/polygon.rb +14 -6
- data/lib/prawn/svg/elements/polyline.rb +12 -11
- data/lib/prawn/svg/elements/root.rb +3 -1
- data/lib/prawn/svg/elements/text.rb +38 -17
- data/lib/prawn/svg/font.rb +6 -6
- data/lib/prawn/svg/interface.rb +3 -0
- data/lib/prawn/svg/pathable.rb +130 -0
- data/lib/prawn/svg/properties.rb +122 -0
- data/lib/prawn/svg/state.rb +7 -29
- data/lib/prawn/svg/version.rb +1 -1
- data/spec/prawn/svg/elements/base_spec.rb +19 -32
- data/spec/prawn/svg/elements/line_spec.rb +37 -0
- data/spec/prawn/svg/elements/marker_spec.rb +90 -0
- data/spec/prawn/svg/elements/path_spec.rb +10 -10
- data/spec/prawn/svg/elements/polygon_spec.rb +49 -0
- data/spec/prawn/svg/elements/polyline_spec.rb +47 -0
- data/spec/prawn/svg/elements/style_spec.rb +1 -1
- data/spec/prawn/svg/elements/text_spec.rb +37 -5
- data/spec/prawn/svg/pathable_spec.rb +92 -0
- data/spec/prawn/svg/properties_spec.rb +186 -0
- data/spec/sample_svg/arrows.svg +73 -0
- data/spec/sample_svg/marker.svg +32 -0
- data/spec/sample_svg/polygon01.svg +25 -5
- metadata +23 -8
- data/lib/prawn/svg/attributes/color.rb +0 -5
- data/lib/prawn/svg/attributes/display.rb +0 -5
- data/lib/prawn/svg/attributes/font.rb +0 -38
- data/spec/prawn/svg/attributes/font_spec.rb +0 -52
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b7ff6138c2cf404469001e4d18217b5758af6c0
|
4
|
+
data.tar.gz: 15a9474c4bf2f479db7a1e167292c81037d17069
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a446a4b93bc4e590eb76ad93c3b3df2786a300a54c4fd0d28a6e2c1360ea7e7f5ff05ed7759e61cd723c6c16048639d2bc44f6f6be3126d43d59a3259b698465
|
7
|
+
data.tar.gz: e383d553e521457d5b248f6043086dcf0a9573060c50044e9d631dc5b5e2deff7ed0ad39aaae5c0291f9225608beb038fe488c90390a56a44042c1cd94da72e9
|
data/.travis.yml
CHANGED
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 document as input and render it into your PDF. Find out more about the Prawn PDF library at:
|
9
9
|
|
10
|
-
http://github.com/prawnpdf/prawn
|
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.
|
@@ -69,18 +69,24 @@ prawn-svg supports most but not all of the full SVG 1.1 specification. It curre
|
|
69
69
|
|
70
70
|
- <tt><style></tt> plus <tt>id</tt>, <tt>class</tt> and <tt>style</tt> attributes (see CSS section below)
|
71
71
|
|
72
|
-
- <tt><image></tt> with <tt>http:</tt>, <tt>https:</tt
|
72
|
+
- <tt><image></tt> with <tt>http:</tt>, <tt>https:</tt>, <tt>data:image/\*;base64</tt> and `file:` schemes
|
73
|
+
(`file:` is disabled by default for security reasons, see Options section above)
|
73
74
|
|
74
75
|
- <tt><clipPath></tt>
|
75
76
|
|
76
|
-
-
|
77
|
-
unimplemented.
|
77
|
+
- `<marker>`
|
78
78
|
|
79
|
-
-
|
79
|
+
- <tt><linearGradient></tt> is implemented but not currently working as we are waiting for a pull request to be accepted
|
80
|
+
into Prawn. (gradientTransform, spreadMethod and stop-opacity are unimplemented.)
|
80
81
|
|
81
|
-
-
|
82
|
+
- `<switch>` and `<foreignObject>`, although prawn-svg cannot handle any data that is not SVG so `<foreignObject>`
|
83
|
+
tags are always ignored.
|
82
84
|
|
83
|
-
-
|
85
|
+
- 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>, `marker-start`, `marker-mid`, `marker-end`
|
86
|
+
|
87
|
+
- the <tt>viewBox</tt> attribute on <tt><svg></tt> and `<marker>` tags
|
88
|
+
|
89
|
+
- the <tt>preserveAspectRatio</tt> attribute on <tt><svg></tt>, <tt><image></tt> and `<marker>` tags
|
84
90
|
|
85
91
|
- transform methods: <tt>translate</tt>, <tt>rotate</tt>, <tt>scale</tt>, <tt>matrix</tt>
|
86
92
|
|
@@ -92,11 +98,11 @@ prawn-svg supports most but not all of the full SVG 1.1 specification. It curre
|
|
92
98
|
|
93
99
|
## CSS
|
94
100
|
|
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.
|
101
|
+
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 by the gem.
|
96
102
|
|
97
103
|
## Not supported
|
98
104
|
|
99
|
-
prawn-svg does not support
|
105
|
+
prawn-svg does not support sub-viewports, radial gradients, or patterns.
|
100
106
|
|
101
107
|
## Configuration
|
102
108
|
|
data/lib/prawn-svg.rb
CHANGED
@@ -6,6 +6,7 @@ require 'prawn/svg/version'
|
|
6
6
|
require 'css_parser'
|
7
7
|
|
8
8
|
require 'prawn/svg/font_registry'
|
9
|
+
require 'prawn/svg/calculators/arc_to_bezier_curve'
|
9
10
|
require 'prawn/svg/calculators/aspect_ratio'
|
10
11
|
require 'prawn/svg/calculators/document_sizing'
|
11
12
|
require 'prawn/svg/calculators/pixels'
|
@@ -15,6 +16,8 @@ require 'prawn/svg/loaders/file'
|
|
15
16
|
require 'prawn/svg/loaders/web'
|
16
17
|
require 'prawn/svg/color'
|
17
18
|
require 'prawn/svg/attributes'
|
19
|
+
require 'prawn/svg/properties'
|
20
|
+
require 'prawn/svg/pathable'
|
18
21
|
require 'prawn/svg/elements'
|
19
22
|
require 'prawn/svg/extension'
|
20
23
|
require 'prawn/svg/interface'
|
data/lib/prawn/svg/attributes.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
module Prawn::SVG::Attributes::ClipPath
|
2
2
|
def parse_clip_path_attribute_and_call
|
3
|
-
return unless clip_path =
|
3
|
+
return unless clip_path = properties.clip_path
|
4
|
+
return if clip_path == 'none'
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
document.warnings << "clip-path
|
9
|
-
elsif clip_path_element.source.name != "clipPath"
|
10
|
-
document.warnings << "clip-path ID '#{matches[1]}' does not point to a clipPath tag"
|
6
|
+
clip_path_element = extract_element_from_url_id_reference(clip_path, 'clipPath')
|
7
|
+
|
8
|
+
if clip_path_element.nil?
|
9
|
+
document.warnings << "Could not resolve clip-path URI to a clipPath element"
|
11
10
|
else
|
12
11
|
add_call_and_enter 'save_graphics_state'
|
13
12
|
add_calls_from_element clip_path_element
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module Prawn::SVG::Attributes::Opacity
|
2
2
|
def parse_opacity_attributes_and_call
|
3
3
|
# We can't do nested opacities quite like the SVG requires, but this is close enough.
|
4
|
-
fill_opacity = stroke_opacity = clamp(
|
5
|
-
fill_opacity = clamp(
|
6
|
-
stroke_opacity = clamp(
|
4
|
+
fill_opacity = stroke_opacity = clamp(properties.opacity.to_f, 0, 1) if properties.opacity
|
5
|
+
fill_opacity = clamp(properties.fill_opacity.to_f, 0, 1) if properties.fill_opacity
|
6
|
+
stroke_opacity = clamp(properties.stroke_opacity.to_f, 0, 1) if properties.stroke_opacity
|
7
7
|
|
8
8
|
if fill_opacity || stroke_opacity
|
9
9
|
state.fill_opacity *= fill_opacity || 1
|
@@ -2,15 +2,17 @@ module Prawn::SVG::Attributes::Stroke
|
|
2
2
|
CAP_STYLE_TRANSLATIONS = {"butt" => :butt, "round" => :round, "square" => :projecting_square}
|
3
3
|
|
4
4
|
def parse_stroke_attributes_and_call
|
5
|
-
if
|
6
|
-
|
5
|
+
if width_string = properties.stroke_width
|
6
|
+
width = distance(width_string)
|
7
|
+
state.stroke_width = width
|
8
|
+
add_call('line_width', width)
|
7
9
|
end
|
8
10
|
|
9
|
-
if (linecap =
|
11
|
+
if (linecap = properties.stroke_linecap) && linecap != 'inherit'
|
10
12
|
add_call('cap_style', CAP_STYLE_TRANSLATIONS.fetch(linecap, :butt))
|
11
13
|
end
|
12
14
|
|
13
|
-
if dasharray =
|
15
|
+
if dasharray = properties.stroke_dasharray
|
14
16
|
case dasharray
|
15
17
|
when 'inherit'
|
16
18
|
# don't do anything
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Prawn::SVG::Calculators
|
2
|
+
module ArcToBezierCurve
|
3
|
+
protected
|
4
|
+
|
5
|
+
# Convert the elliptical arc to a cubic bézier curve using this algorithm:
|
6
|
+
# http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
|
7
|
+
def calculate_bezier_curve_points_for_arc(cx, cy, a, b, lambda_1, lambda_2, theta)
|
8
|
+
e = lambda do |eta|
|
9
|
+
[
|
10
|
+
cx + a * Math.cos(theta) * Math.cos(eta) - b * Math.sin(theta) * Math.sin(eta),
|
11
|
+
cy + a * Math.sin(theta) * Math.cos(eta) + b * Math.cos(theta) * Math.sin(eta)
|
12
|
+
]
|
13
|
+
end
|
14
|
+
|
15
|
+
ep = lambda do |eta|
|
16
|
+
[
|
17
|
+
-a * Math.cos(theta) * Math.sin(eta) - b * Math.sin(theta) * Math.cos(eta),
|
18
|
+
-a * Math.sin(theta) * Math.sin(eta) + b * Math.cos(theta) * Math.cos(eta)
|
19
|
+
]
|
20
|
+
end
|
21
|
+
|
22
|
+
iterations = 1
|
23
|
+
d_lambda = lambda_2 - lambda_1
|
24
|
+
|
25
|
+
while iterations < 1024
|
26
|
+
if d_lambda.abs <= Math::PI / 2.0
|
27
|
+
# TODO : run error algorithm, see whether it meets threshold or not
|
28
|
+
# puts "error = #{calculate_curve_approximation_error(a, b, eta1, eta1 + d_eta)}"
|
29
|
+
break
|
30
|
+
end
|
31
|
+
iterations *= 2
|
32
|
+
d_lambda = (lambda_2 - lambda_1) / iterations
|
33
|
+
end
|
34
|
+
|
35
|
+
(0...iterations).collect do |iteration|
|
36
|
+
eta_a, eta_b = calculate_eta_from_lambda(a, b, lambda_1+iteration*d_lambda, lambda_1+(iteration+1)*d_lambda)
|
37
|
+
d_eta = eta_b - eta_a
|
38
|
+
|
39
|
+
alpha = Math.sin(d_eta) * ((Math.sqrt(4 + 3 * Math.tan(d_eta / 2) ** 2) - 1) / 3)
|
40
|
+
|
41
|
+
x1, y1 = e[eta_a]
|
42
|
+
x2, y2 = e[eta_b]
|
43
|
+
|
44
|
+
ep_eta1_x, ep_eta1_y = ep[eta_a]
|
45
|
+
q1_x = x1 + alpha * ep_eta1_x
|
46
|
+
q1_y = y1 + alpha * ep_eta1_y
|
47
|
+
|
48
|
+
ep_eta2_x, ep_eta2_y = ep[eta_b]
|
49
|
+
q2_x = x2 - alpha * ep_eta2_x
|
50
|
+
q2_y = y2 - alpha * ep_eta2_y
|
51
|
+
|
52
|
+
{:p2 => [x2, y2], :q1 => [q1_x, q1_y], :q2 => [q2_x, q2_y]}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
ERROR_COEFFICIENTS_A = [
|
59
|
+
[
|
60
|
+
[3.85268, -21.229, -0.330434, 0.0127842],
|
61
|
+
[-1.61486, 0.706564, 0.225945, 0.263682],
|
62
|
+
[-0.910164, 0.388383, 0.00551445, 0.00671814],
|
63
|
+
[-0.630184, 0.192402, 0.0098871, 0.0102527]
|
64
|
+
],
|
65
|
+
[
|
66
|
+
[-0.162211, 9.94329, 0.13723, 0.0124084],
|
67
|
+
[-0.253135, 0.00187735, 0.0230286, 0.01264],
|
68
|
+
[-0.0695069, -0.0437594, 0.0120636, 0.0163087],
|
69
|
+
[-0.0328856, -0.00926032, -0.00173573, 0.00527385]
|
70
|
+
]
|
71
|
+
]
|
72
|
+
|
73
|
+
ERROR_COEFFICIENTS_B = [
|
74
|
+
[
|
75
|
+
[0.0899116, -19.2349, -4.11711, 0.183362],
|
76
|
+
[0.138148, -1.45804, 1.32044, 1.38474],
|
77
|
+
[0.230903, -0.450262, 0.219963, 0.414038],
|
78
|
+
[0.0590565, -0.101062, 0.0430592, 0.0204699]
|
79
|
+
],
|
80
|
+
[
|
81
|
+
[0.0164649, 9.89394, 0.0919496, 0.00760802],
|
82
|
+
[0.0191603, -0.0322058, 0.0134667, -0.0825018],
|
83
|
+
[0.0156192, -0.017535, 0.00326508, -0.228157],
|
84
|
+
[-0.0236752, 0.0405821, -0.0173086, 0.176187]
|
85
|
+
]
|
86
|
+
]
|
87
|
+
|
88
|
+
def calculate_curve_approximation_error(a, b, eta1, eta2)
|
89
|
+
b_over_a = b / a
|
90
|
+
coefficents = b_over_a < 0.25 ? ERROR_COEFFICIENTS_A : ERROR_COEFFICIENTS_B
|
91
|
+
|
92
|
+
c = lambda do |i|
|
93
|
+
(0..3).inject(0) do |accumulator, j|
|
94
|
+
coef = coefficents[i][j]
|
95
|
+
accumulator + ((coef[0] * b_over_a**2 + coef[1] * b_over_a + coef[2]) / (b_over_a * coef[3])) * Math.cos(j * (eta1 + eta2))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
((0.001 * b_over_a**2 + 4.98 * b_over_a + 0.207) / (b_over_a * 0.0067)) * a * Math.exp(c[0] + c[1] * (eta2 - eta1))
|
100
|
+
end
|
101
|
+
|
102
|
+
def calculate_eta_from_lambda(a, b, lambda_1, lambda_2)
|
103
|
+
# 2.2.1
|
104
|
+
eta1 = Math.atan2(Math.sin(lambda_1) / b, Math.cos(lambda_1) / a)
|
105
|
+
eta2 = Math.atan2(Math.sin(lambda_2) / b, Math.cos(lambda_2) / a)
|
106
|
+
|
107
|
+
# ensure eta1 <= eta2 <= eta1 + 2*PI
|
108
|
+
eta2 -= 2 * Math::PI * ((eta2 - eta1) / (2 * Math::PI)).floor
|
109
|
+
eta2 += 2 * Math::PI if lambda_2 - lambda_1 > Math::PI && eta2 - eta1 < Math::PI
|
110
|
+
|
111
|
+
[eta1, eta2]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/prawn/svg/elements.rb
CHANGED
@@ -2,7 +2,7 @@ module Prawn::SVG::Elements
|
|
2
2
|
COMMA_WSP_REGEXP = /(?:\s+,?\s*|,\s*)/
|
3
3
|
end
|
4
4
|
|
5
|
-
%w(base root container style text line polyline polygon circle ellipse rect path use image gradient ignored).each do |filename|
|
5
|
+
%w(base root container style text line polyline polygon circle ellipse rect path use image gradient marker ignored).each do |filename|
|
6
6
|
require "prawn/svg/elements/#{filename}"
|
7
7
|
end
|
8
8
|
|
@@ -13,6 +13,7 @@ module Prawn::SVG::Elements
|
|
13
13
|
symbol: Prawn::SVG::Elements::Container,
|
14
14
|
defs: Prawn::SVG::Elements::Container,
|
15
15
|
clipPath: Prawn::SVG::Elements::Container,
|
16
|
+
switch: Prawn::SVG::Elements::Container,
|
16
17
|
style: Prawn::SVG::Elements::Style,
|
17
18
|
text: Prawn::SVG::Elements::Text,
|
18
19
|
line: Prawn::SVG::Elements::Line,
|
@@ -25,9 +26,11 @@ module Prawn::SVG::Elements
|
|
25
26
|
use: Prawn::SVG::Elements::Use,
|
26
27
|
image: Prawn::SVG::Elements::Image,
|
27
28
|
linearGradient: Prawn::SVG::Elements::Gradient,
|
29
|
+
marker: Prawn::SVG::Elements::Marker,
|
28
30
|
title: Prawn::SVG::Elements::Ignored,
|
29
31
|
desc: Prawn::SVG::Elements::Ignored,
|
30
32
|
metadata: Prawn::SVG::Elements::Ignored,
|
33
|
+
foreignObject: Prawn::SVG::Elements::Ignored,
|
31
34
|
:"font-face" => Prawn::SVG::Elements::Ignored,
|
32
35
|
}
|
33
36
|
end
|
@@ -5,20 +5,19 @@ class Prawn::SVG::Elements::Base
|
|
5
5
|
include Prawn::SVG::Attributes::Opacity
|
6
6
|
include Prawn::SVG::Attributes::ClipPath
|
7
7
|
include Prawn::SVG::Attributes::Stroke
|
8
|
-
include Prawn::SVG::Attributes::Font
|
9
|
-
include Prawn::SVG::Attributes::Display
|
10
|
-
include Prawn::SVG::Attributes::Color
|
11
8
|
|
9
|
+
PAINT_TYPES = %w(fill stroke)
|
12
10
|
COMMA_WSP_REGEXP = Prawn::SVG::Elements::COMMA_WSP_REGEXP
|
13
11
|
|
14
12
|
SkipElementQuietly = Class.new(StandardError)
|
15
13
|
SkipElementError = Class.new(StandardError)
|
16
14
|
MissingAttributesError = Class.new(SkipElementError)
|
17
15
|
|
18
|
-
attr_reader :document, :source, :parent_calls, :base_calls, :state, :attributes
|
16
|
+
attr_reader :document, :source, :parent_calls, :base_calls, :state, :attributes, :properties
|
19
17
|
attr_accessor :calls
|
20
18
|
|
21
19
|
def_delegators :@document, :x, :y, :distance, :points, :warnings
|
20
|
+
def_delegator :@state, :computed_properties
|
22
21
|
|
23
22
|
def initialize(document, source, parent_calls, state)
|
24
23
|
@document = document
|
@@ -26,28 +25,35 @@ class Prawn::SVG::Elements::Base
|
|
26
25
|
@parent_calls = parent_calls
|
27
26
|
@state = state
|
28
27
|
@base_calls = @calls = []
|
28
|
+
@attributes = {}
|
29
|
+
@properties = Prawn::SVG::Properties.new
|
29
30
|
|
30
|
-
if id = source.attributes["id"]
|
31
|
+
if source && id = source.attributes["id"]
|
31
32
|
document.elements_by_id[id] = self
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
35
36
|
def process
|
36
|
-
|
37
|
-
|
37
|
+
extract_attributes_and_properties
|
38
|
+
parse_and_apply
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_and_apply
|
38
42
|
parse
|
39
43
|
|
40
44
|
apply_calls_from_standard_attributes
|
41
45
|
apply
|
42
46
|
|
43
|
-
|
47
|
+
process_child_elements if container?
|
48
|
+
|
49
|
+
append_calls_to_parent unless computed_properties.display == 'none'
|
44
50
|
rescue SkipElementQuietly
|
45
51
|
rescue SkipElementError => e
|
46
52
|
@document.warnings << e.message
|
47
53
|
end
|
48
54
|
|
49
55
|
def name
|
50
|
-
@name ||= source.name
|
56
|
+
@name ||= source ? source.name : "???"
|
51
57
|
end
|
52
58
|
|
53
59
|
protected
|
@@ -74,6 +80,15 @@ class Prawn::SVG::Elements::Base
|
|
74
80
|
@calls = @calls.last.last
|
75
81
|
end
|
76
82
|
|
83
|
+
def push_call_position
|
84
|
+
@call_positions ||= []
|
85
|
+
@call_positions << @calls
|
86
|
+
end
|
87
|
+
|
88
|
+
def pop_call_position
|
89
|
+
@calls = @call_positions.pop
|
90
|
+
end
|
91
|
+
|
77
92
|
def append_calls_to_parent
|
78
93
|
@parent_calls.concat(@base_calls)
|
79
94
|
end
|
@@ -82,7 +97,16 @@ class Prawn::SVG::Elements::Base
|
|
82
97
|
@calls.concat other.base_calls
|
83
98
|
end
|
84
99
|
|
100
|
+
def new_call_context_from_base
|
101
|
+
old_calls = @calls
|
102
|
+
@calls = @base_calls
|
103
|
+
yield
|
104
|
+
@calls = old_calls
|
105
|
+
end
|
106
|
+
|
85
107
|
def process_child_elements
|
108
|
+
return unless source
|
109
|
+
|
86
110
|
source.elements.each do |elem|
|
87
111
|
if element_class = Prawn::SVG::Elements::TAG_CLASS_MAPPING[elem.name.to_sym]
|
88
112
|
add_call "save"
|
@@ -101,16 +125,16 @@ class Prawn::SVG::Elements::Base
|
|
101
125
|
parse_transform_attribute_and_call
|
102
126
|
parse_opacity_attributes_and_call
|
103
127
|
parse_clip_path_attribute_and_call
|
104
|
-
|
128
|
+
apply_colors
|
105
129
|
parse_stroke_attributes_and_call
|
106
|
-
|
107
|
-
parse_display_attribute
|
108
|
-
apply_drawing_call(draw_types)
|
130
|
+
apply_drawing_call
|
109
131
|
end
|
110
132
|
|
111
|
-
def apply_drawing_call
|
133
|
+
def apply_drawing_call
|
112
134
|
if !state.disable_drawing && !container?
|
113
|
-
|
135
|
+
draw_types = PAINT_TYPES.select { |property| computed_properties.send(property) != 'none' }
|
136
|
+
|
137
|
+
if draw_types.empty?
|
114
138
|
add_call_and_enter("end_path")
|
115
139
|
else
|
116
140
|
add_call_and_enter(draw_types.join("_and_"))
|
@@ -118,46 +142,37 @@ class Prawn::SVG::Elements::Base
|
|
118
142
|
end
|
119
143
|
end
|
120
144
|
|
121
|
-
def
|
122
|
-
|
123
|
-
|
145
|
+
def apply_colors
|
146
|
+
PAINT_TYPES.each do |type|
|
147
|
+
color = properties.send(type)
|
124
148
|
|
125
|
-
|
126
|
-
["fill", "stroke"].select do |type|
|
127
|
-
case keyword = attribute_value_as_keyword(type)
|
128
|
-
when nil
|
129
|
-
when 'inherit'
|
130
|
-
when 'none'
|
131
|
-
state.disable_draw_type(type)
|
132
|
-
else
|
133
|
-
state.disable_draw_type(type)
|
149
|
+
next if [nil, 'inherit', 'none'].include?(color)
|
134
150
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
color = @attributes[type]
|
139
|
-
end
|
151
|
+
if color == 'currentColor'
|
152
|
+
color = computed_properties.color
|
153
|
+
end
|
140
154
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
add_call "#{type}_gradient", **arguments
|
154
|
-
break
|
155
|
-
end
|
155
|
+
results = Prawn::SVG::Color.parse(color, document.gradients)
|
156
|
+
|
157
|
+
success = results.detect do |result|
|
158
|
+
case result
|
159
|
+
when Prawn::SVG::Color::Hex
|
160
|
+
add_call "#{type}_color", result.value
|
161
|
+
true
|
162
|
+
when Prawn::SVG::Elements::Gradient
|
163
|
+
arguments = result.gradient_arguments(self)
|
164
|
+
if arguments
|
165
|
+
add_call "#{type}_gradient", **arguments
|
166
|
+
true
|
156
167
|
end
|
157
168
|
end
|
158
169
|
end
|
159
170
|
|
160
|
-
|
171
|
+
# If we were unable to find a suitable color candidate,
|
172
|
+
# we turn off this type of paint.
|
173
|
+
if success.nil?
|
174
|
+
computed_properties.set(type, 'none')
|
175
|
+
end
|
161
176
|
end
|
162
177
|
end
|
163
178
|
|
@@ -165,7 +180,7 @@ class Prawn::SVG::Elements::Base
|
|
165
180
|
[[value, min_value].max, max_value].min
|
166
181
|
end
|
167
182
|
|
168
|
-
def
|
183
|
+
def extract_attributes_and_properties
|
169
184
|
if @document && @document.css_parser
|
170
185
|
tag_style = @document.css_parser.find_by_selector(source.name)
|
171
186
|
id_style = @document.css_parser.find_by_selector("##{source.attributes["id"]}") if source.attributes["id"]
|
@@ -185,12 +200,14 @@ class Prawn::SVG::Elements::Base
|
|
185
200
|
style = source.attributes['style'] || ""
|
186
201
|
end
|
187
202
|
|
188
|
-
@attributes = parse_css_declarations(style)
|
189
|
-
|
190
203
|
source.attributes.each do |name, value|
|
191
|
-
|
192
|
-
@
|
204
|
+
# Properties#set returns nil if it's not a recognised property name
|
205
|
+
@properties.set(name, value) or @attributes[name] = value
|
193
206
|
end
|
207
|
+
|
208
|
+
@properties.load_hash(parse_css_declarations(style))
|
209
|
+
|
210
|
+
state.computed_properties.compute_properties(@properties)
|
194
211
|
end
|
195
212
|
|
196
213
|
def parse_css_declarations(declarations)
|
@@ -207,22 +224,6 @@ class Prawn::SVG::Elements::Base
|
|
207
224
|
output
|
208
225
|
end
|
209
226
|
|
210
|
-
def attribute_value_as_keyword(name)
|
211
|
-
if value = @attributes[name]
|
212
|
-
value.strip.downcase
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
def parse_points(points_string)
|
217
|
-
points_string.
|
218
|
-
to_s.
|
219
|
-
strip.
|
220
|
-
gsub(/(\d)-(\d)/, '\1 -\2').
|
221
|
-
split(COMMA_WSP_REGEXP).
|
222
|
-
each_slice(2).
|
223
|
-
map {|x, y| [x(x), y(y)]}
|
224
|
-
end
|
225
|
-
|
226
227
|
def require_attributes(*names)
|
227
228
|
missing_attrs = names - attributes.keys
|
228
229
|
if missing_attrs.any?
|
@@ -235,4 +236,10 @@ class Prawn::SVG::Elements::Base
|
|
235
236
|
raise SkipElementError, "Invalid attributes on tag #{name}; skipping tag"
|
236
237
|
end
|
237
238
|
end
|
239
|
+
|
240
|
+
def extract_element_from_url_id_reference(value, expected_type = nil)
|
241
|
+
matches = value.strip.match(/\Aurl\(\s*#(\S+)\s*\)\z/i) if value
|
242
|
+
element = document.elements_by_id[matches[1]] if matches
|
243
|
+
element if element && (expected_type.nil? || element.name == expected_type)
|
244
|
+
end
|
238
245
|
end
|