prawnx 0.1.1

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.
Files changed (160) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +2 -0
  3. data/GPLv2 +339 -0
  4. data/GPLv3 +674 -0
  5. data/LICENSE +65 -0
  6. data/LICENSE-prawn-svg +21 -0
  7. data/README.md +107 -0
  8. data/data/fonts/Courier-Bold.afm +342 -0
  9. data/data/fonts/Courier-BoldOblique.afm +342 -0
  10. data/data/fonts/Courier-Oblique.afm +342 -0
  11. data/data/fonts/Courier.afm +342 -0
  12. data/data/fonts/Helvetica-Bold.afm +2827 -0
  13. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  14. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  15. data/data/fonts/Helvetica.afm +3051 -0
  16. data/data/fonts/MustRead.html +19 -0
  17. data/data/fonts/Symbol.afm +213 -0
  18. data/data/fonts/Times-Bold.afm +2588 -0
  19. data/data/fonts/Times-BoldItalic.afm +2384 -0
  20. data/data/fonts/Times-Italic.afm +2667 -0
  21. data/data/fonts/Times-Roman.afm +2419 -0
  22. data/data/fonts/ZapfDingbats.afm +225 -0
  23. data/lib/pdf/core.rb +4 -0
  24. data/lib/prawn/measurement_extensions.rb +4 -0
  25. data/lib/prawn/svg/attributes/clip_path.rb +46 -0
  26. data/lib/prawn/svg/attributes/mask.rb +16 -0
  27. data/lib/prawn/svg/attributes/opacity.rb +30 -0
  28. data/lib/prawn/svg/attributes/space.rb +10 -0
  29. data/lib/prawn/svg/attributes/stroke.rb +50 -0
  30. data/lib/prawn/svg/attributes/transform.rb +10 -0
  31. data/lib/prawn/svg/attributes.rb +6 -0
  32. data/lib/prawn/svg/calculators/arc_to_bezier_curve.rb +57 -0
  33. data/lib/prawn/svg/calculators/aspect_ratio.rb +60 -0
  34. data/lib/prawn/svg/calculators/document_sizing.rb +103 -0
  35. data/lib/prawn/svg/calculators/path_length.rb +168 -0
  36. data/lib/prawn/svg/calculators/pixels.rb +57 -0
  37. data/lib/prawn/svg/color.rb +240 -0
  38. data/lib/prawn/svg/css/font_face_parser.rb +72 -0
  39. data/lib/prawn/svg/css/font_family_parser.rb +39 -0
  40. data/lib/prawn/svg/css/font_parser.rb +46 -0
  41. data/lib/prawn/svg/css/selector_parser.rb +178 -0
  42. data/lib/prawn/svg/css/stylesheets.rb +233 -0
  43. data/lib/prawn/svg/css/values_parser.rb +68 -0
  44. data/lib/prawn/svg/document.rb +170 -0
  45. data/lib/prawn/svg/elements/anchor.rb +10 -0
  46. data/lib/prawn/svg/elements/base.rb +312 -0
  47. data/lib/prawn/svg/elements/bbox_scaling.rb +78 -0
  48. data/lib/prawn/svg/elements/call_duplicator.rb +37 -0
  49. data/lib/prawn/svg/elements/circle.rb +25 -0
  50. data/lib/prawn/svg/elements/clip_path.rb +26 -0
  51. data/lib/prawn/svg/elements/container.rb +10 -0
  52. data/lib/prawn/svg/elements/direct_render_base.rb +27 -0
  53. data/lib/prawn/svg/elements/ellipse.rb +22 -0
  54. data/lib/prawn/svg/elements/gradient.rb +184 -0
  55. data/lib/prawn/svg/elements/ignored.rb +5 -0
  56. data/lib/prawn/svg/elements/image.rb +177 -0
  57. data/lib/prawn/svg/elements/line.rb +27 -0
  58. data/lib/prawn/svg/elements/marker.rb +72 -0
  59. data/lib/prawn/svg/elements/mask.rb +65 -0
  60. data/lib/prawn/svg/elements/path.rb +294 -0
  61. data/lib/prawn/svg/elements/pattern.rb +150 -0
  62. data/lib/prawn/svg/elements/polygon.rb +25 -0
  63. data/lib/prawn/svg/elements/polyline.rb +23 -0
  64. data/lib/prawn/svg/elements/rect.rb +64 -0
  65. data/lib/prawn/svg/elements/root.rb +22 -0
  66. data/lib/prawn/svg/elements/switch.rb +92 -0
  67. data/lib/prawn/svg/elements/text.rb +99 -0
  68. data/lib/prawn/svg/elements/text_component.rb +346 -0
  69. data/lib/prawn/svg/elements/text_node.rb +308 -0
  70. data/lib/prawn/svg/elements/text_path.rb +153 -0
  71. data/lib/prawn/svg/elements/use.rb +142 -0
  72. data/lib/prawn/svg/elements/viewport.rb +29 -0
  73. data/lib/prawn/svg/elements.rb +46 -0
  74. data/lib/prawn/svg/extension.rb +25 -0
  75. data/lib/prawn/svg/font.rb +20 -0
  76. data/lib/prawn/svg/font_metrics.rb +144 -0
  77. data/lib/prawn/svg/font_registry.rb +315 -0
  78. data/lib/prawn/svg/funciri.rb +14 -0
  79. data/lib/prawn/svg/gradient_renderer.rb +332 -0
  80. data/lib/prawn/svg/gradients.rb +53 -0
  81. data/lib/prawn/svg/interface.rb +72 -0
  82. data/lib/prawn/svg/length.rb +43 -0
  83. data/lib/prawn/svg/link_renderer.rb +40 -0
  84. data/lib/prawn/svg/loaders/data.rb +17 -0
  85. data/lib/prawn/svg/loaders/file.rb +111 -0
  86. data/lib/prawn/svg/loaders/web.rb +24 -0
  87. data/lib/prawn/svg/paint.rb +67 -0
  88. data/lib/prawn/svg/pathable.rb +153 -0
  89. data/lib/prawn/svg/pattern_renderer.rb +112 -0
  90. data/lib/prawn/svg/pdfmatrix.rb +37 -0
  91. data/lib/prawn/svg/percentage.rb +24 -0
  92. data/lib/prawn/svg/properties.rb +329 -0
  93. data/lib/prawn/svg/renderer.rb +249 -0
  94. data/lib/prawn/svg/state.rb +24 -0
  95. data/lib/prawn/svg/transform_parser.rb +78 -0
  96. data/lib/prawn/svg/ttc.rb +35 -0
  97. data/lib/prawn/svg/ttf.rb +95 -0
  98. data/lib/prawn/svg/url_loader.rb +46 -0
  99. data/lib/prawn/svg/version.rb +5 -0
  100. data/lib/prawn/table.rb +4 -0
  101. data/lib/prawn-svg.rb +53 -0
  102. data/lib/prawn.rb +10 -0
  103. data/lib/prawnx/annotations.rb +21 -0
  104. data/lib/prawnx/document/bounding_box.rb +193 -0
  105. data/lib/prawnx/document/column_box.rb +91 -0
  106. data/lib/prawnx/document/internals.rb +144 -0
  107. data/lib/prawnx/document/span.rb +25 -0
  108. data/lib/prawnx/document.rb +253 -0
  109. data/lib/prawnx/encoding.rb +29 -0
  110. data/lib/prawnx/errors.rb +37 -0
  111. data/lib/prawnx/font.rb +214 -0
  112. data/lib/prawnx/fonts/afm.rb +171 -0
  113. data/lib/prawnx/fonts/otf.rb +50 -0
  114. data/lib/prawnx/fonts/ttf.rb +410 -0
  115. data/lib/prawnx/graphic_state.rb +41 -0
  116. data/lib/prawnx/graphics/color.rb +148 -0
  117. data/lib/prawnx/graphics/patterns.rb +100 -0
  118. data/lib/prawnx/graphics/style.rb +171 -0
  119. data/lib/prawnx/graphics.rb +208 -0
  120. data/lib/prawnx/grid.rb +216 -0
  121. data/lib/prawnx/images/image.rb +244 -0
  122. data/lib/prawnx/images/png_decoder.rb +65 -0
  123. data/lib/prawnx/images.rb +115 -0
  124. data/lib/prawnx/measurement_extensions.rb +40 -0
  125. data/lib/prawnx/measurements.rb +62 -0
  126. data/lib/prawnx/outline.rb +146 -0
  127. data/lib/prawnx/page.rb +125 -0
  128. data/lib/prawnx/page_geometry.rb +27 -0
  129. data/lib/prawnx/pdf/outline_node.rb +53 -0
  130. data/lib/prawnx/pdf/reference.rb +31 -0
  131. data/lib/prawnx/pdf/writer.rb +105 -0
  132. data/lib/prawnx/pdf.rb +66 -0
  133. data/lib/prawnx/pdf_a/srgb_profile.rb +61 -0
  134. data/lib/prawnx/pdf_a.rb +130 -0
  135. data/lib/prawnx/pdf_core.rb +25 -0
  136. data/lib/prawnx/repeater.rb +79 -0
  137. data/lib/prawnx/security.rb +108 -0
  138. data/lib/prawnx/stamp.rb +74 -0
  139. data/lib/prawnx/table/cell/kinds.rb +246 -0
  140. data/lib/prawnx/table/cell.rb +331 -0
  141. data/lib/prawnx/table/cells.rb +208 -0
  142. data/lib/prawnx/table.rb +296 -0
  143. data/lib/prawnx/text/box.rb +16 -0
  144. data/lib/prawnx/text/formatted/arranger.rb +217 -0
  145. data/lib/prawnx/text/formatted/box.rb +312 -0
  146. data/lib/prawnx/text/formatted/fragment.rb +205 -0
  147. data/lib/prawnx/text/formatted/line_wrap.rb +204 -0
  148. data/lib/prawnx/text/formatted/parser.rb +149 -0
  149. data/lib/prawnx/text/formatted/wrap.rb +103 -0
  150. data/lib/prawnx/text.rb +281 -0
  151. data/lib/prawnx/transformation_stack.rb +43 -0
  152. data/lib/prawnx/true_type/cff_subset.rb +157 -0
  153. data/lib/prawnx/true_type/file.rb +371 -0
  154. data/lib/prawnx/true_type/open_type_layout.rb +189 -0
  155. data/lib/prawnx/true_type/simple_subsets.rb +94 -0
  156. data/lib/prawnx/true_type/subset.rb +187 -0
  157. data/lib/prawnx/version.rb +7 -0
  158. data/lib/prawnx/view.rb +28 -0
  159. data/lib/prawnx.rb +102 -0
  160. metadata +251 -0
@@ -0,0 +1,312 @@
1
+ class Prawn::SVG::Elements::Base
2
+ extend Forwardable
3
+
4
+ include Prawn::SVG::Elements::CallDuplicator
5
+ include Prawn::SVG::Elements::BboxScaling
6
+
7
+ include Prawn::SVG::Calculators::Pixels
8
+
9
+ include Prawn::SVG::Attributes::Transform
10
+ include Prawn::SVG::Attributes::Opacity
11
+ include Prawn::SVG::Attributes::ClipPath
12
+ include Prawn::SVG::Attributes::Mask
13
+ include Prawn::SVG::Attributes::Stroke
14
+ include Prawn::SVG::Attributes::Space
15
+
16
+ include Prawn::SVG::TransformParser
17
+
18
+ PAINT_TYPES = %w[fill stroke].freeze
19
+ COMMA_WSP_REGEXP = Prawn::SVG::Elements::COMMA_WSP_REGEXP
20
+ SVG_NAMESPACE = 'http://www.w3.org/2000/svg'.freeze
21
+
22
+ SkipElementQuietly = Class.new(StandardError)
23
+ SkipElementError = Class.new(StandardError)
24
+ MissingAttributesError = Class.new(SkipElementError)
25
+
26
+ attr_reader :document, :source, :parent_calls, :base_calls, :state, :attributes, :properties
27
+ attr_accessor :calls
28
+
29
+ def_delegators :@document, :warnings
30
+ def_delegator :@state, :computed_properties
31
+
32
+ def initialize(document, source, parent_calls, state)
33
+ @document = document
34
+ @source = source
35
+ @parent_calls = parent_calls
36
+ @state = state
37
+ @base_calls = @calls = []
38
+ @attributes = {}
39
+ @properties = Prawn::SVG::Properties.new
40
+
41
+ if source && add_to_elements_by_id? && !state.inside_use
42
+ id = source.attributes['id']
43
+ id = id.strip if id
44
+
45
+ document.elements_by_id[id] = self if id && id != ''
46
+ end
47
+ end
48
+
49
+ def process
50
+ extract_attributes_and_properties
51
+ parse_and_apply
52
+ end
53
+
54
+ def parse_and_apply
55
+ parse_standard_attributes
56
+ parse
57
+
58
+ apply_calls_from_standard_attributes
59
+ apply
60
+
61
+ if state.anchor_href && bounding_box
62
+ add_call('svg:add_link', state.anchor_href, bounding_box)
63
+ end
64
+
65
+ process_child_elements if container?
66
+
67
+ append_calls_to_parent unless computed_properties.display == 'none'
68
+ rescue SkipElementQuietly
69
+ rescue SkipElementError => e
70
+ @document.warnings << e.message
71
+ end
72
+
73
+ def name
74
+ @name ||= source ? source.name : '???'
75
+ end
76
+
77
+ protected
78
+
79
+ def parse; end
80
+
81
+ def apply; end
82
+
83
+ def bounding_box; end
84
+
85
+ def container?
86
+ false
87
+ end
88
+
89
+ def drawable?
90
+ !container?
91
+ end
92
+
93
+ def parse_standard_attributes
94
+ parse_xml_space_attribute
95
+ end
96
+
97
+ def add_call(name, *arguments, **kwarguments)
98
+ @calls << [name.to_s, arguments, kwarguments, []]
99
+ end
100
+
101
+ def add_call_and_enter(name, *arguments, **kwarguments)
102
+ @calls << [name.to_s, arguments, kwarguments, []]
103
+ @calls = @calls.last.last
104
+ end
105
+
106
+ def push_call_position
107
+ @call_positions ||= []
108
+ @call_positions << @calls
109
+ end
110
+
111
+ def pop_call_position
112
+ @calls = @call_positions.pop
113
+ end
114
+
115
+ def append_calls_to_parent
116
+ @parent_calls.concat(@base_calls)
117
+ end
118
+
119
+ def add_calls_from_element(other)
120
+ @calls.concat duplicate_calls(other.base_calls)
121
+ end
122
+
123
+ def add_yield_call(&block)
124
+ add_call('svg:yield', block)
125
+ end
126
+
127
+ def new_call_context_from_base
128
+ old_calls = @calls
129
+ @calls = @base_calls
130
+ yield
131
+ @calls = old_calls
132
+ end
133
+
134
+ def process_child_elements
135
+ return unless source
136
+
137
+ svg_child_elements.each do |elem|
138
+ if (element_class = Prawn::SVG::Elements::TAG_CLASS_MAPPING[elem.name.to_sym])
139
+ add_call 'save'
140
+
141
+ child = element_class.new(@document, elem, @calls, state.dup)
142
+ child.process
143
+
144
+ add_call 'restore'
145
+ else
146
+ @document.warnings << "Unknown tag '#{elem.name}'; ignoring"
147
+ end
148
+ end
149
+ end
150
+
151
+ def svg_child_elements
152
+ source.elements.select do |elem|
153
+ # To be strict, we shouldn't treat namespace-less elements as SVG, but for
154
+ # backwards compatibility, and because it doesn't hurt, we will.
155
+ [SVG_NAMESPACE, ''].include?(elem.namespace)
156
+ end
157
+ end
158
+
159
+ def apply_calls_from_standard_attributes
160
+ parse_transform_attribute_and_call
161
+ parse_opacity_attributes_and_call
162
+ parse_clip_path_attribute_and_call
163
+ parse_mask_attribute_and_call
164
+ apply_colors
165
+ parse_stroke_attributes_and_call
166
+ apply_drawing_call
167
+ end
168
+
169
+ def apply_drawing_call
170
+ return if state.disable_drawing || !drawable?
171
+
172
+ fill = !computed_properties.fill.none? # rubocop:disable Style/InverseMethods
173
+ stroke = !computed_properties.stroke.none? # rubocop:disable Style/InverseMethods
174
+
175
+ if fill
176
+ command = stroke ? 'fill_and_stroke' : 'fill'
177
+
178
+ if computed_properties.fill_rule == 'evenodd'
179
+ add_call_and_enter(command, fill_rule: :even_odd)
180
+ else
181
+ add_call_and_enter(command)
182
+ end
183
+ elsif stroke
184
+ add_call_and_enter('stroke')
185
+ else
186
+ add_call_and_enter('end_path')
187
+ end
188
+ end
189
+
190
+ def apply_colors
191
+ PAINT_TYPES.each do |type|
192
+ paint = properties.send(type)
193
+
194
+ case paint
195
+ when nil, 'inherit'
196
+ next
197
+ when Prawn::SVG::Paint
198
+ color = paint.resolve(document.gradients, computed_properties.color, document.color_mode)
199
+
200
+ case color
201
+ when Prawn::SVG::Color::RGB, Prawn::SVG::Color::CMYK
202
+ add_call "#{type}_color", color.value
203
+ when Prawn::SVG::Elements::Gradient
204
+ add_call 'svg:render_gradient', type.to_sym, **color.gradient_arguments(self)
205
+ when Prawn::SVG::Elements::Pattern
206
+ args = color.pattern_arguments(self)
207
+ add_call 'svg:render_pattern', type.to_sym, **args if args
208
+ when nil
209
+ nil
210
+ else
211
+ raise "Unknown resolved color type: #{color.inspect}"
212
+ end
213
+ else
214
+ raise "Unknown paint type: #{paint.inspect}"
215
+ end
216
+ end
217
+ end
218
+
219
+ def extract_attributes_and_properties
220
+ # Apply user agent stylesheet
221
+ if %w[svg symbol image marker pattern foreignObject].include?(source.name)
222
+ @properties.set('overflow', 'hidden')
223
+ end
224
+
225
+ # Apply presentation attributes, and set attributes that aren't presentation attributes
226
+ source.attributes.each do |name, value|
227
+ # Properties#set returns nil if it's not a recognised property name
228
+ @properties.set(name, value) or @attributes[name] = value
229
+ end
230
+
231
+ # Apply stylesheet styles
232
+ if (styles = document.element_styles[source])
233
+ styles.each do |name, value, important|
234
+ @properties.set(name, value, important: important)
235
+ end
236
+ end
237
+
238
+ # Apply inline styles
239
+ @properties.load_hash(parse_css_declarations(source.attributes['style'] || ''))
240
+
241
+ state.computed_properties.compute_properties(@properties)
242
+ end
243
+
244
+ def parse_css_declarations(declarations)
245
+ # copied from css_parser
246
+ declarations
247
+ .gsub(/(^\s*)|(\s*$)/, '')
248
+ .split(/[;$]+/m)
249
+ .each_with_object({}) do |decs, output|
250
+ if (matches = decs.match(/\s*(.[^:]*)\s*:\s*(.[^;]*)\s*(;|\Z)/i))
251
+ property, value, = matches.captures
252
+ output[property.downcase] = value
253
+ end
254
+ end
255
+ end
256
+
257
+ def require_attributes(*names)
258
+ missing_attrs = names - attributes.keys
259
+ if missing_attrs.any?
260
+ raise MissingAttributesError, "Must have attributes #{missing_attrs.join(', ')} on tag #{name}; skipping tag"
261
+ end
262
+ end
263
+
264
+ def require_positive_value(*args)
265
+ if args.any? { |arg| arg.nil? || arg <= 0 }
266
+ raise SkipElementError, "Invalid attributes on tag #{name}; skipping tag"
267
+ end
268
+ end
269
+
270
+ def extract_element_from_url_id_reference(value, expected_type = nil)
271
+ case value
272
+ when Prawn::SVG::FuncIRI
273
+ element = document.elements_by_id[value.url[1..]] if value.url.start_with?('#')
274
+ element if element && (expected_type.nil? || element.name == expected_type)
275
+ end
276
+ end
277
+
278
+ def href_attribute
279
+ attributes['xlink:href'] || attributes['href']
280
+ end
281
+
282
+ def overflow_hidden?
283
+ ['hidden', 'scroll'].include?(computed_properties.overflow)
284
+ end
285
+
286
+ def transformable?
287
+ true
288
+ end
289
+
290
+ def add_to_elements_by_id?
291
+ true
292
+ end
293
+
294
+ def stroke_width
295
+ if computed_properties.stroke.none?
296
+ 0
297
+ else
298
+ pixels(properties.stroke_width) || 1.0
299
+ end
300
+ end
301
+
302
+ def clone_element_source(source)
303
+ new_source = source.dup
304
+ document.element_styles[new_source] = document.element_styles[source]
305
+ new_source
306
+ end
307
+
308
+ def set_display_none
309
+ properties.display = 'none'
310
+ computed_properties.display = 'none'
311
+ end
312
+ end
@@ -0,0 +1,78 @@
1
+ module Prawn::SVG::Elements::BboxScaling
2
+ private
3
+
4
+ def build_object_bounding_box_calls(bbox, source_element = source)
5
+ bbox_left = bbox[0]
6
+ bbox_top = bbox[1]
7
+ bbox_right = bbox[2]
8
+ bbox_bottom = bbox[3]
9
+ bbox_w = bbox_right - bbox_left
10
+ bbox_h = bbox_top - bbox_bottom
11
+
12
+ unit_sizing = Prawn::SVG::Calculators::DocumentSizing.new([1, 1])
13
+ unit_sizing.document_width = 1
14
+ unit_sizing.document_height = 1
15
+ unit_sizing.calculate
16
+
17
+ result_calls = document.with_sizing(unit_sizing) do
18
+ new_state = state.dup
19
+ new_state.viewport_sizing = unit_sizing
20
+ new_state.inside_use = true
21
+
22
+ container = Prawn::SVG::Elements::Container.new(document, source_element, [], new_state)
23
+ container.process
24
+
25
+ container.base_calls
26
+ end
27
+
28
+ scale_calls_to_bbox(duplicate_calls(result_calls), bbox_left, bbox_top, bbox_w, bbox_h)
29
+ end
30
+
31
+ def scale_calls_to_bbox(calls, bbox_left, bbox_top, bbox_w, bbox_h)
32
+ calls.map do |name, args, kwargs, children|
33
+ new_args = case name
34
+ when 'rectangle'
35
+ point, width, height = args
36
+ [scale_point(point, bbox_left, bbox_top, bbox_w, bbox_h), width * bbox_w, height * bbox_h]
37
+ when 'rounded_rectangle'
38
+ point, width, height, radius = args
39
+ [scale_point(point, bbox_left, bbox_top, bbox_w, bbox_h), width * bbox_w, height * bbox_h, radius * bbox_w]
40
+ when 'move_to', 'line_to'
41
+ [scale_point(args[0], bbox_left, bbox_top, bbox_w, bbox_h)]
42
+ when 'circle'
43
+ point, radius = args
44
+ scaled_point = scale_point(point, bbox_left, bbox_top, bbox_w, bbox_h)
45
+ if bbox_w == bbox_h
46
+ [scaled_point, radius * bbox_w]
47
+ else
48
+ new_children = children.any? ? scale_calls_to_bbox(children, bbox_left, bbox_top, bbox_w, bbox_h) : children
49
+ next ['ellipse', [scaled_point, radius * bbox_w, radius * bbox_h], kwargs, new_children]
50
+ end
51
+ when 'ellipse'
52
+ point, rx, ry = args
53
+ [scale_point(point, bbox_left, bbox_top, bbox_w, bbox_h), rx * bbox_w, ry * bbox_h]
54
+ when 'curve_to'
55
+ dest = scale_point(args[0], bbox_left, bbox_top, bbox_w, bbox_h)
56
+ [dest]
57
+ else
58
+ args
59
+ end
60
+
61
+ new_kwargs = if name == 'curve_to' && kwargs[:bounds]
62
+ b = kwargs[:bounds]
63
+ { bounds: b.map { |p| scale_point(p, bbox_left, bbox_top, bbox_w, bbox_h) } }
64
+ else
65
+ kwargs
66
+ end
67
+
68
+ new_children = children.any? ? scale_calls_to_bbox(children, bbox_left, bbox_top, bbox_w, bbox_h) : children
69
+ [name, new_args, new_kwargs, new_children]
70
+ end
71
+ end
72
+
73
+ def scale_point(point, bbox_left, bbox_top, bbox_w, bbox_h)
74
+ x = bbox_left + (point[0] * bbox_w)
75
+ y = bbox_top - ((1.0 - point[1]) * bbox_h)
76
+ [x, y]
77
+ end
78
+ end
@@ -0,0 +1,37 @@
1
+ #
2
+ # Unfortunately, prawn mutates arguments passed in to it.
3
+ # When we make a copy of one of the call stacks, we need to make a deep
4
+ # duplicate of it so that the first time prawn mutates the arguments, it
5
+ # won't affect the subsequent calls.
6
+ #
7
+ module Prawn::SVG::Elements::CallDuplicator
8
+ private
9
+
10
+ def duplicate_calls(calls)
11
+ calls.map { |call| duplicate_call(call) }
12
+ end
13
+
14
+ def duplicate_call(call)
15
+ [call[0], duplicate_array(call[1]), duplicate_hash(call[2]), duplicate_calls(call[3])]
16
+ end
17
+
18
+ def duplicate_array(array)
19
+ array.map do |value|
20
+ case value
21
+ when Array then duplicate_array(value)
22
+ when Hash then duplicate_hash(value)
23
+ else value
24
+ end
25
+ end
26
+ end
27
+
28
+ def duplicate_hash(hash)
29
+ hash.each.with_object({}) do |(key, value), result|
30
+ result[key] = case value
31
+ when Array then duplicate_array(value)
32
+ when Hash then duplicate_hash(value)
33
+ else value
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ class Prawn::SVG::Elements::Circle < Prawn::SVG::Elements::Base
2
+ USE_NEW_CIRCLE_CALL = Prawn::Document.instance_methods.include?(:circle)
3
+
4
+ def parse
5
+ require_attributes 'r'
6
+
7
+ @x = x(attributes['cx'] || '0')
8
+ @y = y(attributes['cy'] || '0')
9
+ @r = pixels(attributes['r'])
10
+
11
+ require_positive_value @r
12
+ end
13
+
14
+ def apply
15
+ if USE_NEW_CIRCLE_CALL
16
+ add_call 'circle', [@x, @y], @r
17
+ else
18
+ add_call 'circle_at', [@x, @y], radius: @r
19
+ end
20
+ end
21
+
22
+ def bounding_box
23
+ [@x - @r, @y + @r, @x + @r, @y - @r]
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ class Prawn::SVG::Elements::ClipPath < Prawn::SVG::Elements::Base
2
+ def parse
3
+ state.inside_clip_path = true
4
+ set_display_none
5
+ end
6
+
7
+ def container?
8
+ true
9
+ end
10
+
11
+ def build_clip_calls(element)
12
+ units = attributes['clipPathUnits'] || 'userSpaceOnUse'
13
+
14
+ if units == 'objectBoundingBox'
15
+ bbox = element.bounding_box
16
+ if bbox.nil?
17
+ document.warnings << 'clipPath with clipPathUnits="objectBoundingBox" requires element to have a bounding box'
18
+ return nil
19
+ end
20
+
21
+ build_object_bounding_box_calls(bbox)
22
+ else
23
+ duplicate_calls(base_calls)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ class Prawn::SVG::Elements::Container < Prawn::SVG::Elements::Base
2
+ def parse
3
+ set_display_none if name == 'symbol' && !state.inside_use
4
+ set_display_none if name == 'defs'
5
+ end
6
+
7
+ def container?
8
+ true
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ #
2
+ # This element base class is used for elements that render directly to the Prawn canvas.
3
+ #
4
+ # Initially when I wrote prawn-svg, I was expecting to have multiple renderers and thought separating the codebase
5
+ # from the Prawn renderer would be a good idea. However, it turns out that the Prawn renderer was the only one
6
+ # that was targeted, and it ended up being tightly coupled with the Prawn library.
7
+ #
8
+ # This class is probably how I should have written it. Direct render is required to do text rendering properly
9
+ # because we need to know the width of all the things we print. As of the time of this comment it's the only
10
+ # system that uses DirectRenderBase in prawn-svg.
11
+ #
12
+ class Prawn::SVG::Elements::DirectRenderBase < Prawn::SVG::Elements::Base
13
+ # Called by Renderer when it finds the svg:render call added below.
14
+ def render(prawn, renderer); end
15
+
16
+ protected
17
+
18
+ def parse_and_apply
19
+ parse_standard_attributes
20
+ parse
21
+ apply_calls_from_standard_attributes
22
+ @parent_calls << ['svg:render', [self], [], []] unless computed_properties.display == 'none'
23
+ rescue SkipElementQuietly
24
+ rescue SkipElementError => e
25
+ @document.warnings << e.message
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ class Prawn::SVG::Elements::Ellipse < Prawn::SVG::Elements::Base
2
+ USE_NEW_ELLIPSE_CALL = Prawn::Document.instance_methods.include?(:ellipse)
3
+
4
+ def parse
5
+ require_attributes 'rx', 'ry'
6
+
7
+ @x = x(attributes['cx'] || '0')
8
+ @y = y(attributes['cy'] || '0')
9
+ @rx = x_pixels(attributes['rx'])
10
+ @ry = y_pixels(attributes['ry'])
11
+
12
+ require_positive_value @rx, @ry
13
+ end
14
+
15
+ def apply
16
+ add_call USE_NEW_ELLIPSE_CALL ? 'ellipse' : 'ellipse_at', [@x, @y], @rx, @ry
17
+ end
18
+
19
+ def bounding_box
20
+ [@x - @rx, @y + @ry, @x + @rx, @y - @ry]
21
+ end
22
+ end