rubyXL-git-ref-6002046 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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