rubyXL-git-ref-6002046 2.0.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 (88) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +20 -0
  3. data/Gemfile.lock +63 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +197 -0
  6. data/Rakefile +58 -0
  7. data/VERSION +1 -0
  8. data/lib/rubyXL.rb +21 -0
  9. data/lib/rubyXL/cell.rb +325 -0
  10. data/lib/rubyXL/generic_storage.rb +40 -0
  11. data/lib/rubyXL/objects/border.rb +53 -0
  12. data/lib/rubyXL/objects/cell_style.rb +73 -0
  13. data/lib/rubyXL/objects/color.rb +23 -0
  14. data/lib/rubyXL/objects/column_range.rb +88 -0
  15. data/lib/rubyXL/objects/data_validation.rb +31 -0
  16. data/lib/rubyXL/objects/defined_name.rb +27 -0
  17. data/lib/rubyXL/objects/fill.rb +42 -0
  18. data/lib/rubyXL/objects/font.rb +109 -0
  19. data/lib/rubyXL/objects/formula.rb +8 -0
  20. data/lib/rubyXL/objects/ooxml_object.rb +177 -0
  21. data/lib/rubyXL/objects/reference.rb +98 -0
  22. data/lib/rubyXL/objects/sheet_view.rb +62 -0
  23. data/lib/rubyXL/objects/worksheet.rb +11 -0
  24. data/lib/rubyXL/parser.rb +307 -0
  25. data/lib/rubyXL/private_class.rb +95 -0
  26. data/lib/rubyXL/shared_strings.rb +35 -0
  27. data/lib/rubyXL/workbook.rb +342 -0
  28. data/lib/rubyXL/worksheet.rb +1118 -0
  29. data/lib/rubyXL/writer/app_writer.rb +51 -0
  30. data/lib/rubyXL/writer/calc_chain_writer.rb +18 -0
  31. data/lib/rubyXL/writer/content_types_writer.rb +113 -0
  32. data/lib/rubyXL/writer/core_writer.rb +34 -0
  33. data/lib/rubyXL/writer/generic_writer.rb +33 -0
  34. data/lib/rubyXL/writer/root_rels_writer.rb +17 -0
  35. data/lib/rubyXL/writer/shared_strings_writer.rb +21 -0
  36. data/lib/rubyXL/writer/styles_writer.rb +64 -0
  37. data/lib/rubyXL/writer/theme_writer.rb +337 -0
  38. data/lib/rubyXL/writer/workbook_rels_writer.rb +43 -0
  39. data/lib/rubyXL/writer/workbook_writer.rb +73 -0
  40. data/lib/rubyXL/writer/worksheet_writer.rb +164 -0
  41. data/lib/rubyXL/zip.rb +20 -0
  42. data/rdoc/created.rid +36 -0
  43. data/rdoc/fonts.css +167 -0
  44. data/rdoc/fonts/Lato-Light.ttf +0 -0
  45. data/rdoc/fonts/Lato-LightItalic.ttf +0 -0
  46. data/rdoc/fonts/Lato-Regular.ttf +0 -0
  47. data/rdoc/fonts/Lato-RegularItalic.ttf +0 -0
  48. data/rdoc/fonts/SourceCodePro-Bold.ttf +0 -0
  49. data/rdoc/fonts/SourceCodePro-Regular.ttf +0 -0
  50. data/rdoc/images/add.png +0 -0
  51. data/rdoc/images/arrow_up.png +0 -0
  52. data/rdoc/images/brick.png +0 -0
  53. data/rdoc/images/brick_link.png +0 -0
  54. data/rdoc/images/bug.png +0 -0
  55. data/rdoc/images/bullet_black.png +0 -0
  56. data/rdoc/images/bullet_toggle_minus.png +0 -0
  57. data/rdoc/images/bullet_toggle_plus.png +0 -0
  58. data/rdoc/images/date.png +0 -0
  59. data/rdoc/images/delete.png +0 -0
  60. data/rdoc/images/find.png +0 -0
  61. data/rdoc/images/loadingAnimation.gif +0 -0
  62. data/rdoc/images/macFFBgHack.png +0 -0
  63. data/rdoc/images/package.png +0 -0
  64. data/rdoc/images/page_green.png +0 -0
  65. data/rdoc/images/page_white_text.png +0 -0
  66. data/rdoc/images/page_white_width.png +0 -0
  67. data/rdoc/images/plugin.png +0 -0
  68. data/rdoc/images/ruby.png +0 -0
  69. data/rdoc/images/tag_blue.png +0 -0
  70. data/rdoc/images/tag_green.png +0 -0
  71. data/rdoc/images/transparent.png +0 -0
  72. data/rdoc/images/wrench.png +0 -0
  73. data/rdoc/images/wrench_orange.png +0 -0
  74. data/rdoc/images/zoom.png +0 -0
  75. data/rdoc/js/darkfish.js +140 -0
  76. data/rdoc/js/jquery.js +18 -0
  77. data/rdoc/js/navigation.js +142 -0
  78. data/rdoc/js/search.js +109 -0
  79. data/rdoc/js/search_index.js +1 -0
  80. data/rdoc/js/searcher.js +228 -0
  81. data/rdoc/rdoc.css +580 -0
  82. data/rubyXL-git-ref-6002046.gemspec +143 -0
  83. data/spec/lib/cell_spec.rb +407 -0
  84. data/spec/lib/color_spec.rb +14 -0
  85. data/spec/lib/parser_spec.rb +80 -0
  86. data/spec/lib/workbook_spec.rb +73 -0
  87. data/spec/lib/worksheet_spec.rb +1789 -0
  88. metadata +231 -0
@@ -0,0 +1,1118 @@
1
+ module RubyXL
2
+ class Worksheet < PrivateClass
3
+ include Enumerable
4
+
5
+ attr_accessor :sheet_name, :sheet_id, :sheet_data, :column_ranges, :merged_cells, :pane,
6
+ :validations, :sheet_views, :legacy_drawings, :extLst, :workbook,
7
+ :row_styles, :drawings
8
+
9
+ SHEET_NAME_TEMPLATE = 'Sheet%d'
10
+
11
+ def initialize(workbook, sheet_name = nil, sheet_data= [[nil]], cols=[], merged_cells=[])
12
+ @workbook = workbook
13
+
14
+ @sheet_name = sheet_name || get_default_name
15
+ @sheet_id = nil
16
+ @sheet_data = sheet_data
17
+ @column_ranges = cols
18
+ @merged_cells = merged_cells || []
19
+ @row_styles = []
20
+ @sheet_views = [ RubyXL::SheetView.new ]
21
+ @extLst = nil
22
+ @legacy_drawings = []
23
+ @drawings = []
24
+ @validations = []
25
+ end
26
+
27
+ def get_default_name
28
+ n = 0
29
+
30
+ begin
31
+ name = SHEET_NAME_TEMPLATE % (n += 1)
32
+ end until @workbook[name].nil?
33
+
34
+ name
35
+ end
36
+ private :get_default_name
37
+
38
+ # allows for easier access to sheet_data
39
+ def [](row = 0)
40
+ @sheet_data[row]
41
+ end
42
+
43
+ def each
44
+ @sheet_data.each { |row| yield(row) }
45
+ end
46
+
47
+ #returns 2d array of just the cell values (without style or formula information)
48
+ def extract_data(args = {})
49
+ raw_values = args.delete(:raw) || false
50
+ return @sheet_data.map {|row| row.map {|c| if c.is_a?(Cell) then c.value(:raw => raw_values) else nil end}}
51
+ end
52
+
53
+ def get_table(headers = [], opts = {})
54
+ validate_workbook
55
+
56
+ headers = [headers] unless headers.is_a?(Array)
57
+ row_num = find_first_row_with_content(headers)
58
+
59
+ return nil if row_num.nil?
60
+
61
+ table_hash = {}
62
+ table_hash[:table] = []
63
+
64
+ header_row = @sheet_data[row_num]
65
+ header_row.each_with_index { |header_cell, index|
66
+ break if index>0 && !opts[:last_header].nil? && !header_row[index-1].nil? && !header_row[index-1].value.nil? && header_row[index-1].value.to_s==opts[:last_header]
67
+ next if header_cell.nil? || header_cell.value.nil?
68
+ header = header_cell.value.to_s
69
+ table_hash[:sorted_headers]||=[]
70
+ table_hash[:sorted_headers] << header
71
+ table_hash[header] = []
72
+
73
+ original_row = row_num + 1
74
+ current_row = original_row
75
+
76
+ cell = @sheet_data[current_row][index]
77
+
78
+ # makes array of hashes in table_hash[:table]
79
+ # as well as hash of arrays in table_hash[header]
80
+ table_index = current_row - original_row
81
+ cell_test = (!cell.nil? && !cell.value.nil?)
82
+
83
+ while cell_test || (table_hash[:table][table_index] && !table_hash[:table][table_index].empty?)
84
+ table_hash[header] << cell.value if cell_test
85
+ table_index = current_row - original_row
86
+
87
+ if cell_test then
88
+ table_hash[:table][table_index] ||= {}
89
+ table_hash[:table][table_index][header] = cell.value
90
+ end
91
+
92
+ current_row += 1
93
+ if @sheet_data[current_row].nil? then
94
+ cell = nil
95
+ else
96
+ cell = @sheet_data[current_row][index]
97
+ end
98
+ cell_test = (!cell.nil? && !cell.value.nil?)
99
+ end
100
+ }
101
+
102
+ return table_hash
103
+ end
104
+
105
+ #changes color of fill in (zer0 indexed) row
106
+ def change_row_fill(row = 0, rgb = 'ffffff')
107
+ validate_workbook
108
+ validate_nonnegative(row)
109
+ ensure_cell_exists(row)
110
+ Color.validate_color(rgb)
111
+
112
+ if @row_styles[(Integer(row)+1)].nil?
113
+ @row_styles[(Integer(row)+1)] = {}
114
+ @row_styles[(Integer(row)+1)][:style] = 0
115
+ end
116
+
117
+ @row_styles[(Integer(row)+1)][:style] = modify_fill(@workbook,Integer(@row_styles[(Integer(row)+1)][:style]),rgb)
118
+
119
+ @sheet_data[Integer(row)].each do |c|
120
+ unless c.nil?
121
+ c.change_fill(rgb)
122
+ end
123
+ end
124
+ end
125
+
126
+ def change_row_font_name(row = 0, font_name = 'Verdana')
127
+ ensure_cell_exists(row)
128
+ font = row_font(row).dup
129
+ font.set_name(font_name)
130
+ change_row_font(row, Worksheet::NAME, font_name, font)
131
+ end
132
+
133
+ def change_row_font_size(row = 0, font_size=10)
134
+ ensure_cell_exists(row)
135
+ font = row_font(row).dup
136
+ font.set_size(font_size)
137
+ change_row_font(row, Worksheet::SIZE, font_size, font)
138
+ end
139
+
140
+ def change_row_font_color(row = 0, font_color='000000')
141
+ ensure_cell_exists(row)
142
+ Color.validate_color(font_color)
143
+ font = row_font(row).dup
144
+ font.set_rgb_color(font_color)
145
+ change_row_font(row, Worksheet::COLOR, font_color, font)
146
+ end
147
+
148
+ def change_row_italics(row = 0, italicized=false)
149
+ ensure_cell_exists(row)
150
+ font = row_font(row).dup
151
+ font.set_italic(italicized)
152
+ change_row_font(row, Worksheet::ITALICS, italicized, font)
153
+ end
154
+
155
+ def change_row_bold(row = 0, bolded=false)
156
+ ensure_cell_exists(row)
157
+ font = row_font(row).dup
158
+ font.set_bold(bolded)
159
+ change_row_font(row, Worksheet::BOLD, bolded, font)
160
+ end
161
+
162
+ def change_row_underline(row = 0, underlined=false)
163
+ ensure_cell_exists(row)
164
+ font = row_font(row).dup
165
+ font.set_underline(underlined)
166
+ change_row_font(row, Worksheet::UNDERLINE, underlined, font)
167
+ end
168
+
169
+ def change_row_strikethrough(row = 0, struckthrough=false)
170
+ ensure_cell_exists(row)
171
+ font = row_font(row).dup
172
+ font.set_strikethrough(struckthrough)
173
+ change_row_font(row, Worksheet::STRIKETHROUGH, struckthrough, font)
174
+ end
175
+
176
+ def change_row_height(row = 0, height=10)
177
+ validate_workbook
178
+ validate_nonnegative(row)
179
+
180
+ ensure_cell_exists(row)
181
+
182
+ if height.to_i.to_s == height.to_s
183
+ height = Integer(height)
184
+ elsif height.to_f.to_s == height.to_s
185
+ height = Float(height)
186
+ else
187
+ raise 'You must enter a number for the height'
188
+ end
189
+
190
+ if @row_styles[(row+1)].nil?
191
+ @row_styles[(row+1)] = {}
192
+ @row_styles[(row+1)][:style] = 0
193
+ end
194
+ @row_styles[(row+1)][:height] = height
195
+ @row_styles[(row+1)][:customHeight] = '1'
196
+ end
197
+
198
+ def change_row_horizontal_alignment(row = 0,alignment='center')
199
+ validate_workbook
200
+ validate_nonnegative(row)
201
+ validate_horizontal_alignment(alignment)
202
+ change_row_alignment(row,alignment,true)
203
+ end
204
+
205
+ def change_row_vertical_alignment(row = 0,alignment='center')
206
+ validate_workbook
207
+ validate_nonnegative(row)
208
+ validate_vertical_alignment(alignment)
209
+ change_row_alignment(row,alignment,false)
210
+ end
211
+
212
+ def change_row_border_top(row = 0, weight = 'thin')
213
+ change_row_border(row, :top, weight)
214
+ end
215
+
216
+ def change_row_border_left(row = 0, weight = 'thin')
217
+ change_row_border(row, :left, weight)
218
+ end
219
+
220
+ def change_row_border_right(row = 0, weight = 'thin')
221
+ change_row_border(row, :right, weight)
222
+ end
223
+
224
+ def change_row_border_bottom(row = 0, weight = 'thin')
225
+ change_row_border(row, :bottom, weight)
226
+ end
227
+
228
+ def change_row_border_diagonal(row = 0, weight = 'thin')
229
+ change_row_border(row, :diagonal, weight)
230
+ end
231
+
232
+ # Changes font name of column
233
+ def change_column_font_name(col = 0, font_name = 'Verdana')
234
+ xf = get_col_xf(col)
235
+ font = @workbook.fonts[xf.font_id].dup
236
+ font.set_name(font_name)
237
+ change_column_font(col, Worksheet::NAME, font_name, font, xf)
238
+ end
239
+
240
+ # Changes font size of column
241
+ def change_column_font_size(col=0, font_size=10)
242
+ xf = get_col_xf(col)
243
+ font = @workbook.fonts[xf.font_id].dup
244
+ font.set_size(font_size)
245
+ change_column_font(col, Worksheet::SIZE, font_size, font, xf)
246
+ end
247
+
248
+ # Changes font color of column
249
+ def change_column_font_color(col=0, font_color='000000')
250
+ Color.validate_color(font_color)
251
+
252
+ xf = get_col_xf(col)
253
+ font = @workbook.fonts[xf.font_id].dup
254
+ font.set_rgb_color(font_color)
255
+ change_column_font(col, Worksheet::COLOR, font_color, font, xf)
256
+ end
257
+
258
+ def change_column_italics(col = 0, italicized = false)
259
+ xf = get_col_xf(col)
260
+ font = @workbook.fonts[xf.font_id].dup
261
+ font.set_italic(italicized)
262
+ change_column_font(col, Worksheet::ITALICS, italicized, font, xf)
263
+ end
264
+
265
+ def change_column_bold(col = 0, bolded = false)
266
+ xf = get_col_xf(col)
267
+ font = @workbook.fonts[xf.font_id].dup
268
+ font.set_bold(bolded)
269
+ change_column_font(col, Worksheet::BOLD, bolded, font, xf)
270
+ end
271
+
272
+ def change_column_underline(col = 0, underlined = false)
273
+ xf = get_col_xf(col)
274
+ font = @workbook.fonts[xf.font_id].dup
275
+ font.set_underline(underlined)
276
+ change_column_font(col, Worksheet::UNDERLINE, underlined, font, xf)
277
+ end
278
+
279
+ def change_column_strikethrough(col=0, struckthrough=false)
280
+ xf = get_col_xf(col)
281
+ font = @workbook.fonts[xf.font_id].dup
282
+ font.set_strikethrough(struckthrough)
283
+ change_column_font(col, Worksheet::STRIKETHROUGH, struckthrough, font, xf)
284
+ end
285
+
286
+ def change_column_width(col = 0, width = 13)
287
+ validate_workbook
288
+ validate_nonnegative(col)
289
+ ensure_cell_exists(0, col)
290
+
291
+ RubyXL::ColumnRange.update(col, @column_ranges, { 'width' => width, 'customWidth' => 1 })
292
+ end
293
+
294
+ def get_column_style_index(col)
295
+ range = RubyXL::ColumnRange.find(col, @column_ranges)
296
+ (range && range.style_index) || 0
297
+ end
298
+
299
+ def change_column_fill(col=0, color_index='ffffff')
300
+ validate_workbook
301
+ validate_nonnegative(col)
302
+ Color.validate_color(color_index)
303
+ ensure_cell_exists(0, col)
304
+
305
+ new_style_index = modify_fill(@workbook, get_column_style_index(col), color_index)
306
+ RubyXL::ColumnRange.update(col, @column_ranges, { 'style' => new_style_index })
307
+
308
+ @sheet_data.each { |row|
309
+ c = row[col]
310
+ c.change_fill(color_index) if c
311
+ }
312
+ end
313
+
314
+ def change_column_horizontal_alignment(col=0,alignment='center')
315
+ validate_horizontal_alignment(alignment)
316
+ change_column_alignment(col,alignment,true)
317
+ end
318
+
319
+ def change_column_vertical_alignment(col=0,alignment='center')
320
+ validate_vertical_alignment(alignment)
321
+ change_column_alignment(col,alignment,false)
322
+ end
323
+
324
+ def change_column_border_top(col=0,weight = 'thin')
325
+ change_column_border(col, :top, weight)
326
+ end
327
+
328
+ def change_column_border_left(col=0,weight = 'thin')
329
+ change_column_border(col, :left, weight)
330
+ end
331
+
332
+ def change_column_border_right(col=0,weight = 'thin')
333
+ change_column_border(col, :right, weight)
334
+ end
335
+
336
+ def change_column_border_bottom(col=0,weight = 'thin')
337
+ change_column_border(col, :bottom, weight)
338
+ end
339
+
340
+ def change_column_border_diagonal(col=0,weight = 'thin')
341
+ change_column_border(col, :diagonal, weight)
342
+ end
343
+
344
+ # merges cells within a rectangular range
345
+ def merge_cells(row1 = 0, col1 = 0, row2 = 0, col2 = 0)
346
+ validate_workbook
347
+ @merged_cells << RubyXL::Reference.new(row1, row2, col1, col2)
348
+ end
349
+
350
+ def add_cell(row=0, column=0, data='', formula=nil,overwrite=true)
351
+ validate_workbook
352
+ validate_nonnegative(row)
353
+ validate_nonnegative(column)
354
+ ensure_cell_exists(row, column)
355
+
356
+ datatype = (formula.nil?) ? RubyXL::Cell::RAW_STRING : ''
357
+
358
+ if overwrite || @sheet_data[row][column].nil?
359
+ @sheet_data[row][column] = Cell.new(self,row,column,data,formula,datatype)
360
+
361
+ if (data.is_a?Integer) || (data.is_a?Float)
362
+ @sheet_data[row][column].datatype = ''
363
+ end
364
+ col = RubyXL::ColumnRange.find(column, @column_ranges)
365
+
366
+ if @row_styles[row+1] != nil
367
+ @sheet_data[row][column].style_index = @row_styles[row+1][:style]
368
+ elsif col != nil
369
+ @sheet_data[row][column].style_index = col.style_index
370
+ end
371
+ end
372
+
373
+ @sheet_data[row][column].style_index ||= 0
374
+
375
+ add_cell_style(row,column)
376
+
377
+ return @sheet_data[row][column]
378
+ end
379
+
380
+ def add_cell_obj(cell, overwrite=true)
381
+ validate_workbook
382
+
383
+ if cell.nil?
384
+ return cell
385
+ end
386
+
387
+ row = cell.row
388
+ column = cell.column
389
+
390
+ validate_nonnegative(row)
391
+ validate_nonnegative(column)
392
+ ensure_cell_exists(row, column)
393
+
394
+ if overwrite || @sheet_data[row][column].nil?
395
+ @sheet_data[row][column] = cell
396
+ end
397
+
398
+ add_cell_style(row,column)
399
+
400
+ return @sheet_data[row][column]
401
+ end
402
+
403
+ def delete_row(row_index=0)
404
+ validate_workbook
405
+ validate_nonnegative(row_index)
406
+
407
+ if row_index >= @sheet_data.size
408
+ return nil
409
+ end
410
+
411
+ deleted = @sheet_data.delete_at(row_index)
412
+ row_num = row_index+1
413
+
414
+ @row_styles.delete_at(row_index)
415
+
416
+ # Change cell row numbers
417
+ row_index.upto(@sheet_data.size - 1) { |index|
418
+ @sheet_data[index].each{ |c| c.row -= 1 unless c.nil? }
419
+ }
420
+
421
+ return deleted
422
+ end
423
+
424
+ #inserts row at row_index, pushes down, copies style from below (row previously at that index)
425
+ #USE OF THIS METHOD will break formulas which reference cells which are being "pushed down"
426
+ def insert_row(row_index=0)
427
+ validate_workbook
428
+ validate_nonnegative(row_index)
429
+
430
+ ensure_cell_exists(row_index)
431
+
432
+ @sheet_data.insert(row_index,Array.new(@sheet_data[row_index].size))
433
+
434
+ row_num = row_index+1
435
+
436
+ #copy cell styles from row above, (or below if first row)
437
+ @sheet_data[row_index].each_index do |i|
438
+ if row_index > 0
439
+ old_cell = @sheet_data[row_index-1][i]
440
+ else
441
+ old_cell = @sheet_data[row_index+1][i]
442
+ end
443
+
444
+ unless old_cell.nil?
445
+ #only add cell if style exists, not copying content
446
+
447
+ if @row_styles[(row_num+1)].nil?
448
+ @row_styles[(row_num+1)] = {:style=>0}
449
+ end
450
+ if old_cell.style_index != 0 && old_cell.style_index != @row_styles[(row_num+1)][:style]
451
+ c = Cell.new(self,row_index,i)
452
+ c.style_index = old_cell.style_index
453
+ @sheet_data[row_index][i] = c
454
+ end
455
+ end
456
+ end
457
+
458
+ #copy row styles from row above, (or below if first row)
459
+ (@row_styles.size+1).downto(row_num+1) do |i|
460
+ @row_styles[i] = @row_styles[(i-1)]
461
+ end
462
+
463
+ if row_index > 0
464
+ @row_styles[row_num] = @row_styles[(row_num-1)]
465
+ else
466
+ @row_styles[row_num] = nil #@row_styles[(row_num+1).to_s]
467
+ end
468
+
469
+ #update row value for all rows below
470
+ (row_index+1).upto(@sheet_data.size-1) do |i|
471
+ row = @sheet_data[i]
472
+ row.each do |c|
473
+ unless c.nil?
474
+ c.row += 1
475
+ end
476
+ end
477
+ end
478
+
479
+ return @sheet_data[row_index]
480
+ end
481
+
482
+ def delete_column(col_index=0)
483
+ validate_workbook
484
+ validate_nonnegative(col_index)
485
+
486
+ if col_index >= @sheet_data[0].size
487
+ return nil
488
+ end
489
+
490
+ #delete column
491
+ @sheet_data.map {|r| r.delete_at(col_index)}
492
+
493
+ #change column numbers for cells to right of deleted column
494
+ @sheet_data.each_with_index do |row,row_index|
495
+ (col_index...(row.size)).each do |index|
496
+ if @sheet_data[row_index][index].is_a?(Cell)
497
+ @sheet_data[row_index][index].column -= 1
498
+ end
499
+ end
500
+ end
501
+
502
+ @column_ranges.each { |range| range.delete_column(col_index) }
503
+ end
504
+
505
+ # inserts column at col_index, pushes everything right, takes styles from column to left
506
+ # USE OF THIS METHOD will break formulas which reference cells which are being "pushed down"
507
+ def insert_column(col_index = 0)
508
+ validate_workbook
509
+ validate_nonnegative(col_index)
510
+ ensure_cell_exists(0, col_index)
511
+
512
+ old_range = col_index > 0 ? RubyXL::ColumnRange.find(col_index, @column_ranges) : RubyXL::ColumnRange.new
513
+
514
+ #go through each cell in column
515
+ @sheet_data.each_with_index do |row, row_index|
516
+ old_cell = row[col_index]
517
+ new_cell = nil
518
+
519
+ if old_cell && old_cell.style_index != 0 &&
520
+ old_range && old_range.style_index != old_cell.style_index.to_i then
521
+ new_cell = Cell.new(self, row_index, col_index)
522
+ new_cell.style_index = old_cell.style_index
523
+ end
524
+
525
+ row.insert(col_index, new_cell)
526
+ end
527
+
528
+ ColumnRange.insert_column(col_index, @column_ranges)
529
+
530
+ #update column numbers
531
+ @sheet_data.each { |row|
532
+ (col_index + 1).upto(row.size) { |col|
533
+ row[col].column = col unless row[col].nil?
534
+ }
535
+ }
536
+
537
+ end
538
+
539
+ def insert_cell(row = 0, col = 0, data = nil, formula = nil, shift = nil)
540
+ validate_workbook
541
+ validate_nonnegative(row)
542
+ validate_nonnegative(col)
543
+ ensure_cell_exists(row, col)
544
+
545
+ case shift
546
+ when nil then # No shifting at all
547
+ when :right then
548
+ @sheet_data[row].insert(col,nil)
549
+ (row...(@sheet_data[row].size)).each { |index|
550
+ if @sheet_data[row][index].is_a?(Cell)
551
+ @sheet_data[row][index].column += 1
552
+ end
553
+ }
554
+ when :down then
555
+ @sheet_data << Array.new(@sheet_data[row].size)
556
+ (@sheet_data.size-1).downto(row+1) { |index|
557
+ @sheet_data[index][col] = @sheet_data[index-1][col]
558
+ }
559
+ else
560
+ raise 'invalid shift option'
561
+ end
562
+
563
+ return add_cell(row,col,data,formula)
564
+ end
565
+
566
+ # by default, only sets cell to nil
567
+ # if :left is specified, method will shift row contents to the right of the deleted cell to the left
568
+ # if :up is specified, method will shift column contents below the deleted cell upward
569
+ def delete_cell(row = 0, col=0, shift=nil)
570
+ validate_workbook
571
+ validate_nonnegative(row)
572
+ validate_nonnegative(col)
573
+
574
+ return nil if @sheet_data.size <= row || @sheet_data[row].size <= col
575
+
576
+ cell = @sheet_data[row][col]
577
+
578
+ case shift
579
+ when nil then
580
+ @sheet_data[row][col] = nil
581
+ when :left then
582
+ @sheet_data[row].delete_at(col)
583
+ @sheet_data[row] << nil
584
+ (col...(@sheet_data[row].size)).each { |index|
585
+ if @sheet_data[row][index].is_a?(Cell)
586
+ @sheet_data[row][index].column -= 1
587
+ end
588
+ }
589
+ when :up then
590
+ (row...(@sheet_data.size-1)).each { |index|
591
+ @sheet_data[index][col] = @sheet_data[index+1][col]
592
+ if @sheet_data[index][col].is_a?(Cell)
593
+ @sheet_data[index][col].row -= 1
594
+ end
595
+ }
596
+
597
+ @sheet_data.last[col].row -= 1 if @sheet_data.last[col].is_a?(Cell)
598
+ else
599
+ raise 'invalid shift option'
600
+ end
601
+
602
+ return cell
603
+ end
604
+
605
+ def get_row_fill(row = 0)
606
+ validate_workbook
607
+ validate_nonnegative(row)
608
+
609
+ if @sheet_data.size <= row
610
+ return nil
611
+ end
612
+
613
+ if @row_styles[(row+1)].nil?
614
+ return "ffffff" #default, white
615
+ end
616
+
617
+ xf = get_row_xf(row)
618
+
619
+ return @workbook.get_fill_color(xf)
620
+ end
621
+
622
+ def get_row_font_name(row = 0)
623
+ font = row_font(row)
624
+ font && font.get_name
625
+ end
626
+
627
+ def get_row_font_size(row = 0)
628
+ font = row_font(row)
629
+ font && font.get_size
630
+ end
631
+
632
+ def get_row_font_color(row = 0)
633
+ font = row_font(row)
634
+ color = font && font.color
635
+ color && (color.rgb || '000000')
636
+ end
637
+
638
+ def is_row_italicized(row = 0)
639
+ font = row_font(row)
640
+ font && font.is_italic
641
+ end
642
+
643
+ def is_row_bolded(row = 0)
644
+ font = row_font(row)
645
+ font && font.is_bold
646
+ end
647
+
648
+ def is_row_underlined(row = 0)
649
+ font = row_font(row)
650
+ font && font.is_underlined
651
+ end
652
+
653
+ def is_row_struckthrough(row = 0)
654
+ font = row_font(row)
655
+ font && font.is_strikethrough
656
+ end
657
+
658
+ def get_row_height(row = 0)
659
+ validate_workbook
660
+ validate_nonnegative(row)
661
+
662
+ if @sheet_data.size <= row
663
+ return nil
664
+ end
665
+
666
+ if @row_styles[(row+1)].nil?
667
+ return 13
668
+ else
669
+ @row_styles[(row+1)][:height]
670
+ end
671
+ end
672
+
673
+ def get_row_horizontal_alignment(row = 0)
674
+ return get_row_alignment(row,true)
675
+ end
676
+
677
+ def get_row_vertical_alignment(row = 0)
678
+ return get_row_alignment(row,false)
679
+ end
680
+
681
+ def get_row_border_top(row = 0)
682
+ return get_row_border(row, :top)
683
+ end
684
+
685
+ def get_row_border_left(row = 0)
686
+ return get_row_border(row, :left)
687
+ end
688
+
689
+ def get_row_border_right(row = 0)
690
+ return get_row_border(row, :right)
691
+ end
692
+
693
+ def get_row_border_bottom(row = 0)
694
+ return get_row_border(row, :bottom)
695
+ end
696
+
697
+ def get_row_border_diagonal(row = 0)
698
+ return get_row_border(row, :diagonal)
699
+ end
700
+
701
+ def get_column_font_name(col = 0)
702
+ font = column_font(col)
703
+ font && font.get_name
704
+ end
705
+
706
+ def get_column_font_size(col = 0)
707
+ font = column_font(col)
708
+ font && font.get_size
709
+ end
710
+
711
+ def get_column_font_color(col = 0)
712
+ font = column_font(col)
713
+ font && (font.get_rgb_color || '000000')
714
+ end
715
+
716
+ def is_column_italicized(col = 0)
717
+ font = column_font(col)
718
+ font && font.is_italic
719
+ end
720
+
721
+ def is_column_bolded(col = 0)
722
+ font = column_font(col)
723
+ font && font.is_bold
724
+ end
725
+
726
+ def is_column_underlined(col = 0)
727
+ font = column_font(col)
728
+ font && font.is_underlined
729
+ end
730
+
731
+ def is_column_struckthrough(col = 0)
732
+ font = column_font(col)
733
+ font && font.is_strikethrough
734
+ end
735
+
736
+ def get_column_width(col=0)
737
+ validate_workbook
738
+ validate_nonnegative(col)
739
+
740
+ if @sheet_data[0].size <= col
741
+ return nil
742
+ end
743
+
744
+ range = RubyXL::ColumnRange.find(col, @column_ranges)
745
+
746
+ (range && range.width) || 10
747
+ end
748
+
749
+ def get_column_fill(col=0)
750
+ validate_workbook
751
+ validate_nonnegative(col)
752
+ return nil if @sheet_data[0].size <= col
753
+ @workbook.get_fill_color(get_col_xf(col))
754
+ end
755
+
756
+ def get_column_horizontal_alignment(col=0)
757
+ get_column_alignment(col, :horizontal)
758
+ end
759
+
760
+ def get_column_vertical_alignment(col=0)
761
+ get_column_alignment(col, :vertical)
762
+ end
763
+
764
+ def get_column_border_top(col=0)
765
+ get_column_border(col, :top)
766
+ end
767
+
768
+ def get_column_border_left(col=0)
769
+ get_column_border(col, :left)
770
+ end
771
+
772
+ def get_column_border_right(col=0)
773
+ get_column_border(col, :right)
774
+ end
775
+
776
+ def get_column_border_bottom(col=0)
777
+ get_column_border(col, :bottom)
778
+ end
779
+
780
+ def get_column_border_diagonal(col=0)
781
+ get_column_border(col, :diagonal)
782
+ end
783
+
784
+
785
+ private
786
+
787
+ Worksheet::NAME = 0
788
+ Worksheet::SIZE = 1
789
+ Worksheet::COLOR = 2
790
+ Worksheet::ITALICS = 3
791
+ Worksheet::BOLD = 4
792
+ Worksheet::UNDERLINE = 5
793
+ Worksheet::STRIKETHROUGH = 6
794
+
795
+ #row_styles is assumed to not be nil at specified row
796
+ def get_row_xf(row)
797
+ @row_styles[(row+1)] ||= { :style => 0 }
798
+ @workbook.cell_xfs[@row_styles[(row+1)][:style]]
799
+ end
800
+
801
+ def row_font(row)
802
+ validate_workbook
803
+ validate_nonnegative(row)
804
+ xf = get_row_xf(row)
805
+ return nil if @sheet_data.size <= row
806
+ @workbook.fonts[xf.font_id]
807
+ end
808
+
809
+ def get_row_alignment(row, is_horizontal)
810
+ validate_workbook
811
+ validate_nonnegative(row)
812
+
813
+ if @sheet_data.size <= row || @row_styles[(row+1)].nil?
814
+ return nil
815
+ end
816
+
817
+ xf_obj = @workbook.cell_xfs[@row_styles[(row+1)][:style]]
818
+
819
+ return nil if xf_obj.alignment.nil?
820
+
821
+ if is_horizontal then
822
+ return xf_obj.alignment.horizontal
823
+ else
824
+ return xf_obj.alignment.vertical
825
+ end
826
+ end
827
+
828
+ def get_row_border(row, border_direction)
829
+ validate_workbook
830
+ validate_nonnegative(row)
831
+
832
+ return nil if @sheet_data.size <= row || @row_styles[(row+1)].nil?
833
+
834
+ border = @workbook.borders[get_row_xf(row).border_id]
835
+ border && border.get_edge_style(border_direction)
836
+ end
837
+
838
+ def column_font(col)
839
+ validate_workbook
840
+ validate_nonnegative(col)
841
+
842
+ return nil if @sheet_data[0].size <= col
843
+ style_index = get_cols_style_index(col)
844
+ @workbook.fonts[@workbook.cell_xfs[style_index].font_id]
845
+ end
846
+
847
+ def get_column_alignment(col, type)
848
+ validate_workbook
849
+ validate_nonnegative(col)
850
+
851
+ return nil if @sheet_data[0].size <= col
852
+ xf = @workbook.cell_xfs[get_cols_style_index(col)]
853
+ xf.alignment && xf.alignment.send(type)
854
+ end
855
+
856
+ def get_column_border(col, border_direction)
857
+ validate_workbook
858
+ validate_nonnegative(col)
859
+
860
+ return nil if @sheet_data[0].size <= col
861
+
862
+ xf = @workbook.cell_xfs[get_cols_style_index(col)]
863
+
864
+ border = @workbook.borders[xf.border_id]
865
+ border && border.get_edge_style(border_direction)
866
+ end
867
+
868
+ #validates Workbook, ensures that this worksheet is in @workbook
869
+ def validate_workbook()
870
+ unless @workbook.nil? || @workbook.worksheets.nil?
871
+ return if @workbook.worksheets.include?(self)
872
+ end
873
+
874
+ raise "This worksheet #{self} is not in workbook #{@workbook}"
875
+ end
876
+
877
+ def get_cols_style_index(col)
878
+ range = RubyXL::ColumnRange.find(col, @column_ranges)
879
+ (range && range.style_index) || 0
880
+ end
881
+
882
+ # Helper method to update the row styles array
883
+ # change_type - NAME or SIZE or COLOR etc
884
+ # main method to change font, called from each separate font mutator method
885
+ def change_row_font(row, change_type, arg, font)
886
+ validate_workbook
887
+ validate_nonnegative(row)
888
+ ensure_cell_exists(row)
889
+
890
+ xf = workbook.register_new_font(font, get_row_xf(row))
891
+ @row_styles[(row+1)][:style] = workbook.register_new_xf(xf, @row_styles[(row+1)][:style])
892
+
893
+ @sheet_data[row] ||= []
894
+ @sheet_data[Integer(row)].each { |c|
895
+ font_switch(c, change_type, arg) unless c.nil?
896
+ }
897
+ end
898
+
899
+ # Helper method to update the fonts and cell styles array
900
+ # main method to change font, called from each separate font mutator method
901
+ def change_column_font(col, change_type, arg, font, xf)
902
+ validate_workbook
903
+ validate_nonnegative(col)
904
+ ensure_cell_exists(0, col)
905
+
906
+ xf = workbook.register_new_font(font, xf)
907
+ new_style_index = workbook.register_new_xf(xf, get_col_style(col))
908
+ RubyXL::ColumnRange.update(col, @column_ranges, { 'style' => new_style_index })
909
+
910
+ @sheet_data.each { |row|
911
+ c = row[col]
912
+ font_switch(c, change_type, arg) unless c.nil?
913
+ }
914
+ end
915
+
916
+ #performs correct modification based on what type of change_type is specified
917
+ def font_switch(c,change_type,arg)
918
+ case change_type
919
+ when Worksheet::NAME
920
+ unless arg.is_a?String
921
+ raise 'Not a String'
922
+ end
923
+ c.change_font_name(arg)
924
+ when Worksheet::SIZE
925
+ unless arg.is_a?(Integer) || arg.is_a?(Float)
926
+ raise 'Not a Number'
927
+ end
928
+ c.change_font_size(arg)
929
+ when Worksheet::COLOR
930
+ Color.validate_color(arg)
931
+ c.change_font_color(arg)
932
+ when Worksheet::ITALICS
933
+ unless arg == !!arg
934
+ raise 'Not a boolean'
935
+ end
936
+ c.change_font_italics(arg)
937
+ when Worksheet::BOLD
938
+ unless arg == !!arg
939
+ raise 'Not a boolean'
940
+ end
941
+ c.change_font_bold(arg)
942
+ when Worksheet::UNDERLINE
943
+ unless arg == !!arg
944
+ raise 'Not a boolean'
945
+ end
946
+ c.change_font_underline(arg)
947
+ when Worksheet::STRIKETHROUGH
948
+ unless arg == !!arg
949
+ raise 'Not a boolean'
950
+ end
951
+ c.change_font_strikethrough(arg)
952
+ else
953
+ raise 'Invalid change_type'
954
+ end
955
+ end
956
+
957
+ # Ensures that cell with +row_index+ and +col_index+ exists in
958
+ # +sheet_data+ arrays, growing them up if necessary.
959
+ def ensure_cell_exists(row_index, col_index = 0)
960
+ # Writing anything to a cell in the array automatically creates all the members
961
+ # with lower indices, filling them with +nil+s. But, we can't just write +nil+
962
+ # to +col_index+ because it may be less than +size+! So we read from that index
963
+ # (if it didn't exist, we will get nil) and write right back.
964
+ @sheet_data.each { |r| r[col_index] = r[col_index] }
965
+
966
+ col_size = @sheet_data[0].size
967
+
968
+ # Doing +.downto()+ here so the reallocation of row array has to only happen once,
969
+ # when it is extended to max size; after that, we will be writing into existing
970
+ # (but empty) members. Additional checks are not necessary, because if +row_index+
971
+ # is less than +size+, then +.downto()+ will not execute, and if it equals +size+,
972
+ # then the block will be invoked exactly once, which takes care of the case when
973
+ # +row_index+ is greater than the current max index by exactly 1.
974
+ row_index.downto(@sheet_data.size) { |r| @sheet_data[r] = Array.new(col_size) }
975
+ end
976
+
977
+ # Helper method to get the style index for a row
978
+ def get_row_style(row)
979
+ if @row_styles[(row+1)].nil?
980
+ @row_styles[(row+1)] = {}
981
+ @row_styles[(row+1)][:style] = 0
982
+ @workbook.fonts[0].count += 1
983
+ end
984
+ return @row_styles[(row+1)][:style]
985
+ end
986
+
987
+ # Helper method to get the style index for a column
988
+ def get_col_style(col)
989
+ range = RubyXL::ColumnRange.find(col, @column_ranges)
990
+ (range && range.style_index) || 0
991
+ end
992
+
993
+ def get_col_xf(col)
994
+ @workbook.cell_xfs[get_col_style(col)]
995
+ end
996
+
997
+ def change_row_alignment(row,alignment,is_horizontal)
998
+ validate_workbook
999
+ validate_nonnegative(row)
1000
+ ensure_cell_exists(row)
1001
+
1002
+ if @row_styles[(row+1)].nil?
1003
+ @row_styles[(row+1)] = {}
1004
+ @row_styles[(row+1)][:style] = 0
1005
+ end
1006
+
1007
+ @row_styles[(row+1)][:style] =
1008
+ modify_alignment(@workbook,@row_styles[(row+1)][:style],is_horizontal,alignment)
1009
+
1010
+ @sheet_data[row].each do |c|
1011
+ unless c.nil?
1012
+ if is_horizontal
1013
+ c.change_horizontal_alignment(alignment)
1014
+ else
1015
+ c.change_vertical_alignment(alignment)
1016
+ end
1017
+ end
1018
+ end
1019
+ end
1020
+
1021
+ def change_column_alignment(col,alignment,is_horizontal)
1022
+ validate_workbook
1023
+ validate_nonnegative(col)
1024
+ ensure_cell_exists(0, col)
1025
+
1026
+ new_style_index = modify_alignment(@workbook, get_column_style_index(col), is_horizontal, alignment)
1027
+ RubyXL::ColumnRange.update(col, @column_ranges, { 'style' => new_style_index })
1028
+
1029
+ @sheet_data.each { |row|
1030
+ c = row[col]
1031
+ next if c.nil?
1032
+ if is_horizontal
1033
+ c.change_horizontal_alignment(alignment)
1034
+ else
1035
+ c.change_vertical_alignment(alignment)
1036
+ end
1037
+ }
1038
+ end
1039
+
1040
+ def change_row_border(row, direction, weight)
1041
+ validate_workbook
1042
+ validate_nonnegative(row)
1043
+ validate_border(weight)
1044
+ ensure_cell_exists(row)
1045
+
1046
+ xf = get_row_xf(row)
1047
+ border = @workbook.borders[xf.border_id].dup
1048
+ border.set_edge_style(direction, weight)
1049
+
1050
+ xf = workbook.register_new_border(border, xf)
1051
+ @row_styles[(row+1)][:style] = workbook.register_new_xf(xf, @row_styles[(row+1)][:style])
1052
+
1053
+ @sheet_data[row].each { |c|
1054
+ next if c.nil?
1055
+ case direction
1056
+ when :top then c.change_border_top(weight)
1057
+ when :left then c.change_border_left(weight)
1058
+ when :right then c.change_border_right(weight)
1059
+ when :bottom then c.change_border_bottom(weight)
1060
+ when :diagonal then c.change_border_diagonal(weight)
1061
+ else raise 'invalid direction'
1062
+ end
1063
+ }
1064
+ end
1065
+
1066
+ def change_column_border(col, direction, weight)
1067
+ col = Integer(col)
1068
+ validate_workbook
1069
+ validate_nonnegative(col)
1070
+ validate_border(weight)
1071
+ ensure_cell_exists(0, col)
1072
+
1073
+ xf = get_col_xf(col)
1074
+ border = @workbook.borders[xf.border_id].dup
1075
+ border.set_edge_style(direction, weight)
1076
+
1077
+ xf = workbook.register_new_border(border, get_col_xf(col))
1078
+ new_style_index = workbook.register_new_xf(xf, get_col_style(col))
1079
+ RubyXL::ColumnRange.update(col, @column_ranges, { 'style' => new_style_index })
1080
+
1081
+ @sheet_data.each { |row|
1082
+ c = row[col]
1083
+ next if c.nil?
1084
+ case direction
1085
+ when :top then c.change_border_top(weight)
1086
+ when :left then c.change_border_left(weight)
1087
+ when :right then c.change_border_right(weight)
1088
+ when :bottom then c.change_border_bottom(weight)
1089
+ when :diagonal then c.change_border_diagonal(weight)
1090
+ else raise 'invalid direction'
1091
+ end
1092
+ }
1093
+ end
1094
+
1095
+ def add_cell_style(row,column)
1096
+ xf = @workbook.cell_xfs[@sheet_data[row][column].style_index]
1097
+ @workbook.fonts[xf.font_id].count += 1
1098
+ @workbook.fills[xf.fill_id].count += 1
1099
+ @workbook.borders[xf.border_id].count += 1
1100
+ end
1101
+
1102
+ # finds first row which contains at least all strings in cells_content
1103
+ def find_first_row_with_content(cells_content)
1104
+ validate_workbook
1105
+ index = nil
1106
+
1107
+ @sheet_data.each_with_index do |row, index|
1108
+ cells_content = cells_content.map { |header| header.to_s.downcase.strip }
1109
+ original_cells_content = row.map { |cell| cell.nil? ? '' : cell.value.to_s.downcase.strip }
1110
+ if (cells_content & original_cells_content).size == cells_content.size
1111
+ return index
1112
+ end
1113
+ end
1114
+ return nil
1115
+ end
1116
+
1117
+ end #end class
1118
+ end