prawn-svg 0.27.1 → 0.31.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +6 -4
- data/LICENSE +1 -1
- data/README.md +23 -9
- data/lib/prawn-svg.rb +7 -1
- data/lib/prawn/svg/attributes/opacity.rb +4 -4
- data/lib/prawn/svg/attributes/transform.rb +2 -44
- data/lib/prawn/svg/calculators/document_sizing.rb +2 -2
- data/lib/prawn/svg/{css.rb → css/font_family_parser.rb} +3 -4
- data/lib/prawn/svg/css/selector_parser.rb +174 -0
- data/lib/prawn/svg/css/stylesheets.rb +146 -0
- data/lib/prawn/svg/document.rb +3 -15
- data/lib/prawn/svg/elements.rb +4 -2
- data/lib/prawn/svg/elements/base.rb +26 -23
- data/lib/prawn/svg/elements/clip_path.rb +12 -0
- data/lib/prawn/svg/elements/container.rb +1 -3
- data/lib/prawn/svg/elements/gradient.rb +83 -25
- data/lib/prawn/svg/elements/image.rb +2 -2
- data/lib/prawn/svg/elements/path.rb +42 -29
- data/lib/prawn/svg/elements/root.rb +4 -1
- data/lib/prawn/svg/elements/text.rb +1 -1
- data/lib/prawn/svg/elements/text_component.rb +63 -14
- data/lib/prawn/svg/elements/use.rb +23 -7
- data/lib/prawn/svg/extensions/additional_gradient_transforms.rb +23 -0
- data/lib/prawn/svg/font_registry.rb +4 -3
- data/lib/prawn/svg/interface.rb +26 -2
- data/lib/prawn/svg/loaders/data.rb +1 -1
- data/lib/prawn/svg/loaders/file.rb +4 -2
- data/lib/prawn/svg/properties.rb +2 -0
- data/lib/prawn/svg/state.rb +6 -3
- data/lib/prawn/svg/transform_parser.rb +72 -0
- data/lib/prawn/svg/version.rb +1 -1
- data/prawn-svg.gemspec +3 -4
- data/spec/prawn/svg/attributes/opacity_spec.rb +85 -0
- data/spec/prawn/svg/attributes/transform_spec.rb +30 -35
- data/spec/prawn/svg/calculators/document_sizing_spec.rb +4 -4
- data/spec/prawn/svg/{css_spec.rb → css/font_family_parser_spec.rb} +3 -3
- data/spec/prawn/svg/css/selector_parser_spec.rb +33 -0
- data/spec/prawn/svg/css/stylesheets_spec.rb +142 -0
- data/spec/prawn/svg/document_spec.rb +0 -33
- data/spec/prawn/svg/elements/base_spec.rb +58 -2
- data/spec/prawn/svg/elements/gradient_spec.rb +79 -4
- data/spec/prawn/svg/elements/path_spec.rb +29 -17
- data/spec/prawn/svg/elements/text_spec.rb +74 -16
- data/spec/prawn/svg/font_registry_spec.rb +30 -0
- data/spec/prawn/svg/interface_spec.rb +33 -1
- data/spec/prawn/svg/loaders/data_spec.rb +8 -0
- data/spec/prawn/svg/transform_parser_spec.rb +94 -0
- data/spec/sample_output/{directory → .keep} +0 -0
- data/spec/sample_svg/double_opacity.svg +6 -0
- data/spec/sample_svg/gradient_transform.svg +19 -0
- data/spec/sample_svg/links.svg +18 -0
- data/spec/sample_svg/radgrad01-bounding.svg +26 -0
- data/spec/sample_svg/radgrad01.svg +26 -0
- data/spec/sample_svg/svg_fill.svg +5 -0
- data/spec/sample_svg/text-decoration.svg +4 -0
- data/spec/sample_svg/text_stroke.svg +41 -0
- data/spec/sample_svg/transform.svg +20 -0
- data/spec/sample_svg/use_disordered.svg +17 -0
- data/spec/sample_svg/warning-radioactive.svg +98 -0
- data/spec/spec_helper.rb +2 -2
- metadata +137 -15
@@ -8,7 +8,10 @@ class Prawn::SVG::Elements::Root < Prawn::SVG::Elements::Base
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def apply
|
11
|
-
|
11
|
+
if [nil, 'inherit', 'none', 'currentColor'].include?(properties.fill)
|
12
|
+
add_call 'fill_color', '000000'
|
13
|
+
end
|
14
|
+
|
12
15
|
add_call 'transformation_matrix', @document.sizing.x_scale, 0, 0, @document.sizing.y_scale, 0, 0
|
13
16
|
add_call 'transformation_matrix', 1, 0, 0, 1, -@document.sizing.x_offset, @document.sizing.y_offset
|
14
17
|
end
|
@@ -7,7 +7,7 @@ class Prawn::SVG::Elements::Text < Prawn::SVG::Elements::DepthFirstBase
|
|
7
7
|
# of the element, delegating it to our root text component.
|
8
8
|
|
9
9
|
def parse_step
|
10
|
-
state.text = Prawn::SVG::Elements::TextComponent::
|
10
|
+
state.text = Prawn::SVG::Elements::TextComponent::TextState.new
|
11
11
|
|
12
12
|
@text_root = Prawn::SVG::Elements::TextComponent.new(document, source, nil, state.dup)
|
13
13
|
@text_root.parse_step
|
@@ -2,14 +2,22 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
2
2
|
attr_reader :commands
|
3
3
|
|
4
4
|
Printable = Struct.new(:element, :text, :leading_space?, :trailing_space?)
|
5
|
-
|
5
|
+
TextState = Struct.new(:parent, :x, :y, :dx, :dy, :rotation, :spacing, :mode, :text_length, :length_adjust)
|
6
6
|
|
7
7
|
def parse
|
8
|
-
state.
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
state.text.
|
8
|
+
if state.inside_clip_path
|
9
|
+
raise SkipElementError, "<text> elements are not supported in clip paths"
|
10
|
+
end
|
11
|
+
|
12
|
+
state.text.x = (attributes['x'] || "").split(COMMA_WSP_REGEXP).collect { |n| x(n) }
|
13
|
+
state.text.y = (attributes['y'] || "").split(COMMA_WSP_REGEXP).collect { |n| y(n) }
|
14
|
+
state.text.dx = (attributes['dx'] || "").split(COMMA_WSP_REGEXP).collect { |n| x_pixels(n) }
|
15
|
+
state.text.dy = (attributes['dy'] || "").split(COMMA_WSP_REGEXP).collect { |n| y_pixels(n) }
|
16
|
+
state.text.rotation = (attributes['rotate'] || "").split(COMMA_WSP_REGEXP).collect(&:to_f)
|
17
|
+
state.text.text_length = normalize_length(attributes['textLength'])
|
18
|
+
state.text.length_adjust = attributes['lengthAdjust']
|
19
|
+
state.text.spacing = calculate_character_spacing
|
20
|
+
state.text.mode = calculate_text_rendering_mode
|
13
21
|
|
14
22
|
@commands = []
|
15
23
|
|
@@ -38,13 +46,18 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
38
46
|
opts = {
|
39
47
|
size: computed_properties.numerical_font_size,
|
40
48
|
style: font && font.subfamily,
|
41
|
-
text_anchor: computed_properties.text_anchor
|
49
|
+
text_anchor: computed_properties.text_anchor,
|
42
50
|
}
|
43
51
|
|
44
|
-
|
45
|
-
spacing = spacing == 'normal' ? 0 : pixels(spacing)
|
52
|
+
opts[:decoration] = computed_properties.text_decoration unless computed_properties.text_decoration == 'none'
|
46
53
|
|
47
|
-
|
54
|
+
if state.text.parent
|
55
|
+
add_call_and_enter 'character_spacing', state.text.spacing unless state.text.spacing == state.text.parent.spacing
|
56
|
+
add_call_and_enter 'text_rendering_mode', state.text.mode unless state.text.mode == state.text.parent.mode
|
57
|
+
else
|
58
|
+
add_call_and_enter 'character_spacing', state.text.spacing unless state.text.spacing == 0
|
59
|
+
add_call_and_enter 'text_rendering_mode', state.text.mode unless state.text.mode == :fill
|
60
|
+
end
|
48
61
|
|
49
62
|
@commands.each do |command|
|
50
63
|
case command
|
@@ -59,8 +72,8 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
59
72
|
end
|
60
73
|
end
|
61
74
|
|
62
|
-
# It's possible there was no text to render. In that case, add a 'noop' so
|
63
|
-
#
|
75
|
+
# It's possible there was no text to render. In that case, add a 'noop' so character_spacing/text_rendering_mode
|
76
|
+
# don't blow up when they find they don't have a block to execute.
|
64
77
|
add_call 'noop' if calls.empty?
|
65
78
|
end
|
66
79
|
|
@@ -81,7 +94,7 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
81
94
|
|
82
95
|
def append_child(child)
|
83
96
|
new_state = state.dup
|
84
|
-
new_state.text =
|
97
|
+
new_state.text = TextState.new(state.text)
|
85
98
|
|
86
99
|
element = self.class.new(document, child, calls, new_state)
|
87
100
|
@commands << element
|
@@ -124,6 +137,14 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
124
137
|
opts.delete(:rotate)
|
125
138
|
end
|
126
139
|
|
140
|
+
if state.text.text_length
|
141
|
+
if state.text.length_adjust == 'spacingAndGlyphs'
|
142
|
+
opts[:stretch_to_width] = state.text.text_length
|
143
|
+
else
|
144
|
+
opts[:pad_to_width] = state.text.text_length
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
127
148
|
if remaining
|
128
149
|
add_call 'draw_text', text[0..0], opts.dup
|
129
150
|
text = text[1..-1]
|
@@ -164,7 +185,7 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
164
185
|
end
|
165
186
|
|
166
187
|
def find_referenced_element
|
167
|
-
href =
|
188
|
+
href = href_attribute
|
168
189
|
|
169
190
|
if href && href[0..0] == '#'
|
170
191
|
element = document.elements_by_id[href[1..-1]]
|
@@ -189,4 +210,32 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
189
210
|
def apply_font(font)
|
190
211
|
add_call 'font', font.name, style: font.subfamily
|
191
212
|
end
|
213
|
+
|
214
|
+
def calculate_text_rendering_mode
|
215
|
+
fill = computed_properties.fill != 'none'
|
216
|
+
stroke = computed_properties.stroke != 'none'
|
217
|
+
|
218
|
+
if fill && stroke
|
219
|
+
:fill_stroke
|
220
|
+
elsif fill
|
221
|
+
:fill
|
222
|
+
elsif stroke
|
223
|
+
:stroke
|
224
|
+
else
|
225
|
+
:invisible
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def calculate_character_spacing
|
230
|
+
spacing = computed_properties.letter_spacing
|
231
|
+
spacing == 'normal' ? 0 : pixels(spacing)
|
232
|
+
end
|
233
|
+
|
234
|
+
# overridden, we don't want to call fill/stroke as draw_text does this for us
|
235
|
+
def apply_drawing_call
|
236
|
+
end
|
237
|
+
|
238
|
+
def normalize_length(length)
|
239
|
+
x_pixels(length) if length && length.match(/\d/)
|
240
|
+
end
|
192
241
|
end
|
@@ -1,19 +1,35 @@
|
|
1
1
|
class Prawn::SVG::Elements::Use < Prawn::SVG::Elements::Base
|
2
|
-
attr_reader :
|
2
|
+
attr_reader :referenced_element_class
|
3
|
+
attr_reader :referenced_element_source
|
3
4
|
|
4
5
|
def parse
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
href = href_attribute
|
7
|
+
if href.nil?
|
8
|
+
raise SkipElementError, "use tag must have an href or xlink:href"
|
9
|
+
end
|
8
10
|
|
9
11
|
if href[0..0] != '#'
|
10
12
|
raise SkipElementError, "use tag has an href that is not a reference to an id; this is not supported"
|
11
13
|
end
|
12
14
|
|
13
15
|
id = href[1..-1]
|
14
|
-
|
16
|
+
referenced_element = @document.elements_by_id[id]
|
17
|
+
|
18
|
+
if referenced_element
|
19
|
+
@referenced_element_class = referenced_element.class
|
20
|
+
@referenced_element_source = referenced_element.source
|
21
|
+
else
|
22
|
+
# Perhaps the element is defined further down in the document. This is not recommended but still valid SVG,
|
23
|
+
# so we'll support it with an exception case that's not particularly performant.
|
24
|
+
raw_element = REXML::XPath.match(@document.root, %(//*[@id="#{id.gsub('"', '\"')}"])).first
|
25
|
+
|
26
|
+
if raw_element
|
27
|
+
@referenced_element_class = Prawn::SVG::Elements::TAG_CLASS_MAPPING[raw_element.name.to_sym]
|
28
|
+
@referenced_element_source = raw_element
|
29
|
+
end
|
30
|
+
end
|
15
31
|
|
16
|
-
if
|
32
|
+
if referenced_element_class.nil?
|
17
33
|
raise SkipElementError, "no tag with ID '#{id}' was found, referenced by use tag"
|
18
34
|
end
|
19
35
|
|
@@ -36,7 +52,7 @@ class Prawn::SVG::Elements::Use < Prawn::SVG::Elements::Base
|
|
36
52
|
def process_child_elements
|
37
53
|
add_call "save"
|
38
54
|
|
39
|
-
child =
|
55
|
+
child = referenced_element_class.new(document, referenced_element_source, calls, state.dup)
|
40
56
|
child.process
|
41
57
|
|
42
58
|
add_call "restore"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Prawn::SVG::Extensions
|
2
|
+
module AdditionalGradientTransforms
|
3
|
+
def gradient_coordinates(gradient)
|
4
|
+
# As of Prawn 2.2.0, apply_transformations is used as purely a boolean.
|
5
|
+
#
|
6
|
+
# Here we're using it to optionally pass in a 6-tuple transformation matrix that gets applied to the
|
7
|
+
# gradient. This should be added to Prawn properly, and then this monkey patch will not be necessary.
|
8
|
+
|
9
|
+
if gradient.apply_transformations.is_a?(Array)
|
10
|
+
x1, y1, x2, y2, transformation = super
|
11
|
+
a, b, c, d, e, f = transformation
|
12
|
+
na, nb, nc, nd, ne, nf = gradient.apply_transformations
|
13
|
+
|
14
|
+
matrix = Matrix[[a, c, e], [b, d, f], [0, 0, 1]] * Matrix[[na, nc, ne], [nb, nd, nf], [0, 0, 1]]
|
15
|
+
new_transformation = matrix.to_a[0..1].transpose.flatten
|
16
|
+
|
17
|
+
[x1, y1, x2, y2, new_transformation]
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -24,7 +24,7 @@ class Prawn::SVG::FontRegistry
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def load(family, weight = nil, style = nil)
|
27
|
-
Prawn::SVG::CSS.
|
27
|
+
Prawn::SVG::CSS::FontFamilyParser.parse(family).detect do |name|
|
28
28
|
name = name.gsub(/\s{2,}/, ' ').downcase
|
29
29
|
|
30
30
|
font = Prawn::SVG::Font.new(name, weight, style, font_registry: self)
|
@@ -37,8 +37,9 @@ class Prawn::SVG::FontRegistry
|
|
37
37
|
def merge_external_fonts
|
38
38
|
if @font_case_mapping.nil?
|
39
39
|
self.class.load_external_fonts unless self.class.external_font_families
|
40
|
-
@font_families.merge!(self.class.external_font_families)
|
41
|
-
|
40
|
+
@font_families.merge!(self.class.external_font_families) do |key, v1, v2|
|
41
|
+
v1
|
42
|
+
end
|
42
43
|
@font_case_mapping = @font_families.keys.each.with_object({}) do |key, result|
|
43
44
|
result[key.downcase] = key
|
44
45
|
end
|
data/lib/prawn/svg/interface.rb
CHANGED
@@ -119,7 +119,7 @@ module Prawn
|
|
119
119
|
|
120
120
|
if skip
|
121
121
|
# the call has been overridden
|
122
|
-
elsif children.empty?
|
122
|
+
elsif children.empty? && call != 'transparent' # some prawn calls complain if they aren't supplied a block
|
123
123
|
prawn.send(call, *arguments)
|
124
124
|
else
|
125
125
|
prawn.send(call, *arguments, &proc_creator(prawn, children))
|
@@ -148,6 +148,19 @@ module Prawn
|
|
148
148
|
|
149
149
|
width = prawn.width_of(text, options.merge(kerning: true))
|
150
150
|
|
151
|
+
if stretch_to_width = options.delete(:stretch_to_width)
|
152
|
+
factor = stretch_to_width.to_f * 100 / width.to_f
|
153
|
+
prawn.add_content "#{factor} Tz"
|
154
|
+
width = stretch_to_width.to_f
|
155
|
+
end
|
156
|
+
|
157
|
+
if pad_to_width = options.delete(:pad_to_width)
|
158
|
+
padding_required = pad_to_width.to_f - width.to_f
|
159
|
+
padding_per_character = padding_required / text.length.to_f
|
160
|
+
prawn.add_content "#{padding_per_character} Tc"
|
161
|
+
width = pad_to_width.to_f
|
162
|
+
end
|
163
|
+
|
151
164
|
case options.delete(:text_anchor)
|
152
165
|
when 'middle'
|
153
166
|
at[0] -= width / 2
|
@@ -159,6 +172,15 @@ module Prawn
|
|
159
172
|
@cursor = [at[0] + width, at[1]]
|
160
173
|
end
|
161
174
|
|
175
|
+
decoration = options.delete(:decoration)
|
176
|
+
if decoration == 'underline'
|
177
|
+
prawn.save_graphics_state do
|
178
|
+
prawn.line_width 1
|
179
|
+
prawn.line [at[0], at[1] - 1.25], [at[0] + width, at[1] - 1.25]
|
180
|
+
prawn.stroke
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
162
184
|
when 'transformation_matrix'
|
163
185
|
left = prawn.bounds.absolute_left
|
164
186
|
top = prawn.bounds.absolute_top
|
@@ -186,7 +208,9 @@ module Prawn
|
|
186
208
|
# prawn (as at 2.0.1 anyway) uses 'b' for its fill_and_stroke. 'b' is 'h' (closepath) + 'B', and we
|
187
209
|
# never want closepath to be automatically run as it stuffs up many drawing operations, such as dashes
|
188
210
|
# and line caps, and makes paths close that we didn't ask to be closed when fill is specified.
|
189
|
-
|
211
|
+
even_odd = arguments[0].is_a?(Hash) && arguments[0][:fill_rule] == :even_odd
|
212
|
+
content = even_odd ? 'B*' : 'B'
|
213
|
+
prawn.add_content content
|
190
214
|
|
191
215
|
when 'noop'
|
192
216
|
yield
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
|
1
3
|
#
|
2
4
|
# Load a file from disk.
|
3
5
|
#
|
@@ -56,7 +58,7 @@ module Prawn::SVG::Loaders
|
|
56
58
|
private
|
57
59
|
|
58
60
|
def load_file(path)
|
59
|
-
path = URI.
|
61
|
+
path = Addressable::URI.unencode(path)
|
60
62
|
path = build_absolute_and_expand_path(path)
|
61
63
|
assert_valid_path!(path)
|
62
64
|
assert_file_exists!(path)
|
@@ -85,7 +87,7 @@ module Prawn::SVG::Loaders
|
|
85
87
|
end
|
86
88
|
|
87
89
|
def assert_valid_file_uri!(uri)
|
88
|
-
|
90
|
+
unless uri.host.nil? || uri.host.empty?
|
89
91
|
raise Prawn::SVG::UrlLoader::Error, "prawn-svg does not suport file: URLs with a host. Your URL probably doesn't start with three slashes, and it should."
|
90
92
|
end
|
91
93
|
end
|
data/lib/prawn/svg/properties.rb
CHANGED
@@ -18,6 +18,7 @@ class Prawn::SVG::Properties
|
|
18
18
|
"display" => Config.new("inline", false, %w(inherit inline none), true),
|
19
19
|
"fill" => Config.new("black", true, %w(inherit none currentColor)),
|
20
20
|
"fill-opacity" => Config.new("1", true),
|
21
|
+
"fill-rule" => Config.new("nonzero", true, %w(inherit nonzero evenodd)),
|
21
22
|
"font-family" => Config.new("sans-serif", true),
|
22
23
|
"font-size" => Config.new("medium", true, %w(inherit xx-small x-small small medium large x-large xx-large larger smaller)),
|
23
24
|
"font-style" => Config.new("normal", true, %w(inherit normal italic oblique), true),
|
@@ -36,6 +37,7 @@ class Prawn::SVG::Properties
|
|
36
37
|
"stroke-opacity" => Config.new("1", true),
|
37
38
|
"stroke-width" => Config.new("1", true),
|
38
39
|
"text-anchor" => Config.new("start", true, %w(inherit start middle end), true),
|
40
|
+
'text-decoration' => Config.new('none', true, %w(inherit none underline), true),
|
39
41
|
}.freeze
|
40
42
|
|
41
43
|
PROPERTIES.each do |name, value|
|
data/lib/prawn/svg/state.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
class Prawn::SVG::State
|
2
|
-
attr_accessor :
|
3
|
-
:text, :preserve_space,
|
2
|
+
attr_accessor :text, :preserve_space,
|
4
3
|
:fill_opacity, :stroke_opacity, :stroke_width,
|
5
4
|
:computed_properties,
|
6
5
|
:viewport_sizing,
|
7
|
-
:inside_use
|
6
|
+
:inside_use, :inside_clip_path
|
8
7
|
|
9
8
|
def initialize
|
10
9
|
@stroke_width = 1
|
@@ -16,4 +15,8 @@ class Prawn::SVG::State
|
|
16
15
|
def initialize_dup(other)
|
17
16
|
@computed_properties = @computed_properties.dup
|
18
17
|
end
|
18
|
+
|
19
|
+
def disable_drawing
|
20
|
+
inside_clip_path
|
21
|
+
end
|
19
22
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Prawn::SVG::TransformParser
|
2
|
+
def parse_transform_attribute(transform)
|
3
|
+
matrix = Matrix.identity(3)
|
4
|
+
|
5
|
+
parse_css_method_calls(transform).each do |name, arguments|
|
6
|
+
case name
|
7
|
+
when 'translate'
|
8
|
+
x, y = arguments
|
9
|
+
matrix *= Matrix[[1, 0, x_pixels(x.to_f)], [0, 1, -y_pixels(y.to_f)], [0, 0, 1]]
|
10
|
+
|
11
|
+
when 'translateX'
|
12
|
+
x = arguments.first
|
13
|
+
matrix *= Matrix[[1, 0, x_pixels(x.to_f)], [0, 1, 0], [0, 0, 1]]
|
14
|
+
|
15
|
+
when 'translateY'
|
16
|
+
y = arguments.first
|
17
|
+
matrix *= Matrix[[1, 0, 0], [0, 1, -y_pixels(y.to_f)], [0, 0, 1]]
|
18
|
+
|
19
|
+
when 'rotate'
|
20
|
+
angle, x, y = arguments.collect { |a| a.to_f }
|
21
|
+
angle = angle * Math::PI / 180.0
|
22
|
+
|
23
|
+
case arguments.length
|
24
|
+
when 1
|
25
|
+
matrix *= Matrix[[Math.cos(angle), Math.sin(angle), 0], [-Math.sin(angle), Math.cos(angle), 0], [0, 0, 1]]
|
26
|
+
when 3
|
27
|
+
matrix *= Matrix[[1, 0, x_pixels(x.to_f)], [0, 1, -y_pixels(y.to_f)], [0, 0, 1]]
|
28
|
+
matrix *= Matrix[[Math.cos(angle), Math.sin(angle), 0], [-Math.sin(angle), Math.cos(angle), 0], [0, 0, 1]]
|
29
|
+
matrix *= Matrix[[1, 0, -x_pixels(x.to_f)], [0, 1, y_pixels(y.to_f)], [0, 0, 1]]
|
30
|
+
else
|
31
|
+
warnings << "transform 'rotate' must have either one or three arguments"
|
32
|
+
end
|
33
|
+
|
34
|
+
when 'scale'
|
35
|
+
x_scale = arguments[0].to_f
|
36
|
+
y_scale = (arguments[1] || x_scale).to_f
|
37
|
+
matrix *= Matrix[[x_scale, 0, 0], [0, y_scale, 0], [0, 0, 1]]
|
38
|
+
|
39
|
+
when 'skewX'
|
40
|
+
angle = arguments[0].to_f * Math::PI / 180.0
|
41
|
+
matrix *= Matrix[[1, -Math.tan(angle), 0], [0, 1, 0], [0, 0, 1]]
|
42
|
+
|
43
|
+
when 'skewY'
|
44
|
+
angle = arguments[0].to_f * Math::PI / 180.0
|
45
|
+
matrix *= Matrix[[1, 0, 0], [-Math.tan(angle), 1, 0], [0, 0, 1]]
|
46
|
+
|
47
|
+
when 'matrix'
|
48
|
+
if arguments.length != 6
|
49
|
+
warnings << "transform 'matrix' must have six arguments"
|
50
|
+
else
|
51
|
+
a, b, c, d, e, f = arguments.collect { |argument| argument.to_f }
|
52
|
+
matrix *= Matrix[[a, -c, e], [-b, d, -f], [0, 0, 1]]
|
53
|
+
end
|
54
|
+
|
55
|
+
else
|
56
|
+
warnings << "Unknown/unsupported transformation '#{name}'; ignoring"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
matrix.to_a[0..1].transpose.flatten
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def parse_css_method_calls(string)
|
66
|
+
string.scan(/\s*(\w+)\(([^)]+)\)\s*/).collect do |call|
|
67
|
+
name, argument_string = call
|
68
|
+
arguments = argument_string.strip.split(/\s*[,\s]\s*/)
|
69
|
+
[name, arguments]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/prawn/svg/version.rb
CHANGED