ruby_pptx 0.1.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 (101) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +83 -0
  3. data/LICENSE +21 -0
  4. data/NOTICE +48 -0
  5. data/README.md +272 -0
  6. data/lib/ruby_pptx/action.rb +120 -0
  7. data/lib/ruby_pptx/autoshape_spec.rb +337 -0
  8. data/lib/ruby_pptx/builder.rb +228 -0
  9. data/lib/ruby_pptx/chart/categories.rb +208 -0
  10. data/lib/ruby_pptx/chart/chart.rb +443 -0
  11. data/lib/ruby_pptx/chart/combo.rb +230 -0
  12. data/lib/ruby_pptx/chart/data.rb +147 -0
  13. data/lib/ruby_pptx/chart/format.rb +647 -0
  14. data/lib/ruby_pptx/chart/workbook_writer.rb +212 -0
  15. data/lib/ruby_pptx/chart/xml_writer.rb +953 -0
  16. data/lib/ruby_pptx/chart/xy_data.rb +182 -0
  17. data/lib/ruby_pptx/core_ext.rb +12 -0
  18. data/lib/ruby_pptx/dml/color.rb +155 -0
  19. data/lib/ruby_pptx/dml/effect.rb +33 -0
  20. data/lib/ruby_pptx/dml/fill.rb +234 -0
  21. data/lib/ruby_pptx/element_proxy.rb +53 -0
  22. data/lib/ruby_pptx/enum/action.rb +46 -0
  23. data/lib/ruby_pptx/enum/base.rb +139 -0
  24. data/lib/ruby_pptx/enum/chart.rb +278 -0
  25. data/lib/ruby_pptx/enum/dml.rb +237 -0
  26. data/lib/ruby_pptx/enum/lang.rb +441 -0
  27. data/lib/ruby_pptx/enum/prog_id.rb +38 -0
  28. data/lib/ruby_pptx/enum/shapes.rb +514 -0
  29. data/lib/ruby_pptx/enum/text.rb +108 -0
  30. data/lib/ruby_pptx/errors.rb +15 -0
  31. data/lib/ruby_pptx/geometry.rb +89 -0
  32. data/lib/ruby_pptx/image.rb +347 -0
  33. data/lib/ruby_pptx/length.rb +120 -0
  34. data/lib/ruby_pptx/media.rb +64 -0
  35. data/lib/ruby_pptx/numeric_lengths.rb +30 -0
  36. data/lib/ruby_pptx/opc/constants.rb +205 -0
  37. data/lib/ruby_pptx/opc/oxml.rb +99 -0
  38. data/lib/ruby_pptx/opc/pack_uri.rb +146 -0
  39. data/lib/ruby_pptx/opc/package.rb +482 -0
  40. data/lib/ruby_pptx/opc/serialized.rb +229 -0
  41. data/lib/ruby_pptx/opc/spec.rb +37 -0
  42. data/lib/ruby_pptx/oxml/action.rb +43 -0
  43. data/lib/ruby_pptx/oxml/chart.rb +785 -0
  44. data/lib/ruby_pptx/oxml/content_model.rb +186 -0
  45. data/lib/ruby_pptx/oxml/core_properties.rb +145 -0
  46. data/lib/ruby_pptx/oxml/dml/color.rb +115 -0
  47. data/lib/ruby_pptx/oxml/dml/fill.rb +137 -0
  48. data/lib/ruby_pptx/oxml/element.rb +329 -0
  49. data/lib/ruby_pptx/oxml/ns.rb +115 -0
  50. data/lib/ruby_pptx/oxml/presentation.rb +110 -0
  51. data/lib/ruby_pptx/oxml/section.rb +65 -0
  52. data/lib/ruby_pptx/oxml/shapes/autoshape.rb +310 -0
  53. data/lib/ruby_pptx/oxml/shapes/groupshape.rb +217 -0
  54. data/lib/ruby_pptx/oxml/shapes/other.rb +405 -0
  55. data/lib/ruby_pptx/oxml/shapes/shared.rb +292 -0
  56. data/lib/ruby_pptx/oxml/simple_types.rb +563 -0
  57. data/lib/ruby_pptx/oxml/slide.rb +290 -0
  58. data/lib/ruby_pptx/oxml/table.rb +253 -0
  59. data/lib/ruby_pptx/oxml/text.rb +324 -0
  60. data/lib/ruby_pptx/oxml/theme.rb +93 -0
  61. data/lib/ruby_pptx/package.rb +139 -0
  62. data/lib/ruby_pptx/parts/chart.rb +75 -0
  63. data/lib/ruby_pptx/parts/core_properties.rb +40 -0
  64. data/lib/ruby_pptx/parts/embedded_package.rb +43 -0
  65. data/lib/ruby_pptx/parts/image.rb +55 -0
  66. data/lib/ruby_pptx/parts/media.rb +17 -0
  67. data/lib/ruby_pptx/parts/presentation.rb +79 -0
  68. data/lib/ruby_pptx/parts/slide.rb +221 -0
  69. data/lib/ruby_pptx/parts/theme.rb +26 -0
  70. data/lib/ruby_pptx/pattern_matching.rb +50 -0
  71. data/lib/ruby_pptx/presentation.rb +128 -0
  72. data/lib/ruby_pptx/refinements.rb +21 -0
  73. data/lib/ruby_pptx/section.rb +130 -0
  74. data/lib/ruby_pptx/shapes/adjustments.rb +83 -0
  75. data/lib/ruby_pptx/shapes/authoring.rb +110 -0
  76. data/lib/ruby_pptx/shapes/base.rb +138 -0
  77. data/lib/ruby_pptx/shapes/freeform.rb +141 -0
  78. data/lib/ruby_pptx/shapes/placeholder.rb +184 -0
  79. data/lib/ruby_pptx/shapes/shape.rb +337 -0
  80. data/lib/ruby_pptx/shapes/shape_tree.rb +634 -0
  81. data/lib/ruby_pptx/sliceable.rb +34 -0
  82. data/lib/ruby_pptx/slide.rb +430 -0
  83. data/lib/ruby_pptx/table.rb +316 -0
  84. data/lib/ruby_pptx/table_paging.rb +91 -0
  85. data/lib/ruby_pptx/templates/default.pptx +0 -0
  86. data/lib/ruby_pptx/templates/docx-icon.emf +0 -0
  87. data/lib/ruby_pptx/templates/generic-icon.emf +0 -0
  88. data/lib/ruby_pptx/templates/media-speaker.png +0 -0
  89. data/lib/ruby_pptx/templates/notes.xml +23 -0
  90. data/lib/ruby_pptx/templates/notesMaster.xml +352 -0
  91. data/lib/ruby_pptx/templates/pptx-icon.emf +0 -0
  92. data/lib/ruby_pptx/templates/slideMaster.xml +277 -0
  93. data/lib/ruby_pptx/templates/theme.xml +321 -0
  94. data/lib/ruby_pptx/templates/xlsx-icon.emf +0 -0
  95. data/lib/ruby_pptx/text/fitter.rb +72 -0
  96. data/lib/ruby_pptx/text/font_metrics.rb +221 -0
  97. data/lib/ruby_pptx/text/text.rb +393 -0
  98. data/lib/ruby_pptx/theme.rb +98 -0
  99. data/lib/ruby_pptx/version.rb +5 -0
  100. data/lib/ruby_pptx.rb +90 -0
  101. metadata +174 -0
@@ -0,0 +1,337 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated by tools/generate_autoshape_spec.py from python-pptx. Do not edit by hand.
4
+
5
+ require "ruby_pptx/enum/shapes"
6
+
7
+ module Pptx
8
+ # Per auto-shape-type metadata: the name PowerPoint gives a new shape
9
+ # of that type, and the default values of its adjustment handles.
10
+ module AutoShapeSpec
11
+ SPECS = {
12
+ ACTION_BUTTON_BACK_OR_PREVIOUS: { basename: "Action Button: Back or Previous",
13
+ default_adjustments: [].freeze }.freeze,
14
+ ACTION_BUTTON_BEGINNING: { basename: "Action Button: Beginning",
15
+ default_adjustments: [].freeze }.freeze,
16
+ ACTION_BUTTON_CUSTOM: { basename: "Action Button: Custom", default_adjustments: [].freeze }.freeze,
17
+ ACTION_BUTTON_DOCUMENT: { basename: "Action Button: Document", default_adjustments: [].freeze }.freeze,
18
+ ACTION_BUTTON_END: { basename: "Action Button: End", default_adjustments: [].freeze }.freeze,
19
+ ACTION_BUTTON_FORWARD_OR_NEXT: { basename: "Action Button: Forward or Next",
20
+ default_adjustments: [].freeze }.freeze,
21
+ ACTION_BUTTON_HELP: { basename: "Action Button: Help", default_adjustments: [].freeze }.freeze,
22
+ ACTION_BUTTON_HOME: { basename: "Action Button: Home", default_adjustments: [].freeze }.freeze,
23
+ ACTION_BUTTON_INFORMATION: { basename: "Action Button: Information",
24
+ default_adjustments: [].freeze }.freeze,
25
+ ACTION_BUTTON_MOVIE: { basename: "Action Button: Movie", default_adjustments: [].freeze }.freeze,
26
+ ACTION_BUTTON_RETURN: { basename: "Action Button: Return", default_adjustments: [].freeze }.freeze,
27
+ ACTION_BUTTON_SOUND: { basename: "Action Button: Sound", default_adjustments: [].freeze }.freeze,
28
+ ARC: { basename: "Arc", default_adjustments: [["adj1", 16_200_000], ["adj2", 0]].freeze }.freeze,
29
+ BALLOON: { basename: "Rounded Rectangular Callout",
30
+ default_adjustments: [["adj1", -20833], ["adj2", 62500], ["adj3", 16667]].freeze }.freeze,
31
+ BENT_ARROW: { basename: "Bent Arrow",
32
+ default_adjustments: [["adj1", 25000], ["adj2", 25000], ["adj3", 25000],
33
+ ["adj4", 43750]].freeze }.freeze,
34
+ BENT_UP_ARROW: { basename: "Bent-Up Arrow",
35
+ default_adjustments: [["adj1", 25000], ["adj2", 25000],
36
+ ["adj3", 25000]].freeze }.freeze,
37
+ BEVEL: { basename: "Bevel", default_adjustments: [["adj", 12500]].freeze }.freeze,
38
+ BLOCK_ARC: { basename: "Block Arc",
39
+ default_adjustments: [["adj1", 10_800_000], ["adj2", 0], ["adj3", 25000]].freeze }.freeze,
40
+ CAN: { basename: "Can", default_adjustments: [["adj", 25000]].freeze }.freeze,
41
+ CHART_PLUS: { basename: "Chart Plus", default_adjustments: [].freeze }.freeze,
42
+ CHART_STAR: { basename: "Chart Star", default_adjustments: [].freeze }.freeze,
43
+ CHART_X: { basename: "Chart X", default_adjustments: [].freeze }.freeze,
44
+ CHEVRON: { basename: "Chevron", default_adjustments: [["adj", 50000]].freeze }.freeze,
45
+ CHORD: { basename: "Chord",
46
+ default_adjustments: [["adj1", 2_700_000], ["adj2", 16_200_000]].freeze }.freeze,
47
+ CIRCULAR_ARROW: { basename: "Circular Arrow",
48
+ default_adjustments: [["adj1", 12500], ["adj2", 1_142_319], ["adj3", 20_457_681], ["adj4", 10_800_000],
49
+ ["adj5", 12500]].freeze }.freeze,
50
+ CLOUD: { basename: "Cloud", default_adjustments: [].freeze }.freeze,
51
+ CLOUD_CALLOUT: { basename: "Cloud Callout",
52
+ default_adjustments: [["adj1", -20833], ["adj2", 62500]].freeze }.freeze,
53
+ CORNER: { basename: "Corner", default_adjustments: [["adj1", 50000], ["adj2", 50000]].freeze }.freeze,
54
+ CORNER_TABS: { basename: "Corner Tabs", default_adjustments: [].freeze }.freeze,
55
+ CROSS: { basename: "Cross", default_adjustments: [["adj", 25000]].freeze }.freeze,
56
+ CUBE: { basename: "Cube", default_adjustments: [["adj", 25000]].freeze }.freeze,
57
+ CURVED_DOWN_ARROW: { basename: "Curved Down Arrow",
58
+ default_adjustments: [["adj1", 25000], ["adj2", 50000],
59
+ ["adj3", 25000]].freeze }.freeze,
60
+ CURVED_DOWN_RIBBON: { basename: "Curved Down Ribbon",
61
+ default_adjustments: [["adj1", 25000], ["adj2", 50000],
62
+ ["adj3", 12500]].freeze }.freeze,
63
+ CURVED_LEFT_ARROW: { basename: "Curved Left Arrow",
64
+ default_adjustments: [["adj1", 25000], ["adj2", 50000],
65
+ ["adj3", 25000]].freeze }.freeze,
66
+ CURVED_RIGHT_ARROW: { basename: "Curved Right Arrow",
67
+ default_adjustments: [["adj1", 25000], ["adj2", 50000],
68
+ ["adj3", 25000]].freeze }.freeze,
69
+ CURVED_UP_ARROW: { basename: "Curved Up Arrow",
70
+ default_adjustments: [["adj1", 25000], ["adj2", 50000],
71
+ ["adj3", 25000]].freeze }.freeze,
72
+ CURVED_UP_RIBBON: { basename: "Curved Up Ribbon",
73
+ default_adjustments: [["adj1", 25000], ["adj2", 50000],
74
+ ["adj3", 12500]].freeze }.freeze,
75
+ DECAGON: { basename: "Decagon", default_adjustments: [["vf", 105_146]].freeze }.freeze,
76
+ DIAGONAL_STRIPE: { basename: "Diagonal Stripe", default_adjustments: [["adj", 50000]].freeze }.freeze,
77
+ DIAMOND: { basename: "Diamond", default_adjustments: [].freeze }.freeze,
78
+ DODECAGON: { basename: "Dodecagon", default_adjustments: [].freeze }.freeze,
79
+ DONUT: { basename: "Donut", default_adjustments: [["adj", 25000]].freeze }.freeze,
80
+ DOUBLE_BRACE: { basename: "Double Brace", default_adjustments: [["adj", 8333]].freeze }.freeze,
81
+ DOUBLE_BRACKET: { basename: "Double Bracket", default_adjustments: [["adj", 16667]].freeze }.freeze,
82
+ DOUBLE_WAVE: { basename: "Double Wave",
83
+ default_adjustments: [["adj1", 6250], ["adj2", 0]].freeze }.freeze,
84
+ DOWN_ARROW: { basename: "Down Arrow",
85
+ default_adjustments: [["adj1", 50000], ["adj2", 50000]].freeze }.freeze,
86
+ DOWN_ARROW_CALLOUT: { basename: "Down Arrow Callout",
87
+ default_adjustments: [["adj1", 25000], ["adj2", 25000], ["adj3", 25000],
88
+ ["adj4", 64977]].freeze }.freeze,
89
+ DOWN_RIBBON: { basename: "Down Ribbon",
90
+ default_adjustments: [["adj1", 16667], ["adj2", 50000]].freeze }.freeze,
91
+ EXPLOSION1: { basename: "Explosion", default_adjustments: [].freeze }.freeze,
92
+ EXPLOSION2: { basename: "Explosion", default_adjustments: [].freeze }.freeze,
93
+ FLOWCHART_ALTERNATE_PROCESS: { basename: "Alternate process", default_adjustments: [].freeze }.freeze,
94
+ FLOWCHART_CARD: { basename: "Card", default_adjustments: [].freeze }.freeze,
95
+ FLOWCHART_COLLATE: { basename: "Collate", default_adjustments: [].freeze }.freeze,
96
+ FLOWCHART_CONNECTOR: { basename: "Connector", default_adjustments: [].freeze }.freeze,
97
+ FLOWCHART_DATA: { basename: "Data", default_adjustments: [].freeze }.freeze,
98
+ FLOWCHART_DECISION: { basename: "Decision", default_adjustments: [].freeze }.freeze,
99
+ FLOWCHART_DELAY: { basename: "Delay", default_adjustments: [].freeze }.freeze,
100
+ FLOWCHART_DIRECT_ACCESS_STORAGE: { basename: "Direct Access Storage",
101
+ default_adjustments: [].freeze }.freeze,
102
+ FLOWCHART_DISPLAY: { basename: "Display", default_adjustments: [].freeze }.freeze,
103
+ FLOWCHART_DOCUMENT: { basename: "Document", default_adjustments: [].freeze }.freeze,
104
+ FLOWCHART_EXTRACT: { basename: "Extract", default_adjustments: [].freeze }.freeze,
105
+ FLOWCHART_INTERNAL_STORAGE: { basename: "Internal Storage", default_adjustments: [].freeze }.freeze,
106
+ FLOWCHART_MAGNETIC_DISK: { basename: "Magnetic Disk", default_adjustments: [].freeze }.freeze,
107
+ FLOWCHART_MANUAL_INPUT: { basename: "Manual Input", default_adjustments: [].freeze }.freeze,
108
+ FLOWCHART_MANUAL_OPERATION: { basename: "Manual Operation", default_adjustments: [].freeze }.freeze,
109
+ FLOWCHART_MERGE: { basename: "Merge", default_adjustments: [].freeze }.freeze,
110
+ FLOWCHART_MULTIDOCUMENT: { basename: "Multidocument", default_adjustments: [].freeze }.freeze,
111
+ FLOWCHART_OFFLINE_STORAGE: { basename: "Offline Storage", default_adjustments: [].freeze }.freeze,
112
+ FLOWCHART_OFFPAGE_CONNECTOR: { basename: "Off-page Connector", default_adjustments: [].freeze }.freeze,
113
+ FLOWCHART_OR: { basename: "Or", default_adjustments: [].freeze }.freeze,
114
+ FLOWCHART_PREDEFINED_PROCESS: { basename: "Predefined Process", default_adjustments: [].freeze }.freeze,
115
+ FLOWCHART_PREPARATION: { basename: "Preparation", default_adjustments: [].freeze }.freeze,
116
+ FLOWCHART_PROCESS: { basename: "Process", default_adjustments: [].freeze }.freeze,
117
+ FLOWCHART_PUNCHED_TAPE: { basename: "Punched Tape", default_adjustments: [].freeze }.freeze,
118
+ FLOWCHART_SEQUENTIAL_ACCESS_STORAGE: { basename: "Sequential Access Storage",
119
+ default_adjustments: [].freeze }.freeze,
120
+ FLOWCHART_SORT: { basename: "Sort", default_adjustments: [].freeze }.freeze,
121
+ FLOWCHART_STORED_DATA: { basename: "Stored Data", default_adjustments: [].freeze }.freeze,
122
+ FLOWCHART_SUMMING_JUNCTION: { basename: "Summing Junction", default_adjustments: [].freeze }.freeze,
123
+ FLOWCHART_TERMINATOR: { basename: "Terminator", default_adjustments: [].freeze }.freeze,
124
+ FOLDED_CORNER: { basename: "Folded Corner", default_adjustments: [].freeze }.freeze,
125
+ FRAME: { basename: "Frame", default_adjustments: [["adj1", 12500]].freeze }.freeze,
126
+ FUNNEL: { basename: "Funnel", default_adjustments: [].freeze }.freeze,
127
+ GEAR_6: { basename: "Gear 6", default_adjustments: [["adj1", 15000], ["adj2", 3526]].freeze }.freeze,
128
+ GEAR_9: { basename: "Gear 9", default_adjustments: [["adj1", 10000], ["adj2", 1763]].freeze }.freeze,
129
+ HALF_FRAME: { basename: "Half Frame",
130
+ default_adjustments: [["adj1", 33333], ["adj2", 33333]].freeze }.freeze,
131
+ HEART: { basename: "Heart", default_adjustments: [].freeze }.freeze,
132
+ HEPTAGON: { basename: "Heptagon",
133
+ default_adjustments: [["hf", 102_572], ["vf", 105_210]].freeze }.freeze,
134
+ HEXAGON: { basename: "Hexagon", default_adjustments: [["adj", 25000], ["vf", 115_470]].freeze }.freeze,
135
+ HORIZONTAL_SCROLL: { basename: "Horizontal Scroll",
136
+ default_adjustments: [["adj", 12500]].freeze }.freeze,
137
+ ISOSCELES_TRIANGLE: { basename: "Isosceles Triangle",
138
+ default_adjustments: [["adj", 50000]].freeze }.freeze,
139
+ LEFT_ARROW: { basename: "Left Arrow",
140
+ default_adjustments: [["adj1", 50000], ["adj2", 50000]].freeze }.freeze,
141
+ LEFT_ARROW_CALLOUT: { basename: "Left Arrow Callout",
142
+ default_adjustments: [["adj1", 25000], ["adj2", 25000], ["adj3", 25000],
143
+ ["adj4", 64977]].freeze }.freeze,
144
+ LEFT_BRACE: { basename: "Left Brace",
145
+ default_adjustments: [["adj1", 8333], ["adj2", 50000]].freeze }.freeze,
146
+ LEFT_BRACKET: { basename: "Left Bracket", default_adjustments: [["adj", 8333]].freeze }.freeze,
147
+ LEFT_CIRCULAR_ARROW: { basename: "Left Circular Arrow",
148
+ default_adjustments: [["adj1", 12500], ["adj2", -1_142_319], ["adj3", 1_142_319], ["adj4", 10_800_000],
149
+ ["adj5", 12500]].freeze }.freeze,
150
+ LEFT_RIGHT_ARROW: { basename: "Left-Right Arrow",
151
+ default_adjustments: [["adj1", 50000], ["adj2", 50000]].freeze }.freeze,
152
+ LEFT_RIGHT_ARROW_CALLOUT: { basename: "Left-Right Arrow Callout",
153
+ default_adjustments: [["adj1", 25000], ["adj2", 25000], ["adj3", 25000],
154
+ ["adj4", 48123]].freeze }.freeze,
155
+ LEFT_RIGHT_CIRCULAR_ARROW: { basename: "Left Right Circular Arrow",
156
+ default_adjustments: [["adj1", 12500], ["adj2", 1_142_319], ["adj3", 20_457_681], ["adj4", 11_942_319],
157
+ ["adj5", 12500]].freeze }.freeze,
158
+ LEFT_RIGHT_RIBBON: { basename: "Left Right Ribbon",
159
+ default_adjustments: [["adj1", 50000], ["adj2", 50000],
160
+ ["adj3", 16667]].freeze }.freeze,
161
+ LEFT_RIGHT_UP_ARROW: { basename: "Left-Right-Up Arrow",
162
+ default_adjustments: [["adj1", 25000], ["adj2", 25000],
163
+ ["adj3", 25000]].freeze }.freeze,
164
+ LEFT_UP_ARROW: { basename: "Left-Up Arrow",
165
+ default_adjustments: [["adj1", 25000], ["adj2", 25000],
166
+ ["adj3", 25000]].freeze }.freeze,
167
+ LIGHTNING_BOLT: { basename: "Lightning Bolt", default_adjustments: [].freeze }.freeze,
168
+ LINE_CALLOUT_1: { basename: "Line Callout 1",
169
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 112_500],
170
+ ["adj4", -38333]].freeze }.freeze,
171
+ LINE_CALLOUT_1_ACCENT_BAR: { basename: "Line Callout 1 (Accent Bar)",
172
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 112_500],
173
+ ["adj4", -38333]].freeze }.freeze,
174
+ LINE_CALLOUT_1_BORDER_AND_ACCENT_BAR: { basename: "Line Callout 1 (Border and Accent Bar)",
175
+ default_adjustments: [["adj1", 18750], ["adj2", -8333],
176
+ ["adj3", 112_500], ["adj4", -38333]].freeze }.freeze,
177
+ LINE_CALLOUT_1_NO_BORDER: { basename: "Line Callout 1 (No Border)",
178
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 112_500],
179
+ ["adj4", -38333]].freeze }.freeze,
180
+ LINE_CALLOUT_2: { basename: "Line Callout 2",
181
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 18750], ["adj4", -16667], ["adj5", 112_500],
182
+ ["adj6", -46667]].freeze }.freeze,
183
+ LINE_CALLOUT_2_ACCENT_BAR: { basename: "Line Callout 2 (Accent Bar)",
184
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 18750], ["adj4", -16667], ["adj5", 112_500],
185
+ ["adj6", -46667]].freeze }.freeze,
186
+ LINE_CALLOUT_2_BORDER_AND_ACCENT_BAR: { basename: "Line Callout 2 (Border and Accent Bar)",
187
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 18750], ["adj4", -16667], ["adj5", 112_500],
188
+ ["adj6", -46667]].freeze }.freeze,
189
+ LINE_CALLOUT_2_NO_BORDER: { basename: "Line Callout 2 (No Border)",
190
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 18750], ["adj4", -16667], ["adj5", 112_500],
191
+ ["adj6", -46667]].freeze }.freeze,
192
+ LINE_CALLOUT_3: { basename: "Line Callout 3",
193
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 18750], ["adj4", -16667], ["adj5", 100_000],
194
+ ["adj6", -16667], ["adj7", 112_963], ["adj8", -8333]].freeze }.freeze,
195
+ LINE_CALLOUT_3_ACCENT_BAR: { basename: "Line Callout 3 (Accent Bar)",
196
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 18750], ["adj4", -16667], ["adj5", 100_000],
197
+ ["adj6", -16667], ["adj7", 112_963], ["adj8", -8333]].freeze }.freeze,
198
+ LINE_CALLOUT_3_BORDER_AND_ACCENT_BAR: { basename: "Line Callout 3 (Border and Accent Bar)",
199
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 18750], ["adj4", -16667], ["adj5", 100_000],
200
+ ["adj6", -16667], ["adj7", 112_963], ["adj8", -8333]].freeze }.freeze,
201
+ LINE_CALLOUT_3_NO_BORDER: { basename: "Line Callout 3 (No Border)",
202
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 18750], ["adj4", -16667], ["adj5", 100_000],
203
+ ["adj6", -16667], ["adj7", 112_963], ["adj8", -8333]].freeze }.freeze,
204
+ LINE_CALLOUT_4: { basename: "Line Callout 3",
205
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 18750], ["adj4", -16667], ["adj5", 100_000],
206
+ ["adj6", -16667], ["adj7", 112_963], ["adj8", -8333]].freeze }.freeze,
207
+ LINE_CALLOUT_4_ACCENT_BAR: { basename: "Line Callout 3 (Accent Bar)",
208
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 18750], ["adj4", -16667], ["adj5", 100_000],
209
+ ["adj6", -16667], ["adj7", 112_963], ["adj8", -8333]].freeze }.freeze,
210
+ LINE_CALLOUT_4_BORDER_AND_ACCENT_BAR: { basename: "Line Callout 3 (Border and Accent Bar)",
211
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 18750], ["adj4", -16667], ["adj5", 100_000],
212
+ ["adj6", -16667], ["adj7", 112_963], ["adj8", -8333]].freeze }.freeze,
213
+ LINE_CALLOUT_4_NO_BORDER: { basename: "Line Callout 3 (No Border)",
214
+ default_adjustments: [["adj1", 18750], ["adj2", -8333], ["adj3", 18750], ["adj4", -16667], ["adj5", 100_000],
215
+ ["adj6", -16667], ["adj7", 112_963], ["adj8", -8333]].freeze }.freeze,
216
+ LINE_INVERSE: { basename: "Straight Connector", default_adjustments: [].freeze }.freeze,
217
+ MATH_DIVIDE: { basename: "Division",
218
+ default_adjustments: [["adj1", 23520], ["adj2", 5880], ["adj3", 11760]].freeze }.freeze,
219
+ MATH_EQUAL: { basename: "Equal",
220
+ default_adjustments: [["adj1", 23520], ["adj2", 11760]].freeze }.freeze,
221
+ MATH_MINUS: { basename: "Minus", default_adjustments: [["adj1", 23520]].freeze }.freeze,
222
+ MATH_MULTIPLY: { basename: "Multiply", default_adjustments: [["adj1", 23520]].freeze }.freeze,
223
+ MATH_NOT_EQUAL: { basename: "Not Equal",
224
+ default_adjustments: [["adj1", 23520], ["adj2", 6_600_000],
225
+ ["adj3", 11760]].freeze }.freeze,
226
+ MATH_PLUS: { basename: "Plus", default_adjustments: [["adj1", 23520]].freeze }.freeze,
227
+ MOON: { basename: "Moon", default_adjustments: [["adj", 50000]].freeze }.freeze,
228
+ NON_ISOSCELES_TRAPEZOID: { basename: "Non-isosceles Trapezoid",
229
+ default_adjustments: [["adj1", 25000], ["adj2", 25000]].freeze }.freeze,
230
+ NOTCHED_RIGHT_ARROW: { basename: "Notched Right Arrow",
231
+ default_adjustments: [["adj1", 50000], ["adj2", 50000]].freeze }.freeze,
232
+ NO_SYMBOL: { basename: "\"No\" Symbol", default_adjustments: [["adj", 18750]].freeze }.freeze,
233
+ OCTAGON: { basename: "Octagon", default_adjustments: [["adj", 29289]].freeze }.freeze,
234
+ OVAL: { basename: "Oval", default_adjustments: [].freeze }.freeze,
235
+ OVAL_CALLOUT: { basename: "Oval Callout",
236
+ default_adjustments: [["adj1", -20833], ["adj2", 62500]].freeze }.freeze,
237
+ PARALLELOGRAM: { basename: "Parallelogram", default_adjustments: [["adj", 25000]].freeze }.freeze,
238
+ PENTAGON: { basename: "Pentagon", default_adjustments: [["adj", 50000]].freeze }.freeze,
239
+ PIE: { basename: "Pie", default_adjustments: [["adj1", 0], ["adj2", 16_200_000]].freeze }.freeze,
240
+ PIE_WEDGE: { basename: "Pie", default_adjustments: [].freeze }.freeze,
241
+ PLAQUE: { basename: "Plaque", default_adjustments: [["adj", 16667]].freeze }.freeze,
242
+ PLAQUE_TABS: { basename: "Plaque Tabs", default_adjustments: [].freeze }.freeze,
243
+ QUAD_ARROW: { basename: "Quad Arrow",
244
+ default_adjustments: [["adj1", 22500], ["adj2", 22500], ["adj3", 22500]].freeze }.freeze,
245
+ QUAD_ARROW_CALLOUT: { basename: "Quad Arrow Callout",
246
+ default_adjustments: [["adj1", 18515], ["adj2", 18515], ["adj3", 18515],
247
+ ["adj4", 48123]].freeze }.freeze,
248
+ RECTANGLE: { basename: "Rectangle", default_adjustments: [].freeze }.freeze,
249
+ RECTANGULAR_CALLOUT: { basename: "Rectangular Callout",
250
+ default_adjustments: [["adj1", -20833], ["adj2", 62500]].freeze }.freeze,
251
+ REGULAR_PENTAGON: { basename: "Regular Pentagon",
252
+ default_adjustments: [["hf", 105_146], ["vf", 110_557]].freeze }.freeze,
253
+ RIGHT_ARROW: { basename: "Right Arrow",
254
+ default_adjustments: [["adj1", 50000], ["adj2", 50000]].freeze }.freeze,
255
+ RIGHT_ARROW_CALLOUT: { basename: "Right Arrow Callout",
256
+ default_adjustments: [["adj1", 25000], ["adj2", 25000], ["adj3", 25000],
257
+ ["adj4", 64977]].freeze }.freeze,
258
+ RIGHT_BRACE: { basename: "Right Brace",
259
+ default_adjustments: [["adj1", 8333], ["adj2", 50000]].freeze }.freeze,
260
+ RIGHT_BRACKET: { basename: "Right Bracket", default_adjustments: [["adj", 8333]].freeze }.freeze,
261
+ RIGHT_TRIANGLE: { basename: "Right Triangle", default_adjustments: [].freeze }.freeze,
262
+ ROUNDED_RECTANGLE: { basename: "Rounded Rectangle",
263
+ default_adjustments: [["adj", 16667]].freeze }.freeze,
264
+ ROUNDED_RECTANGULAR_CALLOUT: { basename: "Rounded Rectangular Callout",
265
+ default_adjustments: [["adj1", -20833], ["adj2", 62500],
266
+ ["adj3", 16667]].freeze }.freeze,
267
+ ROUND_1_RECTANGLE: { basename: "Round Single Corner Rectangle",
268
+ default_adjustments: [["adj", 16667]].freeze }.freeze,
269
+ ROUND_2_DIAG_RECTANGLE: { basename: "Round Diagonal Corner Rectangle",
270
+ default_adjustments: [["adj1", 16667], ["adj2", 0]].freeze }.freeze,
271
+ ROUND_2_SAME_RECTANGLE: { basename: "Round Same Side Corner Rectangle",
272
+ default_adjustments: [["adj1", 16667], ["adj2", 0]].freeze }.freeze,
273
+ SMILEY_FACE: { basename: "Smiley Face", default_adjustments: [["adj", 4653]].freeze }.freeze,
274
+ SNIP_1_RECTANGLE: { basename: "Snip Single Corner Rectangle",
275
+ default_adjustments: [["adj", 16667]].freeze }.freeze,
276
+ SNIP_2_DIAG_RECTANGLE: { basename: "Snip Diagonal Corner Rectangle",
277
+ default_adjustments: [["adj1", 0], ["adj2", 16667]].freeze }.freeze,
278
+ SNIP_2_SAME_RECTANGLE: { basename: "Snip Same Side Corner Rectangle",
279
+ default_adjustments: [["adj1", 16667], ["adj2", 0]].freeze }.freeze,
280
+ SNIP_ROUND_RECTANGLE: { basename: "Snip and Round Single Corner Rectangle",
281
+ default_adjustments: [["adj1", 16667], ["adj2", 16667]].freeze }.freeze,
282
+ SQUARE_TABS: { basename: "Square Tabs", default_adjustments: [].freeze }.freeze,
283
+ STAR_10_POINT: { basename: "10-Point Star",
284
+ default_adjustments: [["adj", 42533], ["hf", 105_146]].freeze }.freeze,
285
+ STAR_12_POINT: { basename: "12-Point Star", default_adjustments: [["adj", 37500]].freeze }.freeze,
286
+ STAR_16_POINT: { basename: "16-Point Star", default_adjustments: [["adj", 37500]].freeze }.freeze,
287
+ STAR_24_POINT: { basename: "24-Point Star", default_adjustments: [["adj", 37500]].freeze }.freeze,
288
+ STAR_32_POINT: { basename: "32-Point Star", default_adjustments: [["adj", 37500]].freeze }.freeze,
289
+ STAR_4_POINT: { basename: "4-Point Star", default_adjustments: [["adj", 12500]].freeze }.freeze,
290
+ STAR_5_POINT: { basename: "5-Point Star",
291
+ default_adjustments: [["adj", 19098], ["hf", 105_146], ["vf", 110_557]].freeze }.freeze,
292
+ STAR_6_POINT: { basename: "6-Point Star",
293
+ default_adjustments: [["adj", 28868], ["hf", 115_470]].freeze }.freeze,
294
+ STAR_7_POINT: { basename: "7-Point Star",
295
+ default_adjustments: [["adj", 34601], ["hf", 102_572], ["vf", 105_210]].freeze }.freeze,
296
+ STAR_8_POINT: { basename: "8-Point Star", default_adjustments: [["adj", 37500]].freeze }.freeze,
297
+ STRIPED_RIGHT_ARROW: { basename: "Striped Right Arrow",
298
+ default_adjustments: [["adj1", 50000], ["adj2", 50000]].freeze }.freeze,
299
+ SUN: { basename: "Sun", default_adjustments: [["adj", 25000]].freeze }.freeze,
300
+ SWOOSH_ARROW: { basename: "Swoosh Arrow",
301
+ default_adjustments: [["adj1", 25000], ["adj2", 16667]].freeze }.freeze,
302
+ TEAR: { basename: "Teardrop", default_adjustments: [["adj", 100_000]].freeze }.freeze,
303
+ TRAPEZOID: { basename: "Trapezoid", default_adjustments: [["adj", 25000]].freeze }.freeze,
304
+ UP_ARROW: { basename: "Up Arrow",
305
+ default_adjustments: [["adj1", 50000], ["adj2", 50000]].freeze }.freeze,
306
+ UP_ARROW_CALLOUT: { basename: "Up Arrow Callout",
307
+ default_adjustments: [["adj1", 25000], ["adj2", 25000], ["adj3", 25000],
308
+ ["adj4", 64977]].freeze }.freeze,
309
+ UP_DOWN_ARROW: { basename: "Up-Down Arrow",
310
+ default_adjustments: [["adj1", 50000], ["adj1", 50000], ["adj2", 50000],
311
+ ["adj2", 50000]].freeze }.freeze,
312
+ UP_DOWN_ARROW_CALLOUT: { basename: "Up-Down Arrow Callout",
313
+ default_adjustments: [["adj1", 25000], ["adj2", 25000], ["adj3", 25000],
314
+ ["adj4", 48123]].freeze }.freeze,
315
+ UP_RIBBON: { basename: "Up Ribbon",
316
+ default_adjustments: [["adj1", 16667], ["adj2", 50000]].freeze }.freeze,
317
+ U_TURN_ARROW: { basename: "U-Turn Arrow",
318
+ default_adjustments: [["adj1", 25000], ["adj2", 25000], ["adj3", 25000], ["adj4", 43750],
319
+ ["adj5", 75000]].freeze }.freeze,
320
+ VERTICAL_SCROLL: { basename: "Vertical Scroll", default_adjustments: [["adj", 12500]].freeze }.freeze,
321
+ WAVE: { basename: "Wave", default_adjustments: [["adj1", 12500], ["adj2", 0]].freeze }.freeze
322
+ }.freeze
323
+
324
+ module_function
325
+
326
+ # @param shape_type [Pptx::Enum::MSO_SHAPE]
327
+ def for(shape_type)
328
+ SPECS.fetch(Enum::MSO_SHAPE.fetch(shape_type).name) do
329
+ raise ArgumentError, "no auto-shape spec for #{shape_type.inspect}"
330
+ end
331
+ end
332
+
333
+ def basename(shape_type) = self.for(shape_type)[:basename]
334
+
335
+ def default_adjustments(shape_type) = self.for(shape_type)[:default_adjustments]
336
+ end
337
+ end
@@ -0,0 +1,228 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pptx
4
+ # Build a presentation declaratively.
5
+ #
6
+ # This is a thin layer over the ordinary API — every method here is a call
7
+ # the imperative API can make directly — but it keeps position and size out
8
+ # of positional arguments and lets a whole deck read top to bottom:
9
+ #
10
+ # deck = Pptx.build do |d|
11
+ # d.slide("Title Slide") do |s|
12
+ # s.title = "Annual Report"
13
+ # s.subtitle = "Prepared in Ruby"
14
+ # end
15
+ #
16
+ # d.section("Detail") do
17
+ # d.slide("Blank") do |s|
18
+ # s.shape :ROUNDED_RECTANGLE, at: [Pptx.inches(1), Pptx.inches(1)],
19
+ # size: [Pptx.inches(3), Pptx.inches(1)],
20
+ # fill: "1F497D", text: "Next steps"
21
+ # end
22
+ # end
23
+ # end
24
+ #
25
+ # deck.save("out.pptx")
26
+ #
27
+ # @param template [String, IO, nil] a deck to start from; the built-in
28
+ # default template when omitted
29
+ # @return [Presentation]
30
+ def self.build(template = nil)
31
+ presentation = template ? Presentation.open(template) : Presentation.new_default
32
+ yield DeckBuilder.new(presentation) if block_given?
33
+ presentation
34
+ end
35
+
36
+ # The object yielded by {Pptx.build}.
37
+ class DeckBuilder
38
+ attr_reader :presentation
39
+
40
+ def initialize(presentation)
41
+ @presentation = presentation
42
+ @current_section = nil
43
+ end
44
+
45
+ # Add a slide based on +layout+, which may be a layout name, an index, or
46
+ # a {SlideLayout}.
47
+ #
48
+ # @return [Slide]
49
+ def slide(layout = "Title and Content")
50
+ slide_layout = resolve_layout(layout)
51
+ slide = @presentation.slides.add(slide_layout)
52
+ @current_section << slide if @current_section
53
+ yield SlideBuilder.new(slide) if block_given?
54
+ slide
55
+ end
56
+
57
+ # Group the slides added inside the block into a named section.
58
+ #
59
+ # @return [Section]
60
+ def section(name)
61
+ previous = @current_section
62
+ @current_section = @presentation.sections.add(name)
63
+ begin
64
+ yield if block_given?
65
+ @current_section
66
+ ensure
67
+ @current_section = previous
68
+ end
69
+ end
70
+
71
+ # Set the slide size, e.g. `deck.slide_size = :widescreen`.
72
+ #
73
+ # Accepts `:widescreen` (13.333 x 7.5in), `:standard` (10 x 7.5in), or an
74
+ # explicit [width, height] pair.
75
+ def slide_size=(value)
76
+ width, height =
77
+ case value
78
+ when :widescreen then [Pptx.inches(13.333), Pptx.inches(7.5)]
79
+ when :standard then [Pptx.inches(10), Pptx.inches(7.5)]
80
+ else value
81
+ end
82
+ @presentation.slide_width = width
83
+ @presentation.slide_height = height
84
+ end
85
+
86
+ private
87
+
88
+ def resolve_layout(layout)
89
+ return layout if layout.is_a?(SlideLayout)
90
+
91
+ @presentation.slide_layouts[layout] ||
92
+ raise(NotFoundError, "no slide layout #{layout.inspect}")
93
+ end
94
+ end
95
+
96
+ # The object yielded by {DeckBuilder#slide}.
97
+ class SlideBuilder
98
+ attr_reader :slide
99
+
100
+ def initialize(slide)
101
+ @slide = slide
102
+ end
103
+
104
+ # The title placeholder's text.
105
+ def title=(text)
106
+ placeholder = @slide.shapes.title or
107
+ raise NotFoundError, "this slide's layout has no title placeholder"
108
+
109
+ placeholder.text = text
110
+ end
111
+
112
+ # The text of the placeholder with `idx` 1, which is the body on a content
113
+ # layout and the subtitle on a title layout.
114
+ def body=(text)
115
+ self[1] = text
116
+ end
117
+ alias subtitle= body=
118
+
119
+ # Set the text of the placeholder with the given `idx`.
120
+ def []=(idx, text)
121
+ placeholder = @slide.placeholders[idx] or
122
+ raise NotFoundError, "this slide has no placeholder with idx #{idx}"
123
+
124
+ placeholder.text = text
125
+ end
126
+
127
+ # Add a text box.
128
+ #
129
+ # @return [Shape]
130
+ def text(content, at:, size: nil, font_size: nil, bold: nil, italic: nil,
131
+ color: nil, font: nil, align: nil)
132
+ box = @slide.shapes.add_textbox(at: at, size: size || [Pptx.inches(4), Pptx.inches(1)])
133
+ box.text_frame.text = content
134
+ style_text(box.text_frame, font_size: font_size, bold: bold, italic: italic,
135
+ color: color, font: font, align: align)
136
+ box
137
+ end
138
+
139
+ # Add an auto shape.
140
+ #
141
+ # @return [Shape]
142
+ def shape(shape_type, at:, size:, fill: nil, line: nil, text: nil, **text_options)
143
+ auto_shape = @slide.shapes.add_shape(shape_type, at: at, size: size)
144
+ apply_fill(auto_shape, fill)
145
+ apply_line(auto_shape, line)
146
+ if text
147
+ auto_shape.text_frame.text = text
148
+ style_text(auto_shape.text_frame, **text_options)
149
+ end
150
+ auto_shape
151
+ end
152
+
153
+ # Add a picture. Omit +size+ for the image's native size, or give one
154
+ # dimension to scale the other with it.
155
+ #
156
+ # @return [Picture]
157
+ def picture(image, at:, width: nil, height: nil)
158
+ @slide.shapes.add_picture(image, at: at, width: width, height: height)
159
+ end
160
+
161
+ # Add a table filled from +rows+, an array of arrays.
162
+ #
163
+ # @return [Table]
164
+ def table(rows, at:, size:, header: true)
165
+ rows = rows.to_a
166
+ raise ArgumentError, "table needs at least one row" if rows.empty?
167
+
168
+ columns = rows.map(&:size).max
169
+ frame = @slide.shapes.add_table(rows.size, columns, at: at, size: size)
170
+ table = frame.table
171
+ table.first_row = header
172
+ rows.each_with_index do |row, row_index|
173
+ row.each_with_index { |value, col| table.cell(row_index, col).text = value.to_s }
174
+ end
175
+ table
176
+ end
177
+
178
+ # Add a chart.
179
+ #
180
+ # s.chart :COLUMN_CLUSTERED, categories: %w[East West],
181
+ # series: { "Q1" => [1, 2] },
182
+ # at: [x, y], size: [w, h]
183
+ #
184
+ # @return [Chart]
185
+ def chart(chart_type, categories:, series:, at:, size:)
186
+ data = ChartData.new
187
+ data.categories = categories
188
+ series.each { |name, values| data.add_series(name, values) }
189
+ @slide.shapes.add_chart(chart_type, data, at: at, size: size).chart
190
+ end
191
+
192
+ private
193
+
194
+ def style_text(text_frame, font_size: nil, bold: nil, italic: nil,
195
+ color: nil, font: nil, align: nil)
196
+ paragraph = text_frame.paragraphs.first
197
+ paragraph.alignment = align if align
198
+ # Apply to every run so a multi-run value styles consistently.
199
+ runs = text_frame.paragraphs.flat_map(&:runs)
200
+ runs.each do |run|
201
+ run.font.size = font_size if font_size
202
+ run.font.bold = bold unless bold.nil?
203
+ run.font.italic = italic unless italic.nil?
204
+ run.font.name = font if font
205
+ run.font.color.rgb = rgb(color) if color
206
+ end
207
+ end
208
+
209
+ def apply_fill(shape, fill)
210
+ return if fill.nil?
211
+ return shape.fill.background if fill == :none
212
+
213
+ shape.fill.solid
214
+ shape.fill.fore_color.rgb = rgb(fill)
215
+ end
216
+
217
+ def apply_line(shape, line)
218
+ return if line.nil?
219
+
220
+ color, width = line.is_a?(Hash) ? [line[:color], line[:width]] : [line, nil]
221
+ shape.line.color.rgb = rgb(color) if color
222
+ shape.line.width = width if width
223
+ end
224
+
225
+ # A colour may be given as an RGBColor or as a hex string.
226
+ def rgb(value) = value.is_a?(RGBColor) ? value : RGBColor.from_string(value)
227
+ end
228
+ end