prawn-svg 0.9.1.3 → 0.9.1.4
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.
- data/README +5 -1
- data/lib/prawn/svg/parser.rb +96 -39
- data/lib/prawn/svg/svg.rb +15 -5
- metadata +3 -3
data/README
CHANGED
@@ -32,6 +32,8 @@ prawn-svg is in its infancy and does not support the full SVG specifications. I
|
|
32
32
|
attributes: size, text-anchor
|
33
33
|
partially supported attributes: font-family
|
34
34
|
|
35
|
+
- style tag, if css_parser gem is installed on the system [1]
|
36
|
+
|
35
37
|
- attributes/styles: fill, stroke, stroke-width, opacity, fill-opacity, stroke-opacity, transform
|
36
38
|
|
37
39
|
- transform methods: translate, rotate, scale
|
@@ -40,4 +42,6 @@ prawn-svg is in its infancy and does not support the full SVG specifications. I
|
|
40
42
|
|
41
43
|
- measurements specified in pt, cm, dm, ft, in, m, mm, yd
|
42
44
|
|
43
|
-
prawn-svg does NOT support
|
45
|
+
prawn-svg does NOT support named elements, external references, the tspan tag, gradients/patterns or markers.
|
46
|
+
|
47
|
+
[1] If the css_parser gem is installed, it will handle CSS style definitions, but only simple tag, class or id definitions.
|
data/lib/prawn/svg/parser.rb
CHANGED
@@ -12,6 +12,13 @@ require 'rexml/document'
|
|
12
12
|
# SVG to another format.
|
13
13
|
#
|
14
14
|
class Prawn::Svg::Parser
|
15
|
+
begin
|
16
|
+
require 'css_parser'
|
17
|
+
CSS_PARSER_LOADED = true
|
18
|
+
rescue LoadError
|
19
|
+
CSS_PARSER_LOADED = false
|
20
|
+
end
|
21
|
+
|
15
22
|
include Prawn::Measurements
|
16
23
|
|
17
24
|
attr_reader :width, :height
|
@@ -32,6 +39,7 @@ class Prawn::Svg::Parser
|
|
32
39
|
@data = data
|
33
40
|
@options = options
|
34
41
|
@warnings = []
|
42
|
+
@css_parser = CssParser::Parser.new if CSS_PARSER_LOADED
|
35
43
|
|
36
44
|
if data
|
37
45
|
parse_document
|
@@ -82,50 +90,34 @@ class Prawn::Svg::Parser
|
|
82
90
|
|
83
91
|
def parse_element(element, calls, state)
|
84
92
|
attrs = element.attributes
|
93
|
+
calls, style_attrs = apply_styles(element, calls, state)
|
85
94
|
|
86
|
-
if transform = attrs['transform']
|
87
|
-
parse_css_method_calls(transform).each do |name, arguments|
|
88
|
-
case name
|
89
|
-
when 'translate'
|
90
|
-
x, y = arguments
|
91
|
-
x, y = x.split(/\s+/) if y.nil?
|
92
|
-
calls << [name, [distance(x), -distance(y)], []]
|
93
|
-
calls = calls.last.last
|
94
|
-
when 'rotate'
|
95
|
-
calls << [name, [-arguments.first.to_f, {:origin => [0, y('0')]}], []]
|
96
|
-
calls = calls.last.last
|
97
|
-
when 'scale'
|
98
|
-
calls << [name, [arguments.first.to_f], []]
|
99
|
-
calls = calls.last.last
|
100
|
-
else
|
101
|
-
@warnings << "Unknown transformation '#{name}'; ignoring"
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
calls, style_attrs, draw_type = apply_styles(attrs, calls, state)
|
107
|
-
|
108
|
-
state[:draw_type] = draw_type if draw_type != ""
|
109
|
-
if state[:draw_type] && !%w(g svg).include?(element.name)
|
110
|
-
calls << [state[:draw_type], [], []]
|
111
|
-
calls = calls.last.last
|
112
|
-
end
|
113
|
-
|
114
95
|
if required_attributes = REQUIRED_ATTRIBUTES[element.name]
|
115
96
|
return unless check_attrs_present(element, required_attributes)
|
116
97
|
end
|
117
|
-
|
98
|
+
|
118
99
|
case element.name
|
119
|
-
when '
|
120
|
-
# ignore
|
100
|
+
when 'title', 'desc'
|
101
|
+
# ignore
|
121
102
|
|
122
103
|
when 'g', 'svg'
|
123
104
|
element.elements.each do |child|
|
124
105
|
parse_element(child, calls, state.dup)
|
125
106
|
end
|
107
|
+
|
108
|
+
when 'defs'
|
109
|
+
# Pass calls as a blank array so that nothing under this tag can be added to our call tree.
|
110
|
+
element.elements.each do |child|
|
111
|
+
parse_element(child, [], state.dup.merge(:display => false))
|
112
|
+
end
|
113
|
+
|
114
|
+
when 'style'
|
115
|
+
load_css_styles(element)
|
126
116
|
|
127
117
|
when 'text'
|
128
|
-
#
|
118
|
+
# Very primitive support for font-family; it won't work in most cases because
|
119
|
+
# PDF only has a few built-in fonts, and they're not the same as the names
|
120
|
+
# used typically with the web fonts.
|
129
121
|
if (font = style_attrs['font-family']) && !font.match(/[\/\\]/)
|
130
122
|
font = font.strip
|
131
123
|
if font != ""
|
@@ -139,7 +131,8 @@ class Prawn::Svg::Parser
|
|
139
131
|
opts[:size] = size.to_f * @scale
|
140
132
|
end
|
141
133
|
|
142
|
-
# This is not a prawn option but we can't work out how to render it here -
|
134
|
+
# This is not a prawn option but we can't work out how to render it here -
|
135
|
+
# it's handled by Svg#rewrite_call_arguments
|
143
136
|
if anchor = style_attrs['text-anchor']
|
144
137
|
opts[:text_anchor] = anchor
|
145
138
|
end
|
@@ -213,6 +206,18 @@ class Prawn::Svg::Parser
|
|
213
206
|
end
|
214
207
|
end
|
215
208
|
|
209
|
+
def load_css_styles(element)
|
210
|
+
if @css_parser
|
211
|
+
data = if element.cdatas.any?
|
212
|
+
element.cdatas.collect(&:to_s).join
|
213
|
+
else
|
214
|
+
element.text
|
215
|
+
end
|
216
|
+
|
217
|
+
@css_parser.add_block!(data)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
216
221
|
def parse_css_declarations(declarations)
|
217
222
|
# copied from css_parser
|
218
223
|
declarations.gsub!(/(^[\s]*)|([\s]*$)/, '')
|
@@ -227,11 +232,55 @@ class Prawn::Svg::Parser
|
|
227
232
|
end
|
228
233
|
end
|
229
234
|
|
230
|
-
def
|
231
|
-
|
235
|
+
def determine_style_for(element)
|
236
|
+
if @css_parser
|
237
|
+
tag_style = @css_parser.find_by_selector(element.name)
|
238
|
+
id_style = @css_parser.find_by_selector("##{element.attributes["id"]}") if element.attributes["id"]
|
239
|
+
|
240
|
+
if classes = element.attributes["class"]
|
241
|
+
class_styles = classes.strip.split(/\s+/).collect do |class_name|
|
242
|
+
@css_parser.find_by_selector(".#{class_name}")
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
element_style = element.attributes['style']
|
247
|
+
|
248
|
+
style = [tag_style, class_styles, id_style, element_style].flatten.collect do |s|
|
249
|
+
s.nil? || s.strip == "" ? "" : "#{s}#{";" unless s.match(/;\s*\z/)}"
|
250
|
+
end.join
|
251
|
+
else
|
252
|
+
style = element.attributes['style'] || ""
|
253
|
+
end
|
232
254
|
|
233
|
-
decs =
|
234
|
-
|
255
|
+
decs = parse_css_declarations(style)
|
256
|
+
element.attributes.each {|n,v| decs[n] = v unless decs[n]}
|
257
|
+
decs
|
258
|
+
end
|
259
|
+
|
260
|
+
def apply_styles(element, calls, state)
|
261
|
+
decs = determine_style_for(element)
|
262
|
+
draw_types = []
|
263
|
+
|
264
|
+
# Transform
|
265
|
+
if transform = decs['transform']
|
266
|
+
parse_css_method_calls(transform).each do |name, arguments|
|
267
|
+
case name
|
268
|
+
when 'translate'
|
269
|
+
x, y = arguments
|
270
|
+
x, y = x.split(/\s+/) if y.nil?
|
271
|
+
calls << [name, [distance(x), -distance(y)], []]
|
272
|
+
calls = calls.last.last
|
273
|
+
when 'rotate'
|
274
|
+
calls << [name, [-arguments.first.to_f, {:origin => [0, y('0')]}], []]
|
275
|
+
calls = calls.last.last
|
276
|
+
when 'scale'
|
277
|
+
calls << [name, [arguments.first.to_f], []]
|
278
|
+
calls = calls.last.last
|
279
|
+
else
|
280
|
+
@warnings << "Unknown transformation '#{name}'; ignoring"
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
235
284
|
|
236
285
|
# Opacity:
|
237
286
|
# We can't do nested opacities quite like the SVG requires, but this is close enough.
|
@@ -247,6 +296,7 @@ class Prawn::Svg::Parser
|
|
247
296
|
calls = calls.last.last
|
248
297
|
end
|
249
298
|
|
299
|
+
# Fill and stroke
|
250
300
|
if decs['fill'] && decs['fill'] != "none"
|
251
301
|
if color = color_to_hex(decs['fill'])
|
252
302
|
calls << ['fill_color', [color], []]
|
@@ -261,9 +311,16 @@ class Prawn::Svg::Parser
|
|
261
311
|
draw_types << 'stroke'
|
262
312
|
end
|
263
313
|
|
264
|
-
calls << ['line_width', [distance(decs['stroke-width'])], []] if decs['stroke-width']
|
314
|
+
calls << ['line_width', [distance(decs['stroke-width'])], []] if decs['stroke-width']
|
315
|
+
|
316
|
+
draw_type = draw_types.join("_and_")
|
317
|
+
state[:draw_type] = draw_type if draw_type != ""
|
318
|
+
if state[:draw_type] && !%w(g svg).include?(element.name)
|
319
|
+
calls << [state[:draw_type], [], []]
|
320
|
+
calls = calls.last.last
|
321
|
+
end
|
265
322
|
|
266
|
-
[calls, decs
|
323
|
+
[calls, decs]
|
267
324
|
end
|
268
325
|
|
269
326
|
def parse_css_method_calls(string)
|
data/lib/prawn/svg/svg.rb
CHANGED
@@ -49,17 +49,21 @@ class Prawn::Svg
|
|
49
49
|
|
50
50
|
def issue_prawn_command(prawn, calls)
|
51
51
|
calls.each do |call, arguments, children|
|
52
|
-
if
|
53
|
-
|
54
|
-
prawn.send(call, *arguments)
|
52
|
+
if rewrite_call_arguments(prawn, call, arguments) == false
|
53
|
+
issue_prawn_command(prawn, children) if children.any?
|
55
54
|
else
|
56
|
-
|
55
|
+
if children.empty?
|
56
|
+
prawn.send(call, *arguments)
|
57
|
+
else
|
58
|
+
prawn.send(call, *arguments, &proc_creator(prawn, children))
|
59
|
+
end
|
57
60
|
end
|
58
61
|
end
|
59
62
|
end
|
60
63
|
|
61
64
|
def rewrite_call_arguments(prawn, call, arguments)
|
62
|
-
|
65
|
+
case call
|
66
|
+
when 'text_box'
|
63
67
|
if (anchor = arguments.last.delete(:text_anchor)) && %w(middle end).include?(anchor)
|
64
68
|
width = prawn.width_of(*arguments)
|
65
69
|
width /= 2 if anchor == 'middle'
|
@@ -67,6 +71,12 @@ class Prawn::Svg
|
|
67
71
|
end
|
68
72
|
|
69
73
|
arguments.last[:at][1] += prawn.height_of(*arguments) / 3 * 2
|
74
|
+
|
75
|
+
when 'font'
|
76
|
+
unless prawn.font_families.member?(arguments.first)
|
77
|
+
@parser_warnings << "#{arguments.first} is not a known font."
|
78
|
+
false
|
79
|
+
end
|
70
80
|
end
|
71
81
|
end
|
72
82
|
end
|
metadata
CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
|
|
6
6
|
- 0
|
7
7
|
- 9
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.9.1.
|
9
|
+
- 4
|
10
|
+
version: 0.9.1.4
|
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-
|
18
|
+
date: 2010-03-31 00:00:00 +13:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|