prawn-svg 0.9.1.6 → 0.9.1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,184 @@
1
+ module Prawn
2
+ module Svg
3
+ class Parser::Path
4
+ # Raised if the SVG path cannot be parsed.
5
+ InvalidError = Class.new(StandardError)
6
+
7
+ #
8
+ # Parses an SVG path and returns a Prawn-compatible call tree.
9
+ #
10
+ def parse(data)
11
+ cmd = values = nil
12
+ value = ""
13
+ @subpath_initial_point = @last_point = nil
14
+ @previous_control_point = @previous_quadratic_control_point = nil
15
+ @calls = []
16
+
17
+ data.each_char do |c|
18
+ if c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'
19
+ values << value.to_f if value != ""
20
+ run_path_command(cmd, values) if cmd
21
+ cmd = c
22
+ values = []
23
+ value = ""
24
+ elsif c >= '0' && c <= '9' || c == '.' || c == "-"
25
+ unless cmd
26
+ raise InvalidError, "Numerical value specified before character command in SVG path data"
27
+ end
28
+ value << c
29
+ elsif c == ' ' || c == "\t" || c == "\r" || c == "\n" || c == ","
30
+ if value != ""
31
+ values << value.to_f
32
+ value = ""
33
+ end
34
+ else
35
+ raise InvalidError, "Invalid character '#{c}' in SVG path data"
36
+ end
37
+ end
38
+
39
+ values << value.to_f if value != ""
40
+ run_path_command(cmd, values) if cmd
41
+
42
+ @calls
43
+ end
44
+
45
+
46
+ private
47
+ def run_path_command(command, values)
48
+ upcase_command = command.upcase
49
+ relative = command != upcase_command
50
+
51
+ case upcase_command
52
+ when 'M' # moveto
53
+ x = values.shift
54
+ y = values.shift
55
+
56
+ if relative && @last_point
57
+ x += @last_point.first
58
+ y += @last_point.last
59
+ end
60
+
61
+ @last_point = @subpath_initial_point = [x, y]
62
+ @calls << ["move_to", @last_point]
63
+
64
+ return run_path_command('L', values) if values.any?
65
+
66
+ when 'Z' # closepath
67
+ if @subpath_initial_point
68
+ @calls << ["line_to", @subpath_initial_point]
69
+ @last_point = @subpath_initial_point
70
+ end
71
+
72
+ when 'L' # lineto
73
+ while values.any?
74
+ x = values.shift
75
+ y = values.shift
76
+ if relative && @last_point
77
+ x += @last_point.first
78
+ y += @last_point.last
79
+ end
80
+ @last_point = [x, y]
81
+ @calls << ["line_to", @last_point]
82
+ end
83
+
84
+ when 'H' # horizontal lineto
85
+ while values.any?
86
+ x = values.shift
87
+ x += @last_point.first if relative && @last_point
88
+ @last_point = [x, @last_point.last]
89
+ @calls << ["line_to", @last_point]
90
+ end
91
+
92
+ when 'V' # vertical lineto
93
+ while values.any?
94
+ y = values.shift
95
+ y += @last_point.last if relative && @last_point
96
+ @last_point = [@last_point.first, y]
97
+ @calls << ["line_to", @last_point]
98
+ end
99
+
100
+ when 'C' # curveto
101
+ while values.any?
102
+ x1, y1, x2, y2, x, y = (1..6).collect {values.shift}
103
+ if relative && @last_point
104
+ x += @last_point.first
105
+ x1 += @last_point.first
106
+ x2 += @last_point.first
107
+ y += @last_point.last
108
+ y1 += @last_point.last
109
+ y2 += @last_point.last
110
+ end
111
+
112
+ @last_point = [x, y]
113
+ @previous_control_point = [x2, y2]
114
+ @calls << ["curve_to", [x, y, x1, y1, x2, y2]]
115
+ end
116
+
117
+ when 'S' # shorthand/smooth curveto
118
+ while values.any?
119
+ x2, y2, x, y = (1..4).collect {values.shift}
120
+ if relative && @last_point
121
+ x += @last_point.first
122
+ x2 += @last_point.first
123
+ y += @last_point.last
124
+ y2 += @last_point.last
125
+ end
126
+
127
+ if @previous_control_point
128
+ x1 = 2 * @last_point.first - @previous_control_point.first
129
+ y1 = 2 * @last_point.last - @previous_control_point.last
130
+ else
131
+ x1, y1 = @last_point
132
+ end
133
+
134
+ @last_point = [x, y]
135
+ @previous_control_point = [x2, y2]
136
+ @calls << ["curve_to", [x, y, x1, y1, x2, y2]]
137
+ end
138
+
139
+ when 'Q', 'T' # quadratic curveto
140
+ while values.any?
141
+ if shorthand = upcase_command == 'T'
142
+ x, y = (1..2).collect {values.shift}
143
+ else
144
+ x1, y1, x, y = (1..4).collect {values.shift}
145
+ end
146
+
147
+ if relative && @last_point
148
+ x += @last_point.first
149
+ x1 += @last_point.first if x1
150
+ y += @last_point.last
151
+ y1 += @last_point.last if y1
152
+ end
153
+
154
+ if shorthand
155
+ if @previous_quadratic_control_point
156
+ x1 = 2 * @last_point.first - @previous_quadratic_control_point.first
157
+ y1 = 2 * @last_point.last - @previous_quadratic_control_point.last
158
+ else
159
+ x1, y1 = @last_point
160
+ end
161
+ end
162
+
163
+ # convert from quadratic to cubic
164
+ cx1 = @last_point.first + (x1 - @last_point.first) * 2 / 3.0
165
+ cy1 = @last_point.last + (y1 - @last_point.last) * 2 / 3.0
166
+ cx2 = cx1 + (x - @last_point.first) / 3.0
167
+ cy2 = cy1 + (y - @last_point.last) / 3.0
168
+
169
+ @last_point = [x, y]
170
+ @previous_quadratic_control_point = [x1, y1]
171
+
172
+ @calls << ["curve_to", [x, y, cx1, cy1, cx2, cy2]]
173
+ end
174
+
175
+ when 'A'
176
+ # unsupported
177
+ end
178
+
179
+ @previous_control_point = nil unless %w(C S).include?(upcase_command)
180
+ @previous_quadratic_control_point = nil unless %w(Q T).include?(upcase_command)
181
+ end
182
+ end
183
+ end
184
+ end
metadata CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
6
6
  - 0
7
7
  - 9
8
8
  - 1
9
- - 6
10
- version: 0.9.1.6
9
+ - 7
10
+ version: 0.9.1.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Roger Nesbitt
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-03-31 00:00:00 +13:00
18
+ date: 2010-05-08 00:00:00 +12:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -57,10 +57,10 @@ extra_rdoc_files: []
57
57
  files:
58
58
  - README
59
59
  - LICENSE
60
+ - lib/prawn/svg/extension.rb
61
+ - lib/prawn/svg/interface.rb
62
+ - lib/prawn/svg/parser/path.rb
60
63
  - lib/prawn/svg/parser.rb
61
- - lib/prawn/svg/path.rb
62
- - lib/prawn/svg/svg.rb
63
- - lib/prawn/svg_document.rb
64
64
  - lib/prawn-svg.rb
65
65
  has_rdoc: true
66
66
  homepage: http://github.com/mogest/prawn-svg
@@ -1,180 +0,0 @@
1
- class Prawn::Svg::Parser::Path
2
- # Raised if the SVG path cannot be parsed.
3
- InvalidError = Class.new(StandardError)
4
-
5
- #
6
- # Parses an SVG path and returns a Prawn-compatible call tree.
7
- #
8
- def parse(data)
9
- cmd = values = nil
10
- value = ""
11
- @subpath_initial_point = @last_point = nil
12
- @previous_control_point = @previous_quadratic_control_point = nil
13
- @calls = []
14
-
15
- data.each_char do |c|
16
- if c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'
17
- values << value.to_f if value != ""
18
- run_path_command(cmd, values) if cmd
19
- cmd = c
20
- values = []
21
- value = ""
22
- elsif c >= '0' && c <= '9' || c == '.' || c == "-"
23
- unless cmd
24
- raise InvalidError, "Numerical value specified before character command in SVG path data"
25
- end
26
- value << c
27
- elsif c == ' ' || c == "\t" || c == "\r" || c == "\n" || c == ","
28
- if value != ""
29
- values << value.to_f
30
- value = ""
31
- end
32
- else
33
- raise InvalidError, "Invalid character '#{c}' in SVG path data"
34
- end
35
- end
36
-
37
- values << value.to_f if value != ""
38
- run_path_command(cmd, values) if cmd
39
-
40
- @calls
41
- end
42
-
43
-
44
- private
45
- def run_path_command(command, values)
46
- upcase_command = command.upcase
47
- relative = command != upcase_command
48
-
49
- case upcase_command
50
- when 'M' # moveto
51
- x = values.shift
52
- y = values.shift
53
-
54
- if relative && @last_point
55
- x += @last_point.first
56
- y += @last_point.last
57
- end
58
-
59
- @last_point = @subpath_initial_point = [x, y]
60
- @calls << ["move_to", @last_point]
61
-
62
- return run_path_command('L', values) if values.any?
63
-
64
- when 'Z' # closepath
65
- if @subpath_initial_point
66
- @calls << ["line_to", @subpath_initial_point]
67
- @last_point = @subpath_initial_point
68
- end
69
-
70
- when 'L' # lineto
71
- while values.any?
72
- x = values.shift
73
- y = values.shift
74
- if relative && @last_point
75
- x += @last_point.first
76
- y += @last_point.last
77
- end
78
- @last_point = [x, y]
79
- @calls << ["line_to", @last_point]
80
- end
81
-
82
- when 'H' # horizontal lineto
83
- while values.any?
84
- x = values.shift
85
- x += @last_point.first if relative && @last_point
86
- @last_point = [x, @last_point.last]
87
- @calls << ["line_to", @last_point]
88
- end
89
-
90
- when 'V' # vertical lineto
91
- while values.any?
92
- y = values.shift
93
- y += @last_point.last if relative && @last_point
94
- @last_point = [@last_point.first, y]
95
- @calls << ["line_to", @last_point]
96
- end
97
-
98
- when 'C' # curveto
99
- while values.any?
100
- x1, y1, x2, y2, x, y = (1..6).collect {values.shift}
101
- if relative && @last_point
102
- x += @last_point.first
103
- x1 += @last_point.first
104
- x2 += @last_point.first
105
- y += @last_point.last
106
- y1 += @last_point.last
107
- y2 += @last_point.last
108
- end
109
-
110
- @last_point = [x, y]
111
- @previous_control_point = [x2, y2]
112
- @calls << ["curve_to", [x, y, x1, y1, x2, y2]]
113
- end
114
-
115
- when 'S' # shorthand/smooth curveto
116
- while values.any?
117
- x2, y2, x, y = (1..4).collect {values.shift}
118
- if relative && @last_point
119
- x += @last_point.first
120
- x2 += @last_point.first
121
- y += @last_point.last
122
- y2 += @last_point.last
123
- end
124
-
125
- if @previous_control_point
126
- x1 = 2 * @last_point.first - @previous_control_point.first
127
- y1 = 2 * @last_point.last - @previous_control_point.last
128
- else
129
- x1, y1 = @last_point
130
- end
131
-
132
- @last_point = [x, y]
133
- @previous_control_point = [x2, y2]
134
- @calls << ["curve_to", [x, y, x1, y1, x2, y2]]
135
- end
136
-
137
- when 'Q', 'T' # quadratic curveto
138
- while values.any?
139
- if shorthand = upcase_command == 'T'
140
- x, y = (1..2).collect {values.shift}
141
- else
142
- x1, y1, x, y = (1..4).collect {values.shift}
143
- end
144
-
145
- if relative && @last_point
146
- x += @last_point.first
147
- x1 += @last_point.first if x1
148
- y += @last_point.last
149
- y1 += @last_point.last if y1
150
- end
151
-
152
- if shorthand
153
- if @previous_quadratic_control_point
154
- x1 = 2 * @last_point.first - @previous_quadratic_control_point.first
155
- y1 = 2 * @last_point.last - @previous_quadratic_control_point.last
156
- else
157
- x1, y1 = @last_point
158
- end
159
- end
160
-
161
- # convert from quadratic to cubic
162
- cx1 = @last_point.first + (x1 - @last_point.first) * 2 / 3.0
163
- cy1 = @last_point.last + (y1 - @last_point.last) * 2 / 3.0
164
- cx2 = cx1 + (x - @last_point.first) / 3.0
165
- cy2 = cy1 + (y - @last_point.last) / 3.0
166
-
167
- @last_point = [x, y]
168
- @previous_quadratic_control_point = [x1, y1]
169
-
170
- @calls << ["curve_to", [x, y, cx1, cy1, cx2, cy2]]
171
- end
172
-
173
- when 'A'
174
- # unsupported
175
- end
176
-
177
- @previous_control_point = nil unless %w(C S).include?(upcase_command)
178
- @previous_quadratic_control_point = nil unless %w(Q T).include?(upcase_command)
179
- end
180
- end
data/lib/prawn/svg/svg.rb DELETED
@@ -1,83 +0,0 @@
1
- #
2
- # Prawn::Svg makes a Prawn::Svg::Parser instance, uses that object to parse the supplied
3
- # SVG into Prawn-compatible method calls, and then calls the Prawn methods.
4
- #
5
- class Prawn::Svg
6
- DEFAULT_FONT_PATHS = ["/Library/Fonts", "/usr/share/fonts/truetype/**"]
7
-
8
- @font_path = []
9
- DEFAULT_FONT_PATHS.each {|path| @font_path << path if File.exists?(path)}
10
-
11
- class << self; attr_accessor :font_path; end
12
-
13
- attr_reader :data, :prawn, :parser, :options
14
-
15
- # An +Array+ of warnings that occurred while parsing the SVG data. If this array is non-empty,
16
- # it's likely that the SVG failed to render correctly.
17
- attr_reader :parser_warnings
18
-
19
- #
20
- # Creates a Prawn::Svg object.
21
- #
22
- # +data+ is the SVG data to convert. +prawn+ is your Prawn::Document object.
23
- #
24
- # +options+ must contain the key :at, which takes a tuple of x and y co-ordinates.
25
- #
26
- # +options+ can optionally contain the key :width or :height. If both are
27
- # specified, only :width will be used.
28
- #
29
- def initialize(data, prawn, options)
30
- @data = data
31
- @prawn = prawn
32
- @options = options
33
-
34
- @options[:at] or raise "options[:at] must be specified"
35
-
36
- @parser = Parser.new(data, [prawn.bounds.width, prawn.bounds.height], options)
37
- @parser_warnings = @parser.warnings
38
- end
39
-
40
- #
41
- # Draws the SVG to the Prawn::Document object.
42
- #
43
- def draw
44
- prawn.bounding_box(@options[:at], :width => @parser.width, :height => @parser.height) do
45
- prawn.save_graphics_state do
46
- proc_creator(prawn, @parser.parse).call
47
- end
48
- end
49
- end
50
-
51
-
52
- private
53
- def proc_creator(prawn, calls)
54
- Proc.new {issue_prawn_command(prawn, calls)}
55
- end
56
-
57
- def issue_prawn_command(prawn, calls)
58
- calls.each do |call, arguments, children|
59
- if rewrite_call_arguments(prawn, call, arguments) == false
60
- issue_prawn_command(prawn, children) if children.any?
61
- else
62
- if children.empty?
63
- prawn.send(call, *arguments)
64
- else
65
- prawn.send(call, *arguments, &proc_creator(prawn, children))
66
- end
67
- end
68
- end
69
- end
70
-
71
- def rewrite_call_arguments(prawn, call, arguments)
72
- case call
73
- when 'text_box'
74
- if (anchor = arguments.last.delete(:text_anchor)) && %w(middle end).include?(anchor)
75
- width = prawn.width_of(*arguments)
76
- width /= 2 if anchor == 'middle'
77
- arguments.last[:at][0] -= width
78
- end
79
-
80
- arguments.last[:at][1] += prawn.height_of(*arguments) / 3 * 2
81
- end
82
- end
83
- end