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,316 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ruby_pptx/pattern_matching"
|
|
4
|
+
|
|
5
|
+
require "ruby_pptx/sliceable"
|
|
6
|
+
|
|
7
|
+
require "ruby_pptx/element_proxy"
|
|
8
|
+
require "ruby_pptx/text/text"
|
|
9
|
+
require "ruby_pptx/dml/fill"
|
|
10
|
+
|
|
11
|
+
module Pptx
|
|
12
|
+
# A table inside a graphic frame.
|
|
13
|
+
#
|
|
14
|
+
# table = shape.table
|
|
15
|
+
# table.cell(0, 0).text = "Region"
|
|
16
|
+
# table.columns[0].width = Pptx.inches(2)
|
|
17
|
+
class Table
|
|
18
|
+
attr_reader :element, :parent
|
|
19
|
+
|
|
20
|
+
def initialize(tbl, parent)
|
|
21
|
+
@element = tbl
|
|
22
|
+
@parent = parent
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def part = @parent.part
|
|
26
|
+
|
|
27
|
+
# The cell at +row_idx+, +col_idx+.
|
|
28
|
+
def cell(row_idx, col_idx) = Cell.new(@element.tc(row_idx, col_idx), self)
|
|
29
|
+
|
|
30
|
+
# `table[0, 1]` reads better than `table.cell(0, 1)` in a loop.
|
|
31
|
+
alias [] cell
|
|
32
|
+
|
|
33
|
+
def rows = @rows ||= TableRows.new(@element, self)
|
|
34
|
+
|
|
35
|
+
def columns = @columns ||= TableColumns.new(@element, self)
|
|
36
|
+
|
|
37
|
+
def row_count = @element.tr_list.size
|
|
38
|
+
|
|
39
|
+
def column_count = @element.tblGrid.gridCol_list.size
|
|
40
|
+
|
|
41
|
+
# Whether the first row is styled as a header. The same pattern applies to
|
|
42
|
+
# first_col, last_row, last_col, horz_banding and vert_banding.
|
|
43
|
+
def first_row? = @element.firstRow
|
|
44
|
+
|
|
45
|
+
def first_row=(value)
|
|
46
|
+
@element.firstRow = value
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def first_col? = @element.firstCol
|
|
50
|
+
|
|
51
|
+
def first_col=(value)
|
|
52
|
+
@element.firstCol = value
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def last_row? = @element.lastRow
|
|
56
|
+
|
|
57
|
+
def last_row=(value)
|
|
58
|
+
@element.lastRow = value
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def last_col? = @element.lastCol
|
|
62
|
+
|
|
63
|
+
def last_col=(value)
|
|
64
|
+
@element.lastCol = value
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Whether alternate rows are shaded. Spelled out where python-pptx says
|
|
68
|
+
# `horz_banding`.
|
|
69
|
+
def banded_rows? = @element.bandRow
|
|
70
|
+
|
|
71
|
+
def banded_rows=(value)
|
|
72
|
+
@element.bandRow = value
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def banded_columns? = @element.bandCol
|
|
76
|
+
|
|
77
|
+
def banded_columns=(value)
|
|
78
|
+
@element.bandCol = value
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @api private
|
|
82
|
+
# Resize the containing graphic frame to the sum of the row heights.
|
|
83
|
+
#
|
|
84
|
+
# A row calls this when its height changes: the frame and the table have
|
|
85
|
+
# to agree on the total, or PowerPoint shows the table clipped or adrift
|
|
86
|
+
# inside its frame.
|
|
87
|
+
def notify_height_changed
|
|
88
|
+
@parent.height = Length.emu(rows.sum { |row| row.height.emu })
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# @api private
|
|
92
|
+
# As {#notify_height_changed}, for column widths.
|
|
93
|
+
def notify_width_changed
|
|
94
|
+
@parent.width = Length.emu(columns.sum { |column| column.width.emu })
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Iterate every cell, row by row.
|
|
98
|
+
def each_cell
|
|
99
|
+
return enum_for(:each_cell) { row_count * column_count } unless block_given?
|
|
100
|
+
|
|
101
|
+
row_count.times { |r| column_count.times { |c| yield cell(r, c) } }
|
|
102
|
+
self
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def inspect = "#<Pptx::Table #{row_count}x#{column_count}>"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# The rows of a table.
|
|
109
|
+
class TableRows
|
|
110
|
+
include DeconstructToArray
|
|
111
|
+
|
|
112
|
+
include Sliceable
|
|
113
|
+
include Enumerable
|
|
114
|
+
|
|
115
|
+
def initialize(tbl, table)
|
|
116
|
+
@element = tbl
|
|
117
|
+
@table = table
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def each
|
|
121
|
+
return enum_for(:each) { size } unless block_given?
|
|
122
|
+
|
|
123
|
+
@element.tr_list.each { |tr| yield TableRow.new(tr, @table) }
|
|
124
|
+
self
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def size = @element.tr_list.size
|
|
128
|
+
alias length size
|
|
129
|
+
|
|
130
|
+
def [](index, length = nil)
|
|
131
|
+
slice_members(@element.tr_list, index, length) { |tr| TableRow.new(tr, @table) }
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# The columns of a table.
|
|
136
|
+
class TableColumns
|
|
137
|
+
include DeconstructToArray
|
|
138
|
+
|
|
139
|
+
include Sliceable
|
|
140
|
+
include Enumerable
|
|
141
|
+
|
|
142
|
+
def initialize(tbl, table)
|
|
143
|
+
@element = tbl
|
|
144
|
+
@table = table
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def each
|
|
148
|
+
return enum_for(:each) { size } unless block_given?
|
|
149
|
+
|
|
150
|
+
@element.tblGrid.gridCol_list.each { |col| yield TableColumn.new(col, @table) }
|
|
151
|
+
self
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def size = @element.tblGrid.gridCol_list.size
|
|
155
|
+
alias length size
|
|
156
|
+
|
|
157
|
+
def [](index, length = nil)
|
|
158
|
+
slice_members(@element.tblGrid.gridCol_list, index, length) do |col|
|
|
159
|
+
TableColumn.new(col, @table)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# One row of a table.
|
|
165
|
+
class TableRow < ElementProxy
|
|
166
|
+
def initialize(tr, table)
|
|
167
|
+
super(tr)
|
|
168
|
+
@table = table
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def height = @element.h
|
|
172
|
+
|
|
173
|
+
def height=(value)
|
|
174
|
+
@element.h = value
|
|
175
|
+
@table.notify_height_changed
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def cells = @element.tc_list.map { |tc| Cell.new(tc, @table) }
|
|
179
|
+
|
|
180
|
+
def inspect = "#<Pptx::TableRow height=#{height&.inches}in>"
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# One column of a table.
|
|
184
|
+
class TableColumn < ElementProxy
|
|
185
|
+
def initialize(grid_col, table)
|
|
186
|
+
super(grid_col)
|
|
187
|
+
@table = table
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def width = @element.w
|
|
191
|
+
|
|
192
|
+
def width=(value)
|
|
193
|
+
@element.w = value
|
|
194
|
+
@table.notify_width_changed
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def inspect = "#<Pptx::TableColumn width=#{width&.inches}in>"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# One cell of a table.
|
|
201
|
+
class Cell
|
|
202
|
+
include PatternMatching
|
|
203
|
+
|
|
204
|
+
pattern_keys :text, :span_width, :span_height, :merge_origin?, :spanned?, :vertical_anchor
|
|
205
|
+
|
|
206
|
+
attr_reader :element, :parent
|
|
207
|
+
|
|
208
|
+
def initialize(tc, parent)
|
|
209
|
+
@element = tc
|
|
210
|
+
@parent = parent
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def part = @parent.part
|
|
214
|
+
|
|
215
|
+
def text_frame = @text_frame ||= TextFrame.new(@element.get_or_add_txBody, self)
|
|
216
|
+
|
|
217
|
+
def text = text_frame.text
|
|
218
|
+
|
|
219
|
+
def text=(value)
|
|
220
|
+
text_frame.text = value
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def fill = @fill ||= FillFormat.from_fill_parent(cell_properties)
|
|
224
|
+
|
|
225
|
+
# @return [Pptx::Enum::MSO_ANCHOR, nil]
|
|
226
|
+
def vertical_anchor = cell_properties.anchor
|
|
227
|
+
|
|
228
|
+
def vertical_anchor=(value)
|
|
229
|
+
cell_properties.anchor = value
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def margin_left = cell_properties.marL
|
|
233
|
+
|
|
234
|
+
def margin_left=(value)
|
|
235
|
+
cell_properties.marL = value
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def margin_right = cell_properties.marR
|
|
239
|
+
|
|
240
|
+
def margin_right=(value)
|
|
241
|
+
cell_properties.marR = value
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def margin_top = cell_properties.marT
|
|
245
|
+
|
|
246
|
+
def margin_top=(value)
|
|
247
|
+
cell_properties.marT = value
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def margin_bottom = cell_properties.marB
|
|
251
|
+
|
|
252
|
+
def margin_bottom=(value)
|
|
253
|
+
cell_properties.marB = value
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# How many columns this cell spans.
|
|
257
|
+
#
|
|
258
|
+
# Only a merge origin carries the real span; on any other cell this reads
|
|
259
|
+
# 1 whether or not it is part of a merge. Test {#merge_origin?} first.
|
|
260
|
+
def span_width = @element.gridSpan
|
|
261
|
+
|
|
262
|
+
# How many rows this cell spans. See {#span_width} on when to trust it.
|
|
263
|
+
def span_height = @element.rowSpan
|
|
264
|
+
|
|
265
|
+
# True when this cell is the top-left of a merged range.
|
|
266
|
+
def merge_origin? = @element.merge_origin?
|
|
267
|
+
|
|
268
|
+
# True when this cell is covered by a merge rather than being its origin.
|
|
269
|
+
def spanned? = @element.spanned?
|
|
270
|
+
|
|
271
|
+
# Merge this cell with +other+, which is the opposite corner of the range.
|
|
272
|
+
#
|
|
273
|
+
# Either diagonal may be given, in either order. The text of every cell in
|
|
274
|
+
# the range is gathered into the top-left one, which is the cell that stays
|
|
275
|
+
# visible.
|
|
276
|
+
#
|
|
277
|
+
# @raise [Error] when the cells are in different tables, or the range
|
|
278
|
+
# already contains a merge -- merging over a merge produces a table
|
|
279
|
+
# PowerPoint cannot lay out
|
|
280
|
+
def merge(other)
|
|
281
|
+
raise Error, "cannot merge cells from different tables" unless @element.tbl == other.element.tbl
|
|
282
|
+
|
|
283
|
+
range = @element.tbl.cell_range(@element, other.element)
|
|
284
|
+
raise Error, "the range already contains a merged cell" if range.contains_merged_cell?
|
|
285
|
+
|
|
286
|
+
range.move_content_to_origin
|
|
287
|
+
range.top_row_cells.each { |tc| tc.rowSpan = range.row_count }
|
|
288
|
+
range.left_column_cells.each { |tc| tc.gridSpan = range.column_count }
|
|
289
|
+
range.cells_except_left_column.each { |tc| tc.hMerge = true }
|
|
290
|
+
range.cells_except_top_row.each { |tc| tc.vMerge = true }
|
|
291
|
+
self
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Undo a merge, giving back a separate cell for each grid position it
|
|
295
|
+
# covered. The text stays in the origin cell.
|
|
296
|
+
#
|
|
297
|
+
# @raise [Error] unless this is a merge origin
|
|
298
|
+
def split
|
|
299
|
+
raise Error, "only a merge-origin cell can be split" unless merge_origin?
|
|
300
|
+
|
|
301
|
+
Oxml::CellRange.from_merge_origin(@element).cells.each do |tc|
|
|
302
|
+
tc.rowSpan = 1
|
|
303
|
+
tc.gridSpan = 1
|
|
304
|
+
tc.hMerge = false
|
|
305
|
+
tc.vMerge = false
|
|
306
|
+
end
|
|
307
|
+
self
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def inspect = "#<Pptx::Cell #{text.inspect}>"
|
|
311
|
+
|
|
312
|
+
private
|
|
313
|
+
|
|
314
|
+
def cell_properties = @element.get_or_add_tcPr
|
|
315
|
+
end
|
|
316
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pptx
|
|
4
|
+
# Splits a long table across as many slides as it needs.
|
|
5
|
+
#
|
|
6
|
+
# PowerPoint has no notion of a table that flows: a table lives on one slide
|
|
7
|
+
# and is clipped if it outgrows it. Paging it is therefore something the
|
|
8
|
+
# caller has to do, and this does it — repeating the header on each page so
|
|
9
|
+
# every slide stands on its own.
|
|
10
|
+
#
|
|
11
|
+
# prs.slides.add_table_pages(rows,
|
|
12
|
+
# layout: prs.slide_layouts["Blank"],
|
|
13
|
+
# left: Pptx.inches(0.5), top: Pptx.inches(1),
|
|
14
|
+
# width: Pptx.inches(9), height: Pptx.inches(5))
|
|
15
|
+
module TablePaging
|
|
16
|
+
# The row height assumed when working out how many rows fit, unless the
|
|
17
|
+
# caller says otherwise.
|
|
18
|
+
DEFAULT_ROW_HEIGHT = Length.emu(365_760) # 0.4in
|
|
19
|
+
|
|
20
|
+
module_function
|
|
21
|
+
|
|
22
|
+
# Split +rows+ into pages of at most +rows_per_page+ body rows, repeating
|
|
23
|
+
# +header+ at the top of each.
|
|
24
|
+
#
|
|
25
|
+
# @return [Array<Array<Array>>] one array of rows per page
|
|
26
|
+
def paginate(rows, rows_per_page:, header: nil)
|
|
27
|
+
body = rows.to_a
|
|
28
|
+
return [] if body.empty?
|
|
29
|
+
|
|
30
|
+
raise ArgumentError, "rows_per_page must be positive" unless rows_per_page.positive?
|
|
31
|
+
|
|
32
|
+
body.each_slice(rows_per_page).map do |page|
|
|
33
|
+
header ? [header] + page : page
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# How many body rows fit in +height+, leaving room for a repeated header.
|
|
38
|
+
def rows_per_page(height:, row_height: DEFAULT_ROW_HEIGHT, header: false)
|
|
39
|
+
available = Length.coerce(height).emu
|
|
40
|
+
per_row = Length.coerce(row_height).emu
|
|
41
|
+
raise ArgumentError, "row_height must be positive" unless per_row.positive?
|
|
42
|
+
|
|
43
|
+
fitting = available / per_row
|
|
44
|
+
fitting -= 1 if header
|
|
45
|
+
[fitting, 1].max
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class Slides
|
|
50
|
+
# Add one slide per page of +rows+, each carrying a table.
|
|
51
|
+
#
|
|
52
|
+
# The first row is treated as a header and repeated on every page unless
|
|
53
|
+
# +header+ is false. When +rows_per_page+ is omitted it is derived from
|
|
54
|
+
# +height+ and +row_height+.
|
|
55
|
+
#
|
|
56
|
+
# @return [Array<Slide>] the slides created, in order
|
|
57
|
+
def add_table_pages(rows, layout:, left:, top:, width:, height:,
|
|
58
|
+
rows_per_page: nil, header: true,
|
|
59
|
+
row_height: TablePaging::DEFAULT_ROW_HEIGHT)
|
|
60
|
+
rows = rows.to_a
|
|
61
|
+
return [] if rows.empty?
|
|
62
|
+
|
|
63
|
+
header_row = header ? rows.first : nil
|
|
64
|
+
body = header ? rows.drop(1) : rows
|
|
65
|
+
return [] if body.empty?
|
|
66
|
+
|
|
67
|
+
per_page = rows_per_page || TablePaging.rows_per_page(
|
|
68
|
+
height: height, row_height: row_height, header: !header_row.nil?
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
TablePaging.paginate(body, rows_per_page: per_page, header: header_row).map do |page_rows|
|
|
72
|
+
slide = add(layout)
|
|
73
|
+
fill_table(slide, page_rows, left, top, width, height)
|
|
74
|
+
slide
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def fill_table(slide, page_rows, left, top, width, height)
|
|
81
|
+
columns = page_rows.map(&:size).max
|
|
82
|
+
frame = slide.shapes.add_table(page_rows.size, columns,
|
|
83
|
+
at: [left, top], size: [width, height])
|
|
84
|
+
table = frame.table
|
|
85
|
+
page_rows.each_with_index do |row, row_index|
|
|
86
|
+
row.each_with_index { |value, col| table.cell(row_index, col).text = value.to_s }
|
|
87
|
+
end
|
|
88
|
+
table
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
|
|
2
|
+
<p:notes xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
3
|
+
<p:cSld>
|
|
4
|
+
<p:spTree>
|
|
5
|
+
<p:nvGrpSpPr>
|
|
6
|
+
<p:cNvPr id="1" name=""/>
|
|
7
|
+
<p:cNvGrpSpPr/>
|
|
8
|
+
<p:nvPr/>
|
|
9
|
+
</p:nvGrpSpPr>
|
|
10
|
+
<p:grpSpPr>
|
|
11
|
+
<a:xfrm>
|
|
12
|
+
<a:off x="0" y="0"/>
|
|
13
|
+
<a:ext cx="0" cy="0"/>
|
|
14
|
+
<a:chOff x="0" y="0"/>
|
|
15
|
+
<a:chExt cx="0" cy="0"/>
|
|
16
|
+
</a:xfrm>
|
|
17
|
+
</p:grpSpPr>
|
|
18
|
+
</p:spTree>
|
|
19
|
+
</p:cSld>
|
|
20
|
+
<p:clrMapOvr>
|
|
21
|
+
<a:masterClrMapping/>
|
|
22
|
+
</p:clrMapOvr>
|
|
23
|
+
</p:notes>
|