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
@@ -174,9 +174,13 @@ module Pptx
174
174
  # Anything carrying a `c:txPr`, which is where a chart element's font
175
175
  # lives. The text body is created on first use.
176
176
  module ChartTextProperties
177
- def defRPr = get_or_add_txPr.defRPr
177
+ def defRPr
178
+ get_or_add_txPr.defRPr
179
+ end
178
180
 
179
- def new_txPr = build_from_xml(CT_TextBody::TXPR_XML)
181
+ def new_txPr
182
+ build_from_xml(CT_TextBody::TXPR_XML)
183
+ end
180
184
  end
181
185
 
182
186
  # `c:layout`, holding an optional manual layout.
@@ -186,7 +190,9 @@ module Pptx
186
190
 
187
191
  # The horizontal offset as a fraction of the chart width; 0.0 when the
188
192
  # position is automatic.
189
- def horz_offset = manualLayout&.horz_offset || 0.0
193
+ def horz_offset
194
+ manualLayout&.horz_offset || 0.0
195
+ end
190
196
 
191
197
  # Setting 0.0 hands the position back to PowerPoint.
192
198
  def horz_offset=(offset)
@@ -238,7 +244,9 @@ module Pptx
238
244
  </c:rich>
239
245
  XML
240
246
 
241
- def new_rich = build_from_xml(RICH_XML)
247
+ def new_rich
248
+ build_from_xml(RICH_XML)
249
+ end
242
250
  end
243
251
 
244
252
  # `c:title`, the chart or axis title.
@@ -261,7 +269,9 @@ module Pptx
261
269
  end
262
270
 
263
271
  # The rich-text body, or nil when the title has none.
264
- def rich = xpath("./c:tx/c:rich").first
272
+ def rich
273
+ xpath("./c:tx/c:rich").first
274
+ end
265
275
 
266
276
  # A title takes rich text or a cell reference, never both.
267
277
  def get_or_add_rich
@@ -290,14 +300,18 @@ module Pptx
290
300
  zero_or_one "c:min", successors: %w[c:extLst]
291
301
 
292
302
  # nil means "auto", which is what an absent element says.
293
- def maximum = max&.val
303
+ def maximum
304
+ max&.val
305
+ end
294
306
 
295
307
  def maximum=(value)
296
308
  remove_max
297
309
  get_or_add_max.val = value unless value.nil?
298
310
  end
299
311
 
300
- def minimum = min&.val
312
+ def minimum
313
+ min&.val
314
+ end
301
315
 
302
316
  def minimum=(value)
303
317
  remove_min
@@ -330,7 +344,9 @@ module Pptx
330
344
  end
331
345
  end
332
346
 
333
- def orientation = scaling.orientation&.val || SimpleTypes::ST_Orientation::MIN_MAX
347
+ def orientation
348
+ scaling.orientation&.val || SimpleTypes::ST_Orientation::MIN_MAX
349
+ end
334
350
 
335
351
  # Only a reversed axis is written; the normal direction is the default.
336
352
  def orientation=(value)
@@ -340,7 +356,9 @@ module Pptx
340
356
  scaling.get_or_add_orientation.val = value
341
357
  end
342
358
 
343
- def new_title = CT_Title.new_title(self)
359
+ def new_title
360
+ CT_Title.new_title(self)
361
+ end
344
362
  end
345
363
 
346
364
  COMMON_AXIS_SEQ = %w[c:axId c:scaling c:delete c:axPos c:majorGridlines c:minorGridlines
@@ -391,7 +409,9 @@ module Pptx
391
409
  zero_or_one "c:overlay", successors: TAG_SEQ[4..]
392
410
  zero_or_one "c:txPr", successors: TAG_SEQ[6..]
393
411
 
394
- def horz_offset = layout&.horz_offset || 0.0
412
+ def horz_offset
413
+ layout&.horz_offset || 0.0
414
+ end
395
415
 
396
416
  def horz_offset=(offset)
397
417
  get_or_add_layout.horz_offset = offset
@@ -437,7 +457,9 @@ module Pptx
437
457
  </c:dLbls>
438
458
  XML
439
459
 
440
- def self.new_data_labels(context) = context.build_from_xml(DEFAULT_XML)
460
+ def self.new_data_labels(context)
461
+ context.build_from_xml(DEFAULT_XML)
462
+ end
441
463
 
442
464
  # A flag added on its own starts off, as PowerPoint writes them.
443
465
  %w[showLegendKey showVal showCatName showSerName showPercent].each do |flag|
@@ -445,7 +467,9 @@ module Pptx
445
467
  end
446
468
 
447
469
  # The label for the point at +idx+, or nil if it has none of its own.
448
- def dLbl_for_point(idx) = dLbl_list.find { |label| label.idx.val == idx }
470
+ def dLbl_for_point(idx)
471
+ dLbl_list.find { |label| label.idx.val == idx }
472
+ end
449
473
 
450
474
  # The label for the point at +idx+, created in index order if absent.
451
475
  def get_or_add_dLbl_for_point(idx)
@@ -459,11 +483,11 @@ module Pptx
459
483
  label.idx.val = idx
460
484
  following = dLbl_list.find { |existing| existing.idx.val > idx }
461
485
  if following
462
- following.node.add_previous_sibling(label.node)
486
+ following.add_previous_sibling(label)
463
487
  elsif (last = dLbl_list.last)
464
- last.node.add_next_sibling(label.node)
488
+ last.add_next_sibling(label)
465
489
  else
466
- node.prepend_child(label.node)
490
+ prepend(label)
467
491
  end
468
492
  label
469
493
  end
@@ -506,7 +530,9 @@ module Pptx
506
530
  </c:dLbl>
507
531
  XML
508
532
 
509
- def self.new_dLbl(context) = context.build_from_xml(DEFAULT_XML)
533
+ def self.new_dLbl(context)
534
+ context.build_from_xml(DEFAULT_XML)
535
+ end
510
536
 
511
537
  # The label's own text replaces the generated one. A `c:spPr` or
512
538
  # `c:txPr` alongside `c:tx` makes a bubble chart unsaveable in
@@ -519,7 +545,9 @@ module Pptx
519
545
  tx.get_or_add_rich
520
546
  end
521
547
 
522
- def rich = xpath("./c:tx/c:rich").first
548
+ def rich
549
+ xpath("./c:tx/c:rich").first
550
+ end
523
551
 
524
552
  def remove_tx_rich
525
553
  tx = xpath("./c:tx[c:rich]").first
@@ -580,13 +608,21 @@ module Pptx
580
608
  zero_or_one "c:smooth", successors: TAG_SEQ[18..]
581
609
  zero_or_one "c:bubbleSize", successors: TAG_SEQ[19..]
582
610
 
583
- def index = idx&.val.to_i
611
+ def index
612
+ idx&.val.to_i
613
+ end
584
614
 
585
- def name = xpath("./c:tx//c:pt/c:v").first&.text.to_s
615
+ def name
616
+ xpath("./c:tx//c:pt/c:v").first&.text.to_s
617
+ end
586
618
 
587
- def new_dLbls = CT_DataLabels.new_data_labels(self)
619
+ def new_dLbls
620
+ CT_DataLabels.new_data_labels(self)
621
+ end
588
622
 
589
- def new_dPt = CT_DPt.new_dPt(self)
623
+ def new_dPt
624
+ CT_DPt.new_dPt(self)
625
+ end
590
626
 
591
627
  %w[cat xVal yVal bubbleSize].each do |source|
592
628
  define_method(:"#{source}_ptCount_val") do
@@ -595,9 +631,13 @@ module Pptx
595
631
  end
596
632
 
597
633
  # The label of the point at +idx+, or nil.
598
- def dLbl_for_point(idx) = dLbls&.dLbl_for_point(idx)
634
+ def dLbl_for_point(idx)
635
+ dLbls&.dLbl_for_point(idx)
636
+ end
599
637
 
600
- def get_or_add_dLbl_for_point(idx) = get_or_add_dLbls.get_or_add_dLbl_for_point(idx)
638
+ def get_or_add_dLbl_for_point(idx)
639
+ get_or_add_dLbls.get_or_add_dLbl_for_point(idx)
640
+ end
601
641
 
602
642
  # The formatting record for the point at +idx+, created if absent.
603
643
  def get_or_add_dPt_for_point(idx)
@@ -612,7 +652,9 @@ module Pptx
612
652
  zero_or_one "c:multiLvlStrRef", successors: []
613
653
 
614
654
  # The levels of a multi-level category source, leaf level first.
615
- def lvls = xpath(".//c:lvl")
655
+ def lvls
656
+ xpath(".//c:lvl")
657
+ end
616
658
  end
617
659
 
618
660
  # `c:pt`, one cached value in a series source.
@@ -620,9 +662,13 @@ module Pptx
620
662
  tag "c:pt"
621
663
  required_attr "idx", type: SimpleTypes::XsdUnsignedInt
622
664
 
623
- def v = xpath("./c:v").first
665
+ def v
666
+ xpath("./c:v").first
667
+ end
624
668
 
625
- def value = Float(v.text)
669
+ def value
670
+ Float(v.text)
671
+ end
626
672
  end
627
673
 
628
674
  # -------------------------------------------------------------------------
@@ -651,14 +697,22 @@ module Pptx
651
697
  c:extLst]
652
698
 
653
699
  # Series in document order.
654
- def ser_list = find_all("c:ser")
700
+ def ser_list
701
+ find_all("c:ser")
702
+ end
655
703
 
656
704
  # Series in the order the chart draws them, which is `c:order`.
657
- def sers = ser_list.sort_by { |ser| ser.order&.val.to_i }
705
+ def sers
706
+ ser_list.sort_by { |ser| ser.order&.val.to_i }
707
+ end
658
708
 
659
- def grouping_val = grouping&.val || SimpleTypes::ST_Grouping::STANDARD
709
+ def grouping_val
710
+ grouping&.val || SimpleTypes::ST_Grouping::STANDARD
711
+ end
660
712
 
661
- def new_dLbls = CT_DataLabels.new_data_labels(self)
713
+ def new_dLbls
714
+ CT_DataLabels.new_data_labels(self)
715
+ end
662
716
 
663
717
  # The data-label settings, created with PowerPoint's defaults if the
664
718
  # plot has none yet.
@@ -671,9 +725,13 @@ module Pptx
671
725
  end
672
726
 
673
727
  # The first series' category source, which the others share.
674
- def cat = xpath("./c:ser[1]/c:cat").first
728
+ def cat
729
+ xpath("./c:ser[1]/c:cat").first
730
+ end
675
731
 
676
- def cat_pt_count = xpath("./c:ser//c:cat//c:ptCount").first&.val.to_i
732
+ def cat_pt_count
733
+ xpath("./c:ser//c:cat//c:ptCount").first&.val.to_i
734
+ end
677
735
 
678
736
  # One entry per category, nil where the workbook cell is empty -- such
679
737
  # a category has no `c:pt` but still counts in `c:ptCount`.
@@ -693,20 +751,26 @@ module Pptx
693
751
  tag "c:plotArea"
694
752
 
695
753
  def plot_elements
696
- @node.element_children
697
- .select { |child| child.name.end_with?("Chart") }
698
- .map { |child| Element.wrap(child) }
754
+ element_children.select { |child| child.clark_name.end_with?("Chart") }
699
755
  end
700
756
 
701
- def category_axis = find("c:catAx") || find("c:dateAx")
757
+ def category_axis
758
+ find("c:catAx") || find("c:dateAx")
759
+ end
702
760
 
703
- def value_axes = find_all("c:valAx")
761
+ def value_axes
762
+ find_all("c:valAx")
763
+ end
704
764
 
705
765
  # Every series in the chart, in plot order then series order -- which is
706
766
  # the order their chart-wide indices follow.
707
- def series_elements = plot_elements.flat_map(&:ser_list)
767
+ def series_elements
768
+ plot_elements.flat_map(&:ser_list)
769
+ end
708
770
 
709
- def last_plot_element = plot_elements.last
771
+ def last_plot_element
772
+ plot_elements.last
773
+ end
710
774
  end
711
775
 
712
776
  # `c:chart`, which OOXML uses for two different things under one tag name.
@@ -739,7 +803,9 @@ module Pptx
739
803
  one_and_only_one "c:plotArea"
740
804
  zero_or_one "c:legend", successors: TAG_SEQ[9..]
741
805
 
742
- def new_title = CT_Title.new_title(self)
806
+ def new_title
807
+ CT_Title.new_title(self)
808
+ end
743
809
  end
744
810
 
745
811
  # `c:chartSpace`, the root of a chart part.
@@ -759,14 +825,18 @@ module Pptx
759
825
 
760
826
  # The relationship id of the embedded workbook, or nil when the chart
761
827
  # has none.
762
- def xlsx_part_rId = externalData&.rId
828
+ def xlsx_part_rId
829
+ externalData&.rId
830
+ end
763
831
 
764
832
  # The plot element, e.g. `c:barChart`, that this chart draws with.
765
833
  def plot_element
766
834
  xpath("./c:chart/c:plotArea/*").find { |e| e.nsptag.end_with?("Chart") }
767
835
  end
768
836
 
769
- def series_elements = xpath("./c:chart/c:plotArea/*/c:ser")
837
+ def series_elements
838
+ xpath("./c:chart/c:plotArea/*/c:ser")
839
+ end
770
840
  end
771
841
 
772
842
  # `c:externalData`, the link from a chart to its embedded workbook.
@@ -31,11 +31,15 @@ module Pptx
31
31
  module ContentModel
32
32
  # A member of an `EG_*` element group, for {#zero_or_one_choice}.
33
33
  Choice = Data.define(:nsptag) do
34
- def prop_name = Ns.split_tag(nsptag).last
34
+ def prop_name
35
+ Ns.split_tag(nsptag).last
36
+ end
35
37
  end
36
38
 
37
39
  # Declare one member of a choice group.
38
- def choice(nsptag) = Choice.new(nsptag)
40
+ def choice(nsptag)
41
+ Choice.new(nsptag)
42
+ end
39
43
 
40
44
  # An attribute that may be absent. Reading returns +default+ when it is;
41
45
  # assigning +default+ removes it, so a document never carries an
@@ -17,9 +17,13 @@ module Pptx
17
17
  end
18
18
  end
19
19
 
20
- def add_lumMod(value) = add_lumMod_element(value)
20
+ def add_lumMod(value)
21
+ add_lumMod_element(value)
22
+ end
21
23
 
22
- def add_lumOff(value) = add_lumOff_element(value)
24
+ def add_lumOff(value)
25
+ add_lumOff_element(value)
26
+ end
23
27
 
24
28
  # Remove any luminance adjustment, returning self.
25
29
  def clear_lum
@@ -74,7 +74,9 @@ module Pptx
74
74
  </a:gradFill>
75
75
  XML
76
76
 
77
- def self.new_grad_fill(context) = context.build_from_xml(DEFAULT_XML)
77
+ def self.new_grad_fill(context)
78
+ context.build_from_xml(DEFAULT_XML)
79
+ end
78
80
  end
79
81
 
80
82
  # `a:blip`, the image reference inside a picture fill.
@@ -100,7 +102,9 @@ module Pptx
100
102
  end
101
103
 
102
104
  # The relationship id of the SVG, or nil when this is a plain raster.
103
- def svg_rId = xpath("./a:extLst/a:ext/asvg:svgBlip/@r:embed").first&.value
105
+ def svg_rId
106
+ xpath("./a:extLst/a:ext/asvg:svgBlip/@r:embed").first&.value
107
+ end
104
108
  end
105
109
 
106
110
  # `a:extLst` as it appears on a blip.