prawn-svg 0.23.1 → 0.24.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/.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
|