ruby_pptx 0.1.0 → 0.2.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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/README.md +83 -2
  4. data/lib/ruby_pptx/action.rb +12 -4
  5. data/lib/ruby_pptx/autoshape_spec.rb +6 -2
  6. data/lib/ruby_pptx/builder.rb +3 -1
  7. data/lib/ruby_pptx/chart/categories.rb +45 -15
  8. data/lib/ruby_pptx/chart/chart.rb +77 -27
  9. data/lib/ruby_pptx/chart/combo.rb +9 -3
  10. data/lib/ruby_pptx/chart/data.rb +45 -15
  11. data/lib/ruby_pptx/chart/format.rb +229 -76
  12. data/lib/ruby_pptx/chart/workbook_writer.rb +9 -3
  13. data/lib/ruby_pptx/chart/xml_writer.rb +54 -18
  14. data/lib/ruby_pptx/chart/xy_data.rb +68 -22
  15. data/lib/ruby_pptx/dml/color.rb +27 -9
  16. data/lib/ruby_pptx/dml/effect.rb +6 -2
  17. data/lib/ruby_pptx/dml/fill.rb +42 -14
  18. data/lib/ruby_pptx/element_proxy.rb +12 -4
  19. data/lib/ruby_pptx/enum/base.rb +30 -10
  20. data/lib/ruby_pptx/enum/prog_id.rb +3 -1
  21. data/lib/ruby_pptx/geometry.rb +32 -10
  22. data/lib/ruby_pptx/image.rb +27 -9
  23. data/lib/ruby_pptx/length.rb +94 -27
  24. data/lib/ruby_pptx/media.rb +12 -4
  25. data/lib/ruby_pptx/numeric_lengths.rb +34 -9
  26. data/lib/ruby_pptx/opc/oxml.rb +7 -5
  27. data/lib/ruby_pptx/opc/pack_uri.rb +15 -5
  28. data/lib/ruby_pptx/opc/package.rb +97 -31
  29. data/lib/ruby_pptx/opc/serialized.rb +15 -5
  30. data/lib/ruby_pptx/oxml/backend.rb +585 -0
  31. data/lib/ruby_pptx/oxml/chart.rb +112 -42
  32. data/lib/ruby_pptx/oxml/content_model.rb +6 -2
  33. data/lib/ruby_pptx/oxml/dml/color.rb +6 -2
  34. data/lib/ruby_pptx/oxml/dml/fill.rb +6 -2
  35. data/lib/ruby_pptx/oxml/element.rb +138 -101
  36. data/lib/ruby_pptx/oxml/ns.rb +7 -7
  37. data/lib/ruby_pptx/oxml/presentation.rb +9 -3
  38. data/lib/ruby_pptx/oxml/section.rb +3 -1
  39. data/lib/ruby_pptx/oxml/shapes/autoshape.rb +39 -13
  40. data/lib/ruby_pptx/oxml/shapes/groupshape.rb +15 -7
  41. data/lib/ruby_pptx/oxml/shapes/other.rb +47 -16
  42. data/lib/ruby_pptx/oxml/shapes/shared.rb +96 -30
  43. data/lib/ruby_pptx/oxml/simple_types.rb +169 -52
  44. data/lib/ruby_pptx/oxml/slide.rb +24 -8
  45. data/lib/ruby_pptx/oxml/table.rb +39 -13
  46. data/lib/ruby_pptx/oxml/text.rb +37 -15
  47. data/lib/ruby_pptx/oxml/theme.rb +13 -4
  48. data/lib/ruby_pptx/package.rb +9 -3
  49. data/lib/ruby_pptx/parts/chart.rb +6 -2
  50. data/lib/ruby_pptx/parts/image.rb +12 -4
  51. data/lib/ruby_pptx/parts/media.rb +3 -1
  52. data/lib/ruby_pptx/parts/presentation.rb +15 -5
  53. data/lib/ruby_pptx/parts/slide.rb +33 -11
  54. data/lib/ruby_pptx/parts/theme.rb +3 -1
  55. data/lib/ruby_pptx/pattern_matching.rb +6 -2
  56. data/lib/ruby_pptx/presentation.rb +24 -8
  57. data/lib/ruby_pptx/section.rb +30 -10
  58. data/lib/ruby_pptx/shapes/adjustments.rb +9 -3
  59. data/lib/ruby_pptx/shapes/authoring.rb +3 -1
  60. data/lib/ruby_pptx/shapes/base.rb +74 -24
  61. data/lib/ruby_pptx/shapes/freeform.rb +36 -12
  62. data/lib/ruby_pptx/shapes/placeholder.rb +44 -15
  63. data/lib/ruby_pptx/shapes/shape.rb +91 -30
  64. data/lib/ruby_pptx/shapes/shape_tree.rb +66 -22
  65. data/lib/ruby_pptx/slide.rb +123 -41
  66. data/lib/ruby_pptx/table.rb +105 -35
  67. data/lib/ruby_pptx/text/font_metrics.rb +30 -9
  68. data/lib/ruby_pptx/text/text.rb +108 -36
  69. data/lib/ruby_pptx/theme.rb +30 -10
  70. data/lib/ruby_pptx/version.rb +1 -1
  71. data/lib/ruby_pptx.rb +8 -1
  72. metadata +22 -1
@@ -1,17 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "nokogiri"
4
3
  require "ruby_pptx/errors"
4
+ require "ruby_pptx/oxml/backend"
5
5
  require "ruby_pptx/oxml/ns"
6
6
  require "ruby_pptx/oxml/simple_types"
7
7
 
8
8
  module Pptx
9
9
  module Oxml
10
+ # The XML library in use, chosen once at load; see {Backend}.
11
+ BACKEND = Backend.select
12
+
10
13
  # Maps a Clark-notation tag name to the {Element} subclass that models it.
11
14
  #
12
15
  # python-pptx gets this for free from lxml, whose parser instantiates a
13
- # custom class per tag. Nokogiri has no such hook, so element classes
14
- # register themselves here and {Element.wrap} does the dispatch.
16
+ # custom class per tag. Neither Nokogiri nor REXML has such a hook, so
17
+ # element classes register themselves here and {Element.wrap} does the
18
+ # dispatch.
15
19
  module Registry
16
20
  @classes = {}
17
21
 
@@ -29,17 +33,23 @@ module Pptx
29
33
  end
30
34
 
31
35
  # @return [Class] the registered class, or {Element} for an unmodelled tag
32
- def class_for(clark_name) = @classes.fetch(clark_name, Element)
36
+ def class_for(clark_name)
37
+ @classes.fetch(clark_name, Element)
38
+ end
33
39
 
34
- def registered = @classes.dup
40
+ def registered
41
+ @classes.dup
42
+ end
35
43
 
36
44
  # Test seam: forget registrations made by a spec.
37
- def reset!(to) = (@classes = to)
45
+ def reset!(to)
46
+ (@classes = to)
47
+ end
38
48
  end
39
49
  end
40
50
 
41
- # Wraps a `Nokogiri::XML::Node` and gives it the schema-aware behaviour of
42
- # one OOXML element type.
51
+ # Wraps an XML node -- Nokogiri's or REXML's, see {Backend} -- and gives
52
+ # it the schema-aware behaviour of one OOXML element type.
43
53
  #
44
54
  # Subclasses declare their content model with class macros, which generate
45
55
  # the accessors that the rest of the library uses:
@@ -71,7 +81,9 @@ module Pptx
71
81
  prefixed_tags.each { |t| Registry.register(Ns.qn(t), self) }
72
82
  end
73
83
 
74
- def self.nsptag = @nsptags&.first
84
+ def self.nsptag
85
+ @nsptags&.first
86
+ end
75
87
 
76
88
  class << self
77
89
  attr_reader :nsptags
@@ -79,33 +91,24 @@ module Pptx
79
91
 
80
92
  # Wrap +node+ in the class registered for its tag.
81
93
  #
82
- # @param node [Nokogiri::XML::Node]
94
+ # @param node [Object] a node of the active {Backend}
83
95
  # @return [Element, nil] nil when +node+ is nil
84
96
  def self.wrap(node)
85
97
  return nil if node.nil?
86
98
  return node if node.is_a?(Element)
87
99
 
88
- cache = cache_for(node.document)
100
+ cache = BACKEND.wrapper_cache(node)
89
101
  cache[node] ||= Registry.class_for(Ns.clark_name_of(node)).new(node)
90
102
  end
91
103
 
92
104
  # Parse +xml+ into a standalone element tree.
93
105
  def self.parse(xml)
94
- doc = Nokogiri::XML(xml) { |config| config.noblanks.strict }
95
- raise InvalidXmlError, doc.errors.first.to_s if doc.errors.any?
96
-
97
- wrap(doc.root)
106
+ wrap(BACKEND.parse(xml))
98
107
  end
99
108
 
100
- # Per-document wrapper cache, keyed on node identity. Lives on the
101
- # document so it dies with it.
102
- def self.cache_for(document)
103
- document.instance_variable_get(:@pptx_wrappers) ||
104
- document.instance_variable_set(:@pptx_wrappers, {}.compare_by_identity)
105
- end
106
- private_class_method :cache_for
107
-
108
- # @return [Nokogiri::XML::Node] the wrapped node
109
+ # @return [Object] the wrapped node, a Nokogiri or REXML element
110
+ # depending on the active {Backend}. An escape hatch: code that uses
111
+ # it ties itself to one backend.
109
112
  attr_reader :node
110
113
 
111
114
  def initialize(node)
@@ -115,24 +118,42 @@ module Pptx
115
118
  # -- tree navigation -------------------------------------------------
116
119
 
117
120
  # @return [String] this element's prefixed tag, e.g. "p:sp"
118
- def nsptag = Ns.prefixed_tag(Ns.clark_name_of(@node))
121
+ def nsptag
122
+ Ns.prefixed_tag(Ns.clark_name_of(@node))
123
+ end
119
124
 
120
- def document = @node.document
125
+ # An opaque token for the document this element belongs to; equal
126
+ # (+equal?+) for two elements exactly when they share a document.
127
+ def document
128
+ BACKEND.document(@node)
129
+ end
121
130
 
122
- def parent = Element.wrap(@node.parent.is_a?(Nokogiri::XML::Element) ? @node.parent : nil)
131
+ def parent
132
+ Element.wrap(BACKEND.parent_element(@node))
133
+ end
123
134
 
124
135
  # @return [Array<Element>] every child element, in document order
125
- def element_children = @node.element_children.map { |child| Element.wrap(child) }
136
+ def element_children
137
+ BACKEND.element_children(@node).map { |child| Element.wrap(child) }
138
+ end
139
+
140
+ # @return [String] this element's tag in Clark notation, "{uri}local"
141
+ def clark_name
142
+ Ns.clark_name_of(@node)
143
+ end
126
144
 
127
145
  # First child element with +nsptag+, or nil.
128
- def find(nsptag) = Element.wrap(raw_find(nsptag))
146
+ def find(nsptag)
147
+ Element.wrap(raw_find(nsptag))
148
+ end
129
149
 
130
150
  # Every child element with +nsptag+, in document order.
131
151
  def find_all(nsptag)
132
152
  pfx, local = Ns.split_tag(nsptag)
133
- @node.element_children
134
- .select { |c| c.name == local && c.namespace&.href == Ns.nsuri(pfx) }
135
- .map { |c| Element.wrap(c) }
153
+ uri = Ns.nsuri(pfx)
154
+ BACKEND.element_children(@node)
155
+ .select { |c| BACKEND.local_name(c) == local && BACKEND.namespace_uri(c) == uri }
156
+ .map { |c| Element.wrap(c) }
136
157
  end
137
158
 
138
159
  # The first child matching any of +nsptags+, in the order given -- not in
@@ -147,8 +168,8 @@ module Pptx
147
168
 
148
169
  # Run an XPath expression with the full OOXML namespace map bound.
149
170
  def xpath(expression)
150
- @node.xpath(expression, Ns::NSMAP).map do |result|
151
- result.is_a?(Nokogiri::XML::Element) ? Element.wrap(result) : result
171
+ BACKEND.xpath(@node, expression, Ns::NSMAP).map do |result|
172
+ BACKEND.element?(result) ? Element.wrap(result) : result
152
173
  end
153
174
  end
154
175
 
@@ -159,67 +180,82 @@ module Pptx
159
180
  def insert_element_before(element, *successor_tags)
160
181
  successor = first_child_found_in(*successor_tags)
161
182
  if successor
162
- successor.node.add_previous_sibling(adopt(element))
183
+ successor.add_previous_sibling(element)
163
184
  else
164
- @node.add_child(adopt(element))
185
+ append(element)
165
186
  end
166
187
  element
167
188
  end
168
189
 
169
190
  def append(element)
170
- @node.add_child(adopt(element))
191
+ BACKEND.append_child(@node, adopt(element))
192
+ element
193
+ end
194
+
195
+ # Make +element+ this element's first child.
196
+ def prepend(element)
197
+ BACKEND.prepend_child(@node, adopt(element))
198
+ element
199
+ end
200
+
201
+ # Put +element+ immediately before this one.
202
+ def add_previous_sibling(element)
203
+ BACKEND.insert_before(@node, adopt(element))
204
+ element
205
+ end
206
+
207
+ # Put +element+ immediately after this one.
208
+ def add_next_sibling(element)
209
+ BACKEND.insert_after(@node, adopt(element))
171
210
  element
172
211
  end
173
212
 
174
213
  # Remove every child element whose tag is in +nsptags+.
175
214
  def remove_all(*nsptags)
176
- nsptags.each { |nsptag| find_all(nsptag).each { |child| child.node.unlink } }
215
+ nsptags.each { |nsptag| find_all(nsptag).each { |child| BACKEND.unlink(child.node) } }
177
216
  nil
178
217
  end
179
218
 
180
219
  def remove(element)
181
- element.node.unlink
220
+ BACKEND.unlink(element.node)
182
221
  nil
183
222
  end
184
223
 
185
224
  # Create a new, empty element with +nsptag+, in *this* element's
186
- # document.
225
+ # document, reusing a namespace declaration already in scope here.
187
226
  #
188
- # Creating it anywhere else would be a bug: Nokogiri re-creates a node
189
- # adopted from another document, so the object you built is not the
190
- # object that ends up in the tree, and every wrapper held on it goes
191
- # stale. Staying in one document keeps node identity intact.
227
+ # Build new elements this way rather than parsing them separately:
228
+ # Nokogiri re-creates a node adopted from another document, so the
229
+ # object built is not the object that ends up in the tree, and every
230
+ # wrapper held on it goes stale.
192
231
  def build(nsptag)
193
232
  pfx, local = Ns.split_tag(nsptag)
194
- uri = Ns.nsuri(pfx)
195
- child = Nokogiri::XML::Node.new(local, @node.document)
196
- # Resolve against this element's scope, not the detached child's, so
197
- # the new element joins an existing declaration instead of introducing
198
- # a second one. Where the parent holds the namespace as its *default*
199
- # -- as .rels and [Content_Types].xml do -- the child is created
200
- # unprefixed to match, which matters because C14N preserves prefixes.
201
- child.namespace = inherited_namespace(pfx, uri) ||
202
- child.add_namespace_definition(pfx, uri)
203
- Element.wrap(child)
204
- end
205
-
206
- # Parse an XML literal into this element's document.
233
+ Element.wrap(BACKEND.build(@node, pfx, local, Ns.nsuri(pfx)))
234
+ end
235
+
236
+ # Parse an XML literal into this element's document, its prefixes
237
+ # resolved against the namespaces in scope here.
207
238
  def build_from_xml(xml)
208
- # noblanks matches Element.parse, so an indented XML literal does not
209
- # smuggle whitespace text nodes into the tree.
210
- fragment = @node.parse(xml, &:noblanks)
211
- raise InvalidXmlError, "fragment produced no element: #{xml.inspect}" if fragment.first.nil?
239
+ fragment = BACKEND.parse_fragment(@node, xml)
240
+ raise InvalidXmlError, "fragment produced no element: #{xml.inspect}" if fragment.nil?
212
241
 
213
- Element.wrap(fragment.first)
242
+ Element.wrap(fragment)
243
+ end
244
+
245
+ # A copy of +element+, from any document, built into this one.
246
+ def import(element)
247
+ build_from_xml(element.to_xml)
214
248
  end
215
249
 
216
250
  # -- text content ----------------------------------------------------
217
251
 
218
252
  # @return [String] the element's text content
219
- def text = @node.text
253
+ def text
254
+ BACKEND.text(@node)
255
+ end
220
256
 
221
257
  def text=(value)
222
- @node.content = value.to_s
258
+ BACKEND.set_text(@node, value.to_s)
223
259
  end
224
260
 
225
261
  # Declare +prefix+ on this element even if nothing uses it yet.
@@ -227,7 +263,7 @@ module Pptx
227
263
  # Needed where a descendant carries a namespaced attribute but the
228
264
  # declaration belongs on the root, which is where PowerPoint puts it.
229
265
  def declare_namespace(prefix)
230
- namespace_for(@node, prefix)
266
+ BACKEND.declare_namespace(@node, prefix, Ns.nsuri(prefix))
231
267
  self
232
268
  end
233
269
 
@@ -241,62 +277,79 @@ module Pptx
241
277
  # from lxml.
242
278
  def get(attr_name)
243
279
  pfx, local = prefixed_attr_parts(attr_name)
244
- return @node[attr_name.to_s] unless pfx
280
+ return BACKEND.get_attribute(@node, attr_name.to_s) unless pfx
245
281
 
246
- @node.attribute_with_ns(local, Ns.nsuri(pfx))&.value
282
+ BACKEND.get_attribute_ns(@node, local, Ns.nsuri(pfx))
247
283
  end
248
284
 
249
285
  def set(attr_name, value)
250
286
  pfx, local = prefixed_attr_parts(attr_name)
251
- return @node[attr_name.to_s] = value.to_s unless pfx
252
-
253
- uri = Ns.nsuri(pfx)
254
- if (existing = @node.attribute_with_ns(local, uri))
255
- existing.value = value.to_s
287
+ if pfx
288
+ BACKEND.set_attribute_ns(@node, pfx, local, Ns.nsuri(pfx), value.to_s)
256
289
  else
257
- namespace_for(@node, pfx)
258
- @node["#{pfx}:#{local}"] = value.to_s
290
+ BACKEND.set_attribute(@node, attr_name.to_s, value.to_s)
259
291
  end
260
292
  value
261
293
  end
262
294
 
263
295
  def delete_attribute(attr_name)
264
296
  pfx, local = prefixed_attr_parts(attr_name)
265
- return @node.remove_attribute(attr_name.to_s) unless pfx
266
-
267
- @node.attribute_with_ns(local, Ns.nsuri(pfx))&.unlink
297
+ if pfx
298
+ BACKEND.remove_attribute_ns(@node, local, Ns.nsuri(pfx))
299
+ else
300
+ BACKEND.remove_attribute(@node, attr_name.to_s)
301
+ end
268
302
  nil
269
303
  end
270
304
 
271
- def attribute?(attr_name) = !get(attr_name).nil?
305
+ def attribute?(attr_name)
306
+ !get(attr_name).nil?
307
+ end
272
308
 
273
309
  # -- serialization and equality --------------------------------------
274
310
 
275
311
  # Pretty-printed XML for this element, without a declaration. For
276
312
  # debugging and specs.
277
- def xml = @node.to_xml(indent: 2)
313
+ def xml
314
+ BACKEND.pretty_xml(@node)
315
+ end
316
+
317
+ # Compact XML for this element, without a declaration.
318
+ def to_xml
319
+ BACKEND.to_xml(@node)
320
+ end
278
321
 
279
- def to_s = xml
322
+ def to_s
323
+ xml
324
+ end
280
325
 
281
- def ==(other) = other.is_a?(Element) && other.node.equal?(@node)
326
+ def ==(other)
327
+ other.is_a?(Element) && other.node.equal?(@node)
328
+ end
282
329
  alias eql? ==
283
330
 
284
- def hash = @node.object_id.hash
331
+ def hash
332
+ @node.object_id.hash
333
+ end
285
334
 
286
- def inspect = "#<#{self.class.name} <#{nsptag}> children=#{@node.element_children.size}>"
335
+ def inspect
336
+ "#<#{self.class.name} <#{nsptag}> children=#{BACKEND.element_children(@node).size}>"
337
+ end
287
338
 
288
339
  private
289
340
 
290
341
  def raw_find(nsptag)
291
342
  pfx, local = Ns.split_tag(nsptag)
292
343
  uri = Ns.nsuri(pfx)
293
- @node.element_children.find { |c| c.name == local && c.namespace&.href == uri }
344
+ BACKEND.element_children(@node).find do |c|
345
+ BACKEND.local_name(c) == local && BACKEND.namespace_uri(c) == uri
346
+ end
294
347
  end
295
348
 
296
349
  # Guard against the cross-document trap described on {#build}.
297
350
  def adopt(element)
298
351
  node = element.is_a?(Element) ? element.node : element
299
- return node if node.document.equal?(@node.document)
352
+ return node if BACKEND.document(node).equal?(document)
300
353
 
301
354
  raise ArgumentError,
302
355
  "cannot insert an element created in a different document; " \
@@ -308,22 +361,6 @@ module Pptx
308
361
 
309
362
  Ns.split_tag(attr_name.to_s)
310
363
  end
311
-
312
- # An in-scope declaration for +uri+, preferring one bound to +prefix+ but
313
- # accepting any -- including a default (unprefixed) declaration.
314
- def inherited_namespace(prefix, uri)
315
- scopes = @node.namespace_scopes
316
- scopes.find { |ns| ns.href == uri && ns.prefix == prefix } ||
317
- scopes.find { |ns| ns.href == uri }
318
- end
319
-
320
- # Reuse an in-scope namespace declaration when there is one, so the
321
- # output does not sprout a redundant xmlns on every new element.
322
- def namespace_for(node, prefix)
323
- uri = Ns.nsuri(prefix)
324
- existing = node.namespace_scopes.find { |ns| ns.href == uri && ns.prefix == prefix }
325
- existing || node.add_namespace_definition(prefix, uri)
326
- end
327
364
  end
328
365
  end
329
366
  end
@@ -8,7 +8,7 @@ module Pptx
8
8
  #
9
9
  # - prefixed, `"p:cSld"` -- how the spec and this code talk
10
10
  # - Clark, `"{http://...}cSld"` -- unambiguous, used as registry keys
11
- # - Nokogiri, name + namespace object -- what the parser hands back
11
+ # - a backend node, name + namespace -- what the parser hands back
12
12
  module Ns
13
13
  NSMAP = {
14
14
  "a" => "http://schemas.openxmlformats.org/drawingml/2006/main",
@@ -78,7 +78,7 @@ module Pptx
78
78
  "#{pfx}:#{local}"
79
79
  end
80
80
 
81
- # The subset of NSMAP for +prefixes+, in the shape Nokogiri's #xpath wants.
81
+ # The subset of NSMAP for +prefixes+, in the shape an XPath query wants.
82
82
  #
83
83
  # namespaces("a", "p") #=> {"a" => "...", "p" => "..."}
84
84
  def namespaces(*prefixes)
@@ -93,12 +93,12 @@ module Pptx
93
93
  prefixes.map { |pfx| %(xmlns:#{pfx}="#{nsuri(pfx)}") }.join(" ")
94
94
  end
95
95
 
96
- # The Clark name of a Nokogiri node, suitable as an element-registry key.
97
- #
98
- # @param node [Nokogiri::XML::Node]
96
+ # The Clark name of a node of the active XML backend, suitable as an
97
+ # element-registry key.
99
98
  def clark_name_of(node)
100
- href = node.namespace&.href
101
- href ? "{#{href}}#{node.name}" : node.name
99
+ backend = BACKEND
100
+ href = backend.namespace_uri(node)
101
+ href ? "{#{href}}#{backend.local_name(node)}" : backend.local_name(node)
102
102
  end
103
103
 
104
104
  # ["p", "cSld"] for "p:cSld".
@@ -61,9 +61,13 @@ module Pptx
61
61
  MIN_SLIDE_ID = 256
62
62
  MAX_SLIDE_ID = 2_147_483_647
63
63
 
64
- def add_slide_id(r_id) = add_sldId(id: next_id, rId: r_id)
64
+ def add_slide_id(r_id)
65
+ add_sldId(id: next_id, rId: r_id)
66
+ end
65
67
 
66
- def size = sldId_list.size
68
+ def size
69
+ sldId_list.size
70
+ end
67
71
 
68
72
  private
69
73
 
@@ -90,7 +94,9 @@ module Pptx
90
94
  tag "p:sldMasterIdLst"
91
95
  zero_or_more "p:sldMasterId", as: :sldMasterId
92
96
 
93
- def size = sldMasterId_list.size
97
+ def size
98
+ sldMasterId_list.size
99
+ end
94
100
  end
95
101
 
96
102
  # `p:sldMasterId`, a reference to one slide master.
@@ -42,7 +42,9 @@ module Pptx
42
42
  one_and_only_one "p14:sldIdLst"
43
43
 
44
44
  # PowerPoint identifies a section by a brace-wrapped uppercase GUID.
45
- def self.new_id = "{#{SecureRandom.uuid.upcase}}"
45
+ def self.new_id
46
+ "{#{SecureRandom.uuid.upcase}}"
47
+ end
46
48
  end
47
49
 
48
50
  # `p14:sectionLst`, the sections of a presentation.
@@ -25,7 +25,9 @@ module Pptx
25
25
  required_attr "prst", type: Enum::MSO_AUTO_SHAPE_TYPE
26
26
 
27
27
  # The adjustment guides, in document order.
28
- def gd_list = avLst ? avLst.gd_list : []
28
+ def gd_list
29
+ avLst ? avLst.gd_list : []
30
+ end
29
31
 
30
32
  # Replace the adjustment values with +name_value_pairs+.
31
33
  def rewrite_guides(name_value_pairs)
@@ -80,11 +82,17 @@ module Pptx
80
82
  optional_attr "w", type: SimpleTypes::ST_PositiveCoordinate
81
83
  optional_attr "h", type: SimpleTypes::ST_PositiveCoordinate
82
84
 
83
- def move_to(x, y) = add_moveTo.point_at(x, y)
85
+ def move_to(x, y)
86
+ add_moveTo.point_at(x, y)
87
+ end
84
88
 
85
- def line_to(x, y) = add_lnTo.point_at(x, y)
89
+ def line_to(x, y)
90
+ add_lnTo.point_at(x, y)
91
+ end
86
92
 
87
- def close_contour = add_close
93
+ def close_contour
94
+ add_close
95
+ end
88
96
  end
89
97
 
90
98
  # `a:pathLst`, the contours of a freeform shape.
@@ -254,7 +262,9 @@ module Pptx
254
262
  XML
255
263
  end
256
264
 
257
- def escape(text) = text.to_s.gsub("&", "&amp;").gsub("<", "&lt;").gsub('"', "&quot;")
265
+ def escape(text)
266
+ text.to_s.gsub("&", "&amp;").gsub("<", "&lt;").gsub('"', "&quot;")
267
+ end
258
268
 
259
269
  DEFAULT_STYLE = <<~XML
260
270
  <p:style>
@@ -276,7 +286,9 @@ module Pptx
276
286
 
277
287
  # A text body added to a shape that has none needs the minimum structure
278
288
  # the schema requires, not an empty element.
279
- def new_txBody = CT_TextBody.new_element(self)
289
+ def new_txBody
290
+ CT_TextBody.new_element(self)
291
+ end
280
292
 
281
293
  # Start a new contour on this shape's custom geometry.
282
294
  #
@@ -288,23 +300,37 @@ module Pptx
288
300
  geometry.get_or_add_pathLst.add_path_of(width, height)
289
301
  end
290
302
 
291
- def prstGeom = spPr.prstGeom
303
+ def prstGeom
304
+ spPr.prstGeom
305
+ end
292
306
 
293
- def prst = prstGeom&.prst
307
+ def prst
308
+ prstGeom&.prst
309
+ end
294
310
 
295
- def ln = spPr.ln
311
+ def ln
312
+ spPr.ln
313
+ end
296
314
 
297
- def get_or_add_ln = spPr.get_or_add_ln
315
+ def get_or_add_ln
316
+ spPr.get_or_add_ln
317
+ end
298
318
 
299
319
  # A shape has custom geometry -- is a freeform -- when it carries
300
320
  # `a:custGeom` in place of `a:prstGeom`.
301
- def custom_geometry? = !spPr.custGeom.nil?
321
+ def custom_geometry?
322
+ !spPr.custGeom.nil?
323
+ end
302
324
 
303
325
  # A text box is an `p:sp` whose `p:cNvSpPr` says `txBox="1"`.
304
- def textbox? = nvSpPr.cNvSpPr.txBox == true
326
+ def textbox?
327
+ nvSpPr.cNvSpPr.txBox == true
328
+ end
305
329
 
306
330
  # An auto shape has preset geometry and is not a text box.
307
- def autoshape? = !prstGeom.nil? && nvSpPr.cNvSpPr.txBox != true
331
+ def autoshape?
332
+ !prstGeom.nil? && nvSpPr.cNvSpPr.txBox != true
333
+ end
308
334
  end
309
335
  end
310
336
  end
@@ -26,21 +26,29 @@ module Pptx
26
26
  SHAPE_TAGS = %w[p:sp p:grpSp p:graphicFrame p:cxnSp p:pic p:contentPart].freeze
27
27
 
28
28
  # A group's transform hangs off `p:grpSpPr`, not `p:spPr`.
29
- def xfrm = grpSpPr.xfrm
29
+ def xfrm
30
+ grpSpPr.xfrm
31
+ end
30
32
 
31
- def get_or_add_xfrm = grpSpPr.get_or_add_xfrm
33
+ def get_or_add_xfrm
34
+ grpSpPr.get_or_add_xfrm
35
+ end
32
36
 
33
- def chOff = get_or_add_xfrm.get_or_add_chOff
37
+ def chOff
38
+ get_or_add_xfrm.get_or_add_chOff
39
+ end
34
40
 
35
- def chExt = get_or_add_xfrm.get_or_add_chExt
41
+ def chExt
42
+ get_or_add_xfrm.get_or_add_chExt
43
+ end
36
44
 
37
45
  # Each shape child, in document order.
38
46
  def shape_elements
39
47
  return enum_for(:shape_elements) unless block_given?
40
48
 
41
49
  tags = SHAPE_TAGS.map { |t| Ns.qn(t) }
42
- @node.element_children.each do |child|
43
- yield Element.wrap(child) if tags.include?(Ns.clark_name_of(child))
50
+ element_children.each do |child|
51
+ yield child if tags.include?(child.clark_name)
44
52
  end
45
53
  end
46
54
 
@@ -210,7 +218,7 @@ module Pptx
210
218
  def adopt_xml(element)
211
219
  return element if element.document.equal?(document)
212
220
 
213
- build_from_xml(element.node.to_xml)
221
+ import(element)
214
222
  end
215
223
  end
216
224
  end