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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +83 -0
- data/LICENSE +21 -0
- data/NOTICE +48 -0
- data/README.md +272 -0
- data/lib/ruby_pptx/action.rb +120 -0
- data/lib/ruby_pptx/autoshape_spec.rb +337 -0
- data/lib/ruby_pptx/builder.rb +228 -0
- data/lib/ruby_pptx/chart/categories.rb +208 -0
- data/lib/ruby_pptx/chart/chart.rb +443 -0
- data/lib/ruby_pptx/chart/combo.rb +230 -0
- data/lib/ruby_pptx/chart/data.rb +147 -0
- data/lib/ruby_pptx/chart/format.rb +647 -0
- data/lib/ruby_pptx/chart/workbook_writer.rb +212 -0
- data/lib/ruby_pptx/chart/xml_writer.rb +953 -0
- data/lib/ruby_pptx/chart/xy_data.rb +182 -0
- data/lib/ruby_pptx/core_ext.rb +12 -0
- data/lib/ruby_pptx/dml/color.rb +155 -0
- data/lib/ruby_pptx/dml/effect.rb +33 -0
- data/lib/ruby_pptx/dml/fill.rb +234 -0
- data/lib/ruby_pptx/element_proxy.rb +53 -0
- data/lib/ruby_pptx/enum/action.rb +46 -0
- data/lib/ruby_pptx/enum/base.rb +139 -0
- data/lib/ruby_pptx/enum/chart.rb +278 -0
- data/lib/ruby_pptx/enum/dml.rb +237 -0
- data/lib/ruby_pptx/enum/lang.rb +441 -0
- data/lib/ruby_pptx/enum/prog_id.rb +38 -0
- data/lib/ruby_pptx/enum/shapes.rb +514 -0
- data/lib/ruby_pptx/enum/text.rb +108 -0
- data/lib/ruby_pptx/errors.rb +15 -0
- data/lib/ruby_pptx/geometry.rb +89 -0
- data/lib/ruby_pptx/image.rb +347 -0
- data/lib/ruby_pptx/length.rb +120 -0
- data/lib/ruby_pptx/media.rb +64 -0
- data/lib/ruby_pptx/numeric_lengths.rb +30 -0
- data/lib/ruby_pptx/opc/constants.rb +205 -0
- data/lib/ruby_pptx/opc/oxml.rb +99 -0
- data/lib/ruby_pptx/opc/pack_uri.rb +146 -0
- data/lib/ruby_pptx/opc/package.rb +482 -0
- data/lib/ruby_pptx/opc/serialized.rb +229 -0
- data/lib/ruby_pptx/opc/spec.rb +37 -0
- data/lib/ruby_pptx/oxml/action.rb +43 -0
- data/lib/ruby_pptx/oxml/chart.rb +785 -0
- data/lib/ruby_pptx/oxml/content_model.rb +186 -0
- data/lib/ruby_pptx/oxml/core_properties.rb +145 -0
- data/lib/ruby_pptx/oxml/dml/color.rb +115 -0
- data/lib/ruby_pptx/oxml/dml/fill.rb +137 -0
- data/lib/ruby_pptx/oxml/element.rb +329 -0
- data/lib/ruby_pptx/oxml/ns.rb +115 -0
- data/lib/ruby_pptx/oxml/presentation.rb +110 -0
- data/lib/ruby_pptx/oxml/section.rb +65 -0
- data/lib/ruby_pptx/oxml/shapes/autoshape.rb +310 -0
- data/lib/ruby_pptx/oxml/shapes/groupshape.rb +217 -0
- data/lib/ruby_pptx/oxml/shapes/other.rb +405 -0
- data/lib/ruby_pptx/oxml/shapes/shared.rb +292 -0
- data/lib/ruby_pptx/oxml/simple_types.rb +563 -0
- data/lib/ruby_pptx/oxml/slide.rb +290 -0
- data/lib/ruby_pptx/oxml/table.rb +253 -0
- data/lib/ruby_pptx/oxml/text.rb +324 -0
- data/lib/ruby_pptx/oxml/theme.rb +93 -0
- data/lib/ruby_pptx/package.rb +139 -0
- data/lib/ruby_pptx/parts/chart.rb +75 -0
- data/lib/ruby_pptx/parts/core_properties.rb +40 -0
- data/lib/ruby_pptx/parts/embedded_package.rb +43 -0
- data/lib/ruby_pptx/parts/image.rb +55 -0
- data/lib/ruby_pptx/parts/media.rb +17 -0
- data/lib/ruby_pptx/parts/presentation.rb +79 -0
- data/lib/ruby_pptx/parts/slide.rb +221 -0
- data/lib/ruby_pptx/parts/theme.rb +26 -0
- data/lib/ruby_pptx/pattern_matching.rb +50 -0
- data/lib/ruby_pptx/presentation.rb +128 -0
- data/lib/ruby_pptx/refinements.rb +21 -0
- data/lib/ruby_pptx/section.rb +130 -0
- data/lib/ruby_pptx/shapes/adjustments.rb +83 -0
- data/lib/ruby_pptx/shapes/authoring.rb +110 -0
- data/lib/ruby_pptx/shapes/base.rb +138 -0
- data/lib/ruby_pptx/shapes/freeform.rb +141 -0
- data/lib/ruby_pptx/shapes/placeholder.rb +184 -0
- data/lib/ruby_pptx/shapes/shape.rb +337 -0
- data/lib/ruby_pptx/shapes/shape_tree.rb +634 -0
- data/lib/ruby_pptx/sliceable.rb +34 -0
- data/lib/ruby_pptx/slide.rb +430 -0
- data/lib/ruby_pptx/table.rb +316 -0
- data/lib/ruby_pptx/table_paging.rb +91 -0
- data/lib/ruby_pptx/templates/default.pptx +0 -0
- data/lib/ruby_pptx/templates/docx-icon.emf +0 -0
- data/lib/ruby_pptx/templates/generic-icon.emf +0 -0
- data/lib/ruby_pptx/templates/media-speaker.png +0 -0
- data/lib/ruby_pptx/templates/notes.xml +23 -0
- data/lib/ruby_pptx/templates/notesMaster.xml +352 -0
- data/lib/ruby_pptx/templates/pptx-icon.emf +0 -0
- data/lib/ruby_pptx/templates/slideMaster.xml +277 -0
- data/lib/ruby_pptx/templates/theme.xml +321 -0
- data/lib/ruby_pptx/templates/xlsx-icon.emf +0 -0
- data/lib/ruby_pptx/text/fitter.rb +72 -0
- data/lib/ruby_pptx/text/font_metrics.rb +221 -0
- data/lib/ruby_pptx/text/text.rb +393 -0
- data/lib/ruby_pptx/theme.rb +98 -0
- data/lib/ruby_pptx/version.rb +5 -0
- data/lib/ruby_pptx.rb +90 -0
- metadata +174 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ruby_pptx/pattern_matching"
|
|
4
|
+
|
|
5
|
+
require "ruby_pptx/element_proxy"
|
|
6
|
+
require "ruby_pptx/dml/color"
|
|
7
|
+
require "ruby_pptx/dml/fill"
|
|
8
|
+
require "ruby_pptx/oxml/text"
|
|
9
|
+
require "ruby_pptx/enum/text"
|
|
10
|
+
require "ruby_pptx/action"
|
|
11
|
+
require "ruby_pptx/text/fitter"
|
|
12
|
+
|
|
13
|
+
module Pptx
|
|
14
|
+
# The text inside a shape.
|
|
15
|
+
#
|
|
16
|
+
# A text frame always holds at least one paragraph. Reading {#text} joins
|
|
17
|
+
# the paragraphs with "\n"; assigning it replaces everything.
|
|
18
|
+
#
|
|
19
|
+
# frame = shape.text_frame
|
|
20
|
+
# frame.text = "First line\nSecond line"
|
|
21
|
+
# frame.paragraphs.first.runs.first.font.bold = true
|
|
22
|
+
class TextFrame
|
|
23
|
+
include PatternMatching
|
|
24
|
+
|
|
25
|
+
pattern_keys :text, :paragraphs, :word_wrap, :auto_size, :vertical_anchor
|
|
26
|
+
|
|
27
|
+
attr_reader :parent, :element
|
|
28
|
+
|
|
29
|
+
def initialize(tx_body, parent)
|
|
30
|
+
@element = tx_body
|
|
31
|
+
@parent = parent
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def part = @parent.part
|
|
35
|
+
|
|
36
|
+
# The paragraphs of this text frame; there is always at least one.
|
|
37
|
+
#
|
|
38
|
+
# @return [Array<Paragraph>]
|
|
39
|
+
def paragraphs = @element.p_list.map { |p| Paragraph.new(p, self) }
|
|
40
|
+
|
|
41
|
+
# Append an empty paragraph and return it.
|
|
42
|
+
def add_paragraph = Paragraph.new(@element.add_p, self)
|
|
43
|
+
|
|
44
|
+
# All the text, with "\n" between paragraphs and "\v" for each soft line
|
|
45
|
+
# break.
|
|
46
|
+
def text = paragraphs.map(&:text).join("\n")
|
|
47
|
+
|
|
48
|
+
# Replace all text. Each "\n" starts a new paragraph, each "\v" a soft
|
|
49
|
+
# line break within one.
|
|
50
|
+
def text=(value)
|
|
51
|
+
@element.clear_content
|
|
52
|
+
value.to_s.split("\n", -1).each do |paragraph_text|
|
|
53
|
+
@element.add_p.append_text(paragraph_text)
|
|
54
|
+
end
|
|
55
|
+
@element.unclear_content
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Remove all text, leaving a single empty paragraph.
|
|
59
|
+
def clear
|
|
60
|
+
@element.clear_content
|
|
61
|
+
@element.unclear_content
|
|
62
|
+
self
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def margin_left = body_properties.lIns
|
|
66
|
+
|
|
67
|
+
def margin_left=(value)
|
|
68
|
+
body_properties.lIns = value
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def margin_right = body_properties.rIns
|
|
72
|
+
|
|
73
|
+
def margin_right=(value)
|
|
74
|
+
body_properties.rIns = value
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def margin_top = body_properties.tIns
|
|
78
|
+
|
|
79
|
+
def margin_top=(value)
|
|
80
|
+
body_properties.tIns = value
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def margin_bottom = body_properties.bIns
|
|
84
|
+
|
|
85
|
+
def margin_bottom=(value)
|
|
86
|
+
body_properties.bIns = value
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @return [Pptx::Enum::MSO_ANCHOR, nil] nil when inherited
|
|
90
|
+
def vertical_anchor = body_properties.anchor
|
|
91
|
+
|
|
92
|
+
def vertical_anchor=(value)
|
|
93
|
+
body_properties.anchor = value
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @return [Pptx::Enum::MSO_AUTO_SIZE, nil] nil when inherited
|
|
97
|
+
def auto_size = body_properties.autofit
|
|
98
|
+
|
|
99
|
+
def auto_size=(value)
|
|
100
|
+
body_properties.autofit = value
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# True, false, or nil when the setting is inherited.
|
|
104
|
+
def word_wrap
|
|
105
|
+
case body_properties.wrap
|
|
106
|
+
when Oxml::SimpleTypes::ST_TextWrappingType::SQUARE then true
|
|
107
|
+
when Oxml::SimpleTypes::ST_TextWrappingType::NONE then false
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def word_wrap=(value)
|
|
112
|
+
unless [true, false, nil].include?(value)
|
|
113
|
+
raise ArgumentError, "word_wrap must be true, false or nil, got #{value.inspect}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
body_properties.wrap =
|
|
117
|
+
case value
|
|
118
|
+
when true then Oxml::SimpleTypes::ST_TextWrappingType::SQUARE
|
|
119
|
+
when false then Oxml::SimpleTypes::ST_TextWrappingType::NONE
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Shrink the text until it fits the shape, and apply that size to all of it.
|
|
124
|
+
#
|
|
125
|
+
# Turns word wrap on and autofit off, then sets every run -- and the
|
|
126
|
+
# end-paragraph properties, so typing in PowerPoint continues in the same
|
|
127
|
+
# font -- to +font_family+ at the size found.
|
|
128
|
+
#
|
|
129
|
+
# frame.fit_text(font_file: "/usr/share/fonts/.../DejaVuSans.ttf",
|
|
130
|
+
# font_family: "DejaVu Sans", max_size: 28)
|
|
131
|
+
#
|
|
132
|
+
# A +font_file+ is required: measuring needs the actual glyph outlines, and
|
|
133
|
+
# this gem does not go looking through system font directories for them.
|
|
134
|
+
# The size is measured from the font's own metrics rather than by
|
|
135
|
+
# rendering, so it can differ from python-pptx's by a point on text that
|
|
136
|
+
# only just fits; see PORTING.md.
|
|
137
|
+
#
|
|
138
|
+
# @return [Integer] the point size applied
|
|
139
|
+
def fit_text(font_file:, font_family: "Calibri", max_size: 18, bold: false, italic: false)
|
|
140
|
+
raise Error, "cannot fit text in a text frame with no text" if text.empty?
|
|
141
|
+
|
|
142
|
+
size = TextFitter.best_fit_font_size(text, extents: extents, max_size: max_size,
|
|
143
|
+
font_file: font_file)
|
|
144
|
+
raise Error, "text does not fit at any size up to #{max_size}pt" if size.nil?
|
|
145
|
+
|
|
146
|
+
apply_fit(font_family, size, bold, italic)
|
|
147
|
+
size
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# The area text actually gets to occupy: the shape less its margins.
|
|
151
|
+
#
|
|
152
|
+
# @return [Array(Integer, Integer)] width and height in EMU
|
|
153
|
+
def extents
|
|
154
|
+
[@parent.width - margin_left - margin_right, @parent.height - margin_top - margin_bottom]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def inspect = "#<Pptx::TextFrame #{text.inspect}>"
|
|
158
|
+
|
|
159
|
+
private
|
|
160
|
+
|
|
161
|
+
def apply_fit(family, size, bold, italic)
|
|
162
|
+
self.auto_size = Enum::MSO_AUTO_SIZE::NONE
|
|
163
|
+
self.word_wrap = true
|
|
164
|
+
each_character_properties do |properties|
|
|
165
|
+
font = Font.new(properties)
|
|
166
|
+
font.name = family
|
|
167
|
+
font.size = Pptx.pt(size)
|
|
168
|
+
font.bold = bold
|
|
169
|
+
font.italic = italic
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Every run in the frame, plus each paragraph's end-paragraph properties.
|
|
174
|
+
def each_character_properties
|
|
175
|
+
@element.p_list.each do |p|
|
|
176
|
+
p.content_children.each { |child| yield child.get_or_add_rPr }
|
|
177
|
+
yield p.get_or_add_endParaRPr
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def body_properties = @element.bodyPr
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# One paragraph of a text frame.
|
|
185
|
+
class Paragraph
|
|
186
|
+
include PatternMatching
|
|
187
|
+
|
|
188
|
+
pattern_keys :text, :runs, :level, :alignment
|
|
189
|
+
|
|
190
|
+
attr_reader :element, :parent
|
|
191
|
+
|
|
192
|
+
def initialize(p, parent)
|
|
193
|
+
@element = p
|
|
194
|
+
@parent = parent
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def part = @parent.part
|
|
198
|
+
|
|
199
|
+
# @return [Array<Run>]
|
|
200
|
+
def runs = @element.r_list.map { |r| Run.new(r, self) }
|
|
201
|
+
|
|
202
|
+
def add_run(text = nil) = Run.new(@element.add_run(text), self)
|
|
203
|
+
|
|
204
|
+
def add_line_break
|
|
205
|
+
@element.add_line_break
|
|
206
|
+
self
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# The text of this paragraph, with "\v" for each soft line break.
|
|
210
|
+
def text = @element.text
|
|
211
|
+
|
|
212
|
+
# Replace this paragraph's text, turning "\v" (or "\n") into line breaks
|
|
213
|
+
# rather than new paragraphs -- a paragraph cannot contain another.
|
|
214
|
+
def text=(value)
|
|
215
|
+
clear
|
|
216
|
+
@element.append_text(value)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Remove the content, keeping the paragraph and its properties.
|
|
220
|
+
def clear
|
|
221
|
+
@element.content_children.each { |child| @element.remove(child) }
|
|
222
|
+
self
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# @return [Pptx::Enum::PP_ALIGN, nil] nil when inherited
|
|
226
|
+
def alignment = paragraph_properties.algn
|
|
227
|
+
|
|
228
|
+
def alignment=(value)
|
|
229
|
+
paragraph_properties.algn = value
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Outline level, 0 for the top level.
|
|
233
|
+
def level = paragraph_properties.lvl
|
|
234
|
+
|
|
235
|
+
def level=(value)
|
|
236
|
+
paragraph_properties.lvl = value
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# A Float is a number of lines; a {Pptx::Length} is a fixed distance.
|
|
240
|
+
def line_spacing = paragraph_properties.line_spacing
|
|
241
|
+
|
|
242
|
+
def line_spacing=(value)
|
|
243
|
+
paragraph_properties.line_spacing = value
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def space_before = paragraph_properties.space_before
|
|
247
|
+
|
|
248
|
+
def space_before=(value)
|
|
249
|
+
paragraph_properties.space_before = value
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def space_after = paragraph_properties.space_after
|
|
253
|
+
|
|
254
|
+
def space_after=(value)
|
|
255
|
+
paragraph_properties.space_after = value
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# The default character formatting for runs in this paragraph.
|
|
259
|
+
def font = Font.new(paragraph_properties.get_or_add_defRPr)
|
|
260
|
+
|
|
261
|
+
def inspect = "#<Pptx::Paragraph #{text.inspect}>"
|
|
262
|
+
|
|
263
|
+
private
|
|
264
|
+
|
|
265
|
+
def paragraph_properties = @element.get_or_add_pPr
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# A run: a span of text sharing one set of character properties.
|
|
269
|
+
class Run
|
|
270
|
+
include PatternMatching
|
|
271
|
+
|
|
272
|
+
pattern_keys :text, :font, :hyperlink
|
|
273
|
+
|
|
274
|
+
attr_reader :element, :parent
|
|
275
|
+
|
|
276
|
+
def initialize(r, parent)
|
|
277
|
+
@element = r
|
|
278
|
+
@parent = parent
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def part = @parent.part
|
|
282
|
+
|
|
283
|
+
def text = @element.text
|
|
284
|
+
|
|
285
|
+
def text=(value)
|
|
286
|
+
@element.text = value
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def font = Font.new(@element.get_or_add_rPr)
|
|
290
|
+
|
|
291
|
+
# What happens when this run of text is clicked.
|
|
292
|
+
def click_action = @click_action ||= ActionSetting.new(@element.get_or_add_rPr, self)
|
|
293
|
+
|
|
294
|
+
# The URL this run links to, or nil.
|
|
295
|
+
def hyperlink = click_action.url
|
|
296
|
+
|
|
297
|
+
def hyperlink=(url)
|
|
298
|
+
click_action.address = url
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def inspect = "#<Pptx::Run #{text.inspect}>"
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Character formatting: typeface, size, weight, colour.
|
|
305
|
+
#
|
|
306
|
+
# Every property returns nil when the value is inherited from the style
|
|
307
|
+
# hierarchy rather than set here, and assigning nil restores that
|
|
308
|
+
# inheritance.
|
|
309
|
+
class Font
|
|
310
|
+
include PatternMatching
|
|
311
|
+
|
|
312
|
+
pattern_keys :name, :size, :bold, :italic, :underline
|
|
313
|
+
|
|
314
|
+
attr_reader :element
|
|
315
|
+
|
|
316
|
+
def initialize(r_pr)
|
|
317
|
+
@element = r_pr
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def bold = @element.b
|
|
321
|
+
|
|
322
|
+
def bold=(value)
|
|
323
|
+
@element.b = value
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def italic = @element.i
|
|
327
|
+
|
|
328
|
+
def italic=(value)
|
|
329
|
+
@element.i = value
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# The typeface name, or nil when inherited from the theme.
|
|
333
|
+
def name = @element.latin&.typeface
|
|
334
|
+
|
|
335
|
+
def name=(value)
|
|
336
|
+
if value.nil?
|
|
337
|
+
@element.remove_latin
|
|
338
|
+
else
|
|
339
|
+
@element.get_or_add_latin.typeface = value
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# @return [Pptx::Length, nil]
|
|
344
|
+
def size
|
|
345
|
+
centipoints = @element.sz
|
|
346
|
+
centipoints && Pptx::Length.centipoints(centipoints)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def size=(value)
|
|
350
|
+
@element.sz = value.nil? ? nil : Pptx::Length.coerce(value).centipoints
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# true for a single underline, false for none, a
|
|
354
|
+
# {Pptx::Enum::MSO_UNDERLINE} member for anything fancier, nil when
|
|
355
|
+
# inherited.
|
|
356
|
+
def underline
|
|
357
|
+
value = @element.u
|
|
358
|
+
return nil if value.nil?
|
|
359
|
+
return false if value == Enum::MSO_UNDERLINE::NONE
|
|
360
|
+
return true if value == Enum::MSO_UNDERLINE::SINGLE_LINE
|
|
361
|
+
|
|
362
|
+
value
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def underline=(value)
|
|
366
|
+
@element.u =
|
|
367
|
+
case value
|
|
368
|
+
when true then Enum::MSO_UNDERLINE::SINGLE_LINE
|
|
369
|
+
when false then Enum::MSO_UNDERLINE::NONE
|
|
370
|
+
else value
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# @return [Pptx::Enum::MSO_LANGUAGE_ID, nil]
|
|
375
|
+
def language = @element.lang
|
|
376
|
+
|
|
377
|
+
def language=(value)
|
|
378
|
+
@element.lang = value
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# The fill of the text itself.
|
|
382
|
+
def fill = @fill ||= FillFormat.from_fill_parent(@element)
|
|
383
|
+
|
|
384
|
+
# The text colour. Accessing it makes the fill solid, since a colour has
|
|
385
|
+
# to live on some fill.
|
|
386
|
+
def color
|
|
387
|
+
fill.solid if fill.type.nil?
|
|
388
|
+
fill.fore_color
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def inspect = "#<Pptx::Font name=#{name.inspect} size=#{size&.pt} bold=#{bold.inspect}>"
|
|
392
|
+
end
|
|
393
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ruby_pptx/element_proxy"
|
|
4
|
+
require "ruby_pptx/dml/color"
|
|
5
|
+
require "ruby_pptx/oxml/theme"
|
|
6
|
+
|
|
7
|
+
module Pptx
|
|
8
|
+
# The colours and fonts a slide master draws from.
|
|
9
|
+
#
|
|
10
|
+
# theme = master.theme
|
|
11
|
+
# theme.name = "Corporate"
|
|
12
|
+
# theme.colors[:accent1].rgb = Pptx::RGBColor.from_string("1F497D")
|
|
13
|
+
# theme.fonts.major = "Georgia"
|
|
14
|
+
#
|
|
15
|
+
# A theme also carries a *format* scheme -- the numbered fill, line and
|
|
16
|
+
# effect styles that shapes and slide backgrounds refer to by `idx`. Those
|
|
17
|
+
# are inherited from the base theme and not editable here; writing a
|
|
18
|
+
# coherent set by hand is a design exercise, not a configuration one.
|
|
19
|
+
class Theme < PartElementProxy
|
|
20
|
+
def name = @element.name
|
|
21
|
+
|
|
22
|
+
def name=(value)
|
|
23
|
+
# PowerPoint shows the theme name in three places and expects them to
|
|
24
|
+
# agree, so the schemes are renamed with it.
|
|
25
|
+
@element.name = value
|
|
26
|
+
@element.clrScheme.name = value
|
|
27
|
+
@element.fontScheme.name = value
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @return [ThemeColors]
|
|
31
|
+
def colors = @colors ||= ThemeColors.new(@element.clrScheme, self)
|
|
32
|
+
|
|
33
|
+
# @return [ThemeFonts]
|
|
34
|
+
def fonts = @fonts ||= ThemeFonts.new(@element.fontScheme, self)
|
|
35
|
+
|
|
36
|
+
def inspect = "#<Pptx::Theme #{name.inspect}>"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# The twelve theme colours, addressed by name.
|
|
40
|
+
#
|
|
41
|
+
# theme.colors[:accent1].rgb = Pptx::RGBColor.from_string("C0504D")
|
|
42
|
+
# theme.colors[:accent1].rgb #=> #<Pptx::RGBColor C0504D>
|
|
43
|
+
#
|
|
44
|
+
# Each entry is an ordinary {Pptx::ColorFormat}, so it takes an explicit RGB
|
|
45
|
+
# value the same way any other colour in the library does.
|
|
46
|
+
class ThemeColors < ParentedElementProxy
|
|
47
|
+
include Enumerable
|
|
48
|
+
|
|
49
|
+
# The names, in the order PowerPoint lists them.
|
|
50
|
+
NAMES = Oxml::CT_ColorScheme::SLOTS.map(&:to_sym).freeze
|
|
51
|
+
|
|
52
|
+
# @return [Pptx::ColorFormat]
|
|
53
|
+
def [](name) = ColorFormat.from_color_choice_parent(@element.slot(name))
|
|
54
|
+
|
|
55
|
+
def each
|
|
56
|
+
return enum_for(:each) { NAMES.size } unless block_given?
|
|
57
|
+
|
|
58
|
+
NAMES.each { |name| yield name, self[name] }
|
|
59
|
+
self
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Set several at once, which is how a palette is usually handed over.
|
|
63
|
+
#
|
|
64
|
+
# theme.colors.update(accent1: "1F497D", accent2: "C0504D")
|
|
65
|
+
#
|
|
66
|
+
# Values may be {Pptx::RGBColor} or a hex string.
|
|
67
|
+
def update(**colors)
|
|
68
|
+
colors.each do |name, value|
|
|
69
|
+
self[name].rgb = value.is_a?(RGBColor) ? value : RGBColor.from_string(value.to_s)
|
|
70
|
+
end
|
|
71
|
+
self
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def names = NAMES
|
|
75
|
+
|
|
76
|
+
def inspect = "#<Pptx::ThemeColors #{NAMES.size} colours>"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# The heading and body typefaces of a theme.
|
|
80
|
+
#
|
|
81
|
+
# PowerPoint calls these "major" and "minor"; they are what `+mj-lt` and
|
|
82
|
+
# `+mn-lt` resolve to in placeholder text.
|
|
83
|
+
class ThemeFonts < ParentedElementProxy
|
|
84
|
+
def major = @element.majorFont.typeface
|
|
85
|
+
|
|
86
|
+
def major=(value)
|
|
87
|
+
@element.majorFont.typeface = value
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def minor = @element.minorFont.typeface
|
|
91
|
+
|
|
92
|
+
def minor=(value)
|
|
93
|
+
@element.minorFont.typeface = value
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def inspect = "#<Pptx::ThemeFonts major=#{major.inspect} minor=#{minor.inspect}>"
|
|
97
|
+
end
|
|
98
|
+
end
|
data/lib/ruby_pptx.rb
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "nokogiri"
|
|
4
|
+
require "zip"
|
|
5
|
+
|
|
6
|
+
require "ruby_pptx/version"
|
|
7
|
+
require "ruby_pptx/errors"
|
|
8
|
+
require "ruby_pptx/length"
|
|
9
|
+
require "ruby_pptx/geometry"
|
|
10
|
+
require "ruby_pptx/oxml/ns"
|
|
11
|
+
require "ruby_pptx/oxml/simple_types"
|
|
12
|
+
require "ruby_pptx/oxml/element"
|
|
13
|
+
require "ruby_pptx/oxml/content_model"
|
|
14
|
+
require "ruby_pptx/opc/pack_uri"
|
|
15
|
+
require "ruby_pptx/opc/constants"
|
|
16
|
+
require "ruby_pptx/opc/spec"
|
|
17
|
+
require "ruby_pptx/opc/oxml"
|
|
18
|
+
require "ruby_pptx/opc/serialized"
|
|
19
|
+
require "ruby_pptx/opc/package"
|
|
20
|
+
|
|
21
|
+
require "ruby_pptx/sliceable"
|
|
22
|
+
require "ruby_pptx/pattern_matching"
|
|
23
|
+
require "ruby_pptx/element_proxy"
|
|
24
|
+
require "ruby_pptx/oxml/presentation"
|
|
25
|
+
require "ruby_pptx/oxml/slide"
|
|
26
|
+
require "ruby_pptx/oxml/core_properties"
|
|
27
|
+
require "ruby_pptx/oxml/dml/color"
|
|
28
|
+
require "ruby_pptx/oxml/dml/fill"
|
|
29
|
+
require "ruby_pptx/oxml/text"
|
|
30
|
+
require "ruby_pptx/dml/color"
|
|
31
|
+
require "ruby_pptx/dml/fill"
|
|
32
|
+
require "ruby_pptx/dml/effect"
|
|
33
|
+
require "ruby_pptx/oxml/theme"
|
|
34
|
+
require "ruby_pptx/theme"
|
|
35
|
+
require "ruby_pptx/text/text"
|
|
36
|
+
require "ruby_pptx/text/font_metrics"
|
|
37
|
+
require "ruby_pptx/text/fitter"
|
|
38
|
+
require "ruby_pptx/oxml/table"
|
|
39
|
+
require "ruby_pptx/oxml/chart"
|
|
40
|
+
require "ruby_pptx/chart/data"
|
|
41
|
+
require "ruby_pptx/chart/workbook_writer"
|
|
42
|
+
require "ruby_pptx/chart/xy_data"
|
|
43
|
+
require "ruby_pptx/chart/xml_writer"
|
|
44
|
+
require "ruby_pptx/chart/format"
|
|
45
|
+
require "ruby_pptx/chart/combo"
|
|
46
|
+
require "ruby_pptx/chart/chart"
|
|
47
|
+
require "ruby_pptx/image"
|
|
48
|
+
require "ruby_pptx/media"
|
|
49
|
+
require "ruby_pptx/oxml/action"
|
|
50
|
+
require "ruby_pptx/action"
|
|
51
|
+
require "ruby_pptx/oxml/shapes/shared"
|
|
52
|
+
require "ruby_pptx/oxml/shapes/autoshape"
|
|
53
|
+
require "ruby_pptx/oxml/shapes/groupshape"
|
|
54
|
+
require "ruby_pptx/oxml/shapes/other"
|
|
55
|
+
require "ruby_pptx/autoshape_spec"
|
|
56
|
+
require "ruby_pptx/shapes/base"
|
|
57
|
+
require "ruby_pptx/shapes/shape"
|
|
58
|
+
require "ruby_pptx/shapes/placeholder"
|
|
59
|
+
require "ruby_pptx/shapes/freeform"
|
|
60
|
+
require "ruby_pptx/shapes/adjustments"
|
|
61
|
+
require "ruby_pptx/shapes/authoring"
|
|
62
|
+
require "ruby_pptx/shapes/shape_tree"
|
|
63
|
+
require "ruby_pptx/oxml/section"
|
|
64
|
+
require "ruby_pptx/slide"
|
|
65
|
+
require "ruby_pptx/section"
|
|
66
|
+
require "ruby_pptx/table_paging"
|
|
67
|
+
require "ruby_pptx/builder"
|
|
68
|
+
require "ruby_pptx/presentation"
|
|
69
|
+
require "ruby_pptx/parts/core_properties"
|
|
70
|
+
require "ruby_pptx/parts/image"
|
|
71
|
+
require "ruby_pptx/parts/media"
|
|
72
|
+
require "ruby_pptx/parts/chart"
|
|
73
|
+
require "ruby_pptx/parts/slide"
|
|
74
|
+
require "ruby_pptx/parts/presentation"
|
|
75
|
+
require "ruby_pptx/parts/theme"
|
|
76
|
+
require "ruby_pptx/parts/embedded_package"
|
|
77
|
+
require "ruby_pptx/package"
|
|
78
|
+
|
|
79
|
+
require "ruby_pptx/enum/base"
|
|
80
|
+
require "ruby_pptx/enum/action"
|
|
81
|
+
require "ruby_pptx/enum/chart"
|
|
82
|
+
require "ruby_pptx/enum/dml"
|
|
83
|
+
require "ruby_pptx/enum/lang"
|
|
84
|
+
require "ruby_pptx/enum/shapes"
|
|
85
|
+
require "ruby_pptx/enum/text"
|
|
86
|
+
require "ruby_pptx/enum/prog_id"
|
|
87
|
+
|
|
88
|
+
# A Ruby object model for PowerPoint (.pptx) files, ported from python-pptx.
|
|
89
|
+
module Pptx
|
|
90
|
+
end
|