roo-immersion 1.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 (97) hide show
  1. data/History.txt +225 -0
  2. data/README.markdown +60 -0
  3. data/examples/roo_soap_client.rb +53 -0
  4. data/examples/roo_soap_server.rb +29 -0
  5. data/examples/write_me.rb +33 -0
  6. data/lib/roo.rb +32 -0
  7. data/lib/roo/excel.rb +468 -0
  8. data/lib/roo/excel2003xml.rb +394 -0
  9. data/lib/roo/excelx.rb +601 -0
  10. data/lib/roo/generic_spreadsheet.rb +628 -0
  11. data/lib/roo/google.rb +379 -0
  12. data/lib/roo/openoffice.rb +451 -0
  13. data/lib/roo/roo_rails_helper.rb +82 -0
  14. data/lib/roo/version.rb +9 -0
  15. data/test/1900_base.xls +0 -0
  16. data/test/1904_base.xls +0 -0
  17. data/test/Bibelbund.csv +3741 -0
  18. data/test/Bibelbund.ods +0 -0
  19. data/test/Bibelbund.xls +0 -0
  20. data/test/Bibelbund.xlsx +0 -0
  21. data/test/Bibelbund.xml +62518 -0
  22. data/test/Bibelbund1.ods +0 -0
  23. data/test/bad_excel_date.xls +0 -0
  24. data/test/bbu.ods +0 -0
  25. data/test/bbu.xls +0 -0
  26. data/test/bbu.xlsx +0 -0
  27. data/test/bbu.xml +152 -0
  28. data/test/bode-v1.ods.zip +0 -0
  29. data/test/bode-v1.xls.zip +0 -0
  30. data/test/boolean.ods +0 -0
  31. data/test/boolean.xls +0 -0
  32. data/test/boolean.xlsx +0 -0
  33. data/test/boolean.xml +112 -0
  34. data/test/borders.ods +0 -0
  35. data/test/borders.xls +0 -0
  36. data/test/borders.xlsx +0 -0
  37. data/test/borders.xml +144 -0
  38. data/test/bug-row-column-fixnum-float.xls +0 -0
  39. data/test/bug-row-column-fixnum-float.xml +127 -0
  40. data/test/datetime.ods +0 -0
  41. data/test/datetime.xls +0 -0
  42. data/test/datetime.xlsx +0 -0
  43. data/test/datetime.xml +142 -0
  44. data/test/datetime_floatconv.xls +0 -0
  45. data/test/datetime_floatconv.xml +148 -0
  46. data/test/emptysheets.ods +0 -0
  47. data/test/emptysheets.xls +0 -0
  48. data/test/emptysheets.xml +105 -0
  49. data/test/excel2003.xml +21140 -0
  50. data/test/false_encoding.xls +0 -0
  51. data/test/false_encoding.xml +132 -0
  52. data/test/formula.ods +0 -0
  53. data/test/formula.xls +0 -0
  54. data/test/formula.xlsx +0 -0
  55. data/test/formula.xml +134 -0
  56. data/test/formula_parse_error.xls +0 -0
  57. data/test/formula_parse_error.xml +1833 -0
  58. data/test/html-escape.ods +0 -0
  59. data/test/no_spreadsheet_file.txt +1 -0
  60. data/test/numbers1.csv +18 -0
  61. data/test/numbers1.ods +0 -0
  62. data/test/numbers1.xls +0 -0
  63. data/test/numbers1.xlsx +0 -0
  64. data/test/numbers1.xml +312 -0
  65. data/test/only_one_sheet.ods +0 -0
  66. data/test/only_one_sheet.xls +0 -0
  67. data/test/only_one_sheet.xlsx +0 -0
  68. data/test/only_one_sheet.xml +67 -0
  69. data/test/paragraph.ods +0 -0
  70. data/test/paragraph.xls +0 -0
  71. data/test/paragraph.xlsx +0 -0
  72. data/test/paragraph.xml +127 -0
  73. data/test/ric.ods +0 -0
  74. data/test/simple_spreadsheet.ods +0 -0
  75. data/test/simple_spreadsheet.xls +0 -0
  76. data/test/simple_spreadsheet.xlsx +0 -0
  77. data/test/simple_spreadsheet.xml +225 -0
  78. data/test/simple_spreadsheet_from_italo.ods +0 -0
  79. data/test/simple_spreadsheet_from_italo.xls +0 -0
  80. data/test/simple_spreadsheet_from_italo.xml +242 -0
  81. data/test/skipped_tests.rb +789 -0
  82. data/test/style.ods +0 -0
  83. data/test/style.xls +0 -0
  84. data/test/style.xlsx +0 -0
  85. data/test/style.xml +154 -0
  86. data/test/test_helper.rb +19 -0
  87. data/test/test_roo.rb +1834 -0
  88. data/test/time-test.csv +2 -0
  89. data/test/time-test.ods +0 -0
  90. data/test/time-test.xls +0 -0
  91. data/test/time-test.xlsx +0 -0
  92. data/test/time-test.xml +131 -0
  93. data/test/whitespace.ods +0 -0
  94. data/test/whitespace.xls +0 -0
  95. data/test/whitespace.xlsx +0 -0
  96. data/test/whitespace.xml +184 -0
  97. metadata +231 -0
@@ -0,0 +1,628 @@
1
+ require 'rubygems'
2
+ require 'builder'
3
+
4
+ # Base class for all other types of spreadsheets
5
+ class GenericSpreadsheet
6
+
7
+ attr_reader :default_sheet
8
+
9
+ # sets the line with attribute names (default: 1)
10
+ attr_accessor :header_line
11
+
12
+ def initialize
13
+ end
14
+
15
+ # set the working sheet in the document
16
+ def default_sheet=(sheet)
17
+ if sheet.kind_of? Fixnum
18
+ if sheet >= 0 and sheet <= sheets.length
19
+ sheet = self.sheets[sheet-1]
20
+ else
21
+ raise RangeError
22
+ end
23
+ elsif sheet.kind_of?(String)
24
+ raise RangeError if ! self.sheets.include?(sheet)
25
+ else
26
+ raise TypeError, "what are you trying to set as default sheet?"
27
+ end
28
+ @default_sheet = sheet
29
+ check_default_sheet
30
+ @first_row[sheet] = @last_row[sheet] = @first_column[sheet] = @last_column[sheet] = nil
31
+ @cells_read[sheet] = false
32
+ end
33
+
34
+ # first non-empty column as a letter
35
+ def first_column_as_letter(sheet=nil)
36
+ GenericSpreadsheet.number_to_letter(first_column(sheet))
37
+ end
38
+
39
+ # last non-empty column as a letter
40
+ def last_column_as_letter(sheet=nil)
41
+ GenericSpreadsheet.number_to_letter(last_column(sheet))
42
+ end
43
+
44
+ # returns the number of the first non-empty row
45
+ def first_row(sheet=nil)
46
+ if sheet == nil
47
+ sheet = @default_sheet
48
+ end
49
+ read_cells(sheet) unless @cells_read[sheet]
50
+ if @first_row[sheet]
51
+ return @first_row[sheet]
52
+ end
53
+ impossible_value = 999_999 # more than a spreadsheet can hold
54
+ result = impossible_value
55
+ @cell[sheet].each_pair {|key,value|
56
+ y,x = key # _to_string(key).split(',')
57
+ y = y.to_i
58
+ result = [result, y].min if value
59
+ } if @cell[sheet]
60
+ result = nil if result == impossible_value
61
+ @first_row[sheet] = result
62
+ result
63
+ end
64
+
65
+ # returns the number of the last non-empty row
66
+ def last_row(sheet=nil)
67
+ sheet = @default_sheet unless sheet
68
+ read_cells(sheet) unless @cells_read[sheet]
69
+ if @last_row[sheet]
70
+ return @last_row[sheet]
71
+ end
72
+ impossible_value = 0
73
+ result = impossible_value
74
+ @cell[sheet].each_pair {|key,value|
75
+ y,x = key # _to_string(key).split(',')
76
+ y = y.to_i
77
+ result = [result, y].max if value
78
+ } if @cell[sheet]
79
+ result = nil if result == impossible_value
80
+ @last_row[sheet] = result
81
+ result
82
+ end
83
+
84
+ # returns the number of the first non-empty column
85
+ def first_column(sheet=nil)
86
+ if sheet == nil
87
+ sheet = @default_sheet
88
+ end
89
+ read_cells(sheet) unless @cells_read[sheet]
90
+ if @first_column[sheet]
91
+ return @first_column[sheet]
92
+ end
93
+ impossible_value = 999_999 # more than a spreadsheet can hold
94
+ result = impossible_value
95
+ @cell[sheet].each_pair {|key,value|
96
+ y,x = key # _to_string(key).split(',')
97
+ x = x # .to_i
98
+ result = [result, x].min if value
99
+ } if @cell[sheet]
100
+ result = nil if result == impossible_value
101
+ @first_column[sheet] = result
102
+ result
103
+ end
104
+
105
+ # returns the number of the last non-empty column
106
+ def last_column(sheet=nil)
107
+ sheet = @default_sheet unless sheet
108
+ read_cells(sheet) unless @cells_read[sheet]
109
+ if @last_column[sheet]
110
+ return @last_column[sheet]
111
+ end
112
+ impossible_value = 0
113
+ result = impossible_value
114
+ @cell[sheet].each_pair {|key,value|
115
+ y,x = key # _to_string(key).split(',')
116
+ x = x.to_i
117
+ result = [result, x].max if value
118
+ } if @cell[sheet]
119
+ result = nil if result == impossible_value
120
+ @last_column[sheet] = result
121
+ result
122
+ end
123
+
124
+ # returns a rectangular area (default: all cells) as yaml-output
125
+ # you can add additional attributes with the prefix parameter like:
126
+ # oo.to_yaml({"file"=>"flightdata_2007-06-26", "sheet" => "1"})
127
+ def to_yaml(prefix={}, from_row=nil, from_column=nil, to_row=nil, to_column=nil,sheet=nil)
128
+ sheet = @default_sheet unless sheet
129
+ result = "--- \n"
130
+ (from_row||first_row(sheet)).upto(to_row||last_row(sheet)) do |row|
131
+ (from_column||first_column(sheet)).upto(to_column||last_column(sheet)) do |col|
132
+ unless empty?(row,col,sheet)
133
+ result << "cell_#{row}_#{col}: \n"
134
+ prefix.each {|k,v|
135
+ result << " #{k}: #{v} \n"
136
+ }
137
+ result << " row: #{row} \n"
138
+ result << " col: #{col} \n"
139
+ result << " celltype: #{self.celltype(row,col,sheet)} \n"
140
+ if self.celltype(row,col,sheet) == :time
141
+ result << " value: #{GenericSpreadsheet.integer_to_timestring( self.cell(row,col,sheet))} \n"
142
+ else
143
+ result << " value: #{self.cell(row,col,sheet)} \n"
144
+ end
145
+ end
146
+ end
147
+ end
148
+ result
149
+ end
150
+
151
+ # write the current spreadsheet to stdout or into a file
152
+ def to_csv(filename=nil,sheet=nil)
153
+ sheet = @default_sheet unless sheet
154
+ if filename
155
+ file = File.open(filename,"w") # do |file|
156
+ write_csv_content(file,sheet)
157
+ file.close
158
+ else
159
+ write_csv_content(STDOUT,sheet)
160
+ end
161
+ true
162
+ end
163
+
164
+ # find a row either by row number or a condition
165
+ # Caution: this works only within the default sheet -> set default_sheet before you call this method
166
+ # (experimental. see examples in the test_roo.rb file)
167
+ def find(*args) # :nodoc
168
+ result_array = false
169
+ args.each {|arg,val|
170
+ if arg.class == Hash
171
+ arg.each { |hkey,hval|
172
+ if hkey == :array and hval == true
173
+ result_array = true
174
+ end
175
+ }
176
+ end
177
+ }
178
+ column_with = {}
179
+ 1.upto(last_column) do |col|
180
+ column_with[cell(@header_line,col)] = col
181
+ end
182
+ result = Array.new
183
+ #-- id
184
+ if args[0].class == Fixnum
185
+ rownum = args[0]
186
+ if @header_line
187
+ tmp = {}
188
+ else
189
+ tmp = []
190
+ end
191
+ 1.upto(self.row(rownum).size) {|j|
192
+ x = ''
193
+ column_with.each { |key,val|
194
+ if val == j
195
+ x = key
196
+ end
197
+ }
198
+ if @header_line
199
+ tmp[x] = cell(rownum,j)
200
+ else
201
+ tmp[j-1] = cell(rownum,j)
202
+ end
203
+
204
+ }
205
+ if @header_line
206
+ result = [ tmp ]
207
+ else
208
+ result = tmp
209
+ end
210
+ #-- :all
211
+ elsif args[0] == :all
212
+ if args[1].class == Hash
213
+ args[1].each {|key,val|
214
+ if key == :conditions
215
+ column_with = {}
216
+ 1.upto(last_column) do |col|
217
+ column_with[cell(@header_line,col)] = col
218
+ end
219
+ conditions = val
220
+ first_row.upto(last_row) do |i|
221
+ # are all conditions met?
222
+ found = 1
223
+ conditions.each { |key,val|
224
+ if cell(i,column_with[key]) == val
225
+ found *= 1
226
+ else
227
+ found *= 0
228
+ end
229
+ }
230
+ if found > 0
231
+ tmp = {}
232
+ 1.upto(self.row(i).size) {|j|
233
+ x = ''
234
+ column_with.each { |key,val|
235
+ if val == j
236
+ x = key
237
+ end
238
+ }
239
+ tmp[x] = cell(i,j)
240
+ }
241
+ if result_array
242
+ result << self.row(i)
243
+ else
244
+ result << tmp
245
+ end
246
+ end
247
+ end
248
+ end # :conditions
249
+ }
250
+ end
251
+ end
252
+ result
253
+ end
254
+
255
+ # returns all values in this row as an array
256
+ # row numbers are 1,2,3,... like in the spreadsheet
257
+ def row(rownumber,sheet=nil)
258
+ sheet = @default_sheet unless sheet
259
+ read_cells(sheet) unless @cells_read[sheet]
260
+ result = []
261
+ first_column(sheet).upto(last_column(sheet)) do |col|
262
+ result << cell(rownumber,col,sheet)
263
+ end
264
+ result
265
+ end
266
+
267
+ # returns all values in this column as an array
268
+ # column numbers are 1,2,3,... like in the spreadsheet
269
+ def column(columnnumber,sheet=nil)
270
+ if columnnumber.class == String
271
+ columnnumber = Excel.letter_to_number(columnnumber)
272
+ end
273
+ sheet = @default_sheet unless sheet
274
+ read_cells(sheet) unless @cells_read[sheet]
275
+ result = []
276
+ first_row(sheet).upto(last_row(sheet)) do |row|
277
+ result << cell(row,columnnumber,sheet)
278
+ end
279
+ result
280
+ end
281
+
282
+ # reopens and read a spreadsheet document
283
+ def reload
284
+ ds = @default_sheet
285
+ initialize(@filename) if self.class == Openoffice or
286
+ self.class == Excel
287
+ initialize(@spreadsheetkey,@user,@password) if self.class == Google
288
+ self.default_sheet = ds
289
+ #@first_row = @last_row = @first_column = @last_column = nil
290
+ end
291
+
292
+ # true if cell is empty
293
+ def empty?(row, col, sheet=nil)
294
+ sheet = @default_sheet unless sheet
295
+ read_cells(sheet) unless @cells_read[sheet] or self.class == Excel
296
+ row,col = normalize(row,col)
297
+ return true unless cell(row, col, sheet)
298
+ return true if celltype(row, col, sheet) == :string && cell(row, col, sheet).empty?
299
+ return true if row < first_row(sheet) || row > last_row(sheet) || col < first_column(sheet) || col > last_column(sheet)
300
+ false
301
+ end
302
+
303
+ # recursively removes the current temporary directory
304
+ # this is only needed if you work with zipped files or files via the web
305
+ def remove_tmp
306
+ if File.exists?(@tmpdir)
307
+ FileUtils::rm_r(@tmpdir)
308
+ end
309
+ end
310
+
311
+ # Returns information of the spreadsheet document and all sheets within
312
+ # this document.
313
+ def info
314
+ result = "File: #{File.basename(@filename)}\n"+
315
+ "Number of sheets: #{sheets.size}\n"+
316
+ "Sheets: #{sheets.map{|sheet| sheet+", "}.to_s[0..-3]}\n"
317
+ n = 1
318
+ sheets.each {|sheet|
319
+ self.default_sheet = sheet
320
+ result << "Sheet " + n.to_s + ":\n"
321
+ unless first_row
322
+ result << " - empty -"
323
+ else
324
+ result << " First row: #{first_row}\n"
325
+ result << " Last row: #{last_row}\n"
326
+ result << " First column: #{GenericSpreadsheet.number_to_letter(first_column)}\n"
327
+ result << " Last column: #{GenericSpreadsheet.number_to_letter(last_column)}"
328
+ end
329
+ result << "\n" if sheet != sheets.last
330
+ n += 1
331
+ }
332
+ result
333
+ end
334
+
335
+ def to_xml
336
+ xml_document = ''
337
+ xml = Builder::XmlMarkup.new(:target => xml_document, :indent => 2)
338
+ xml.instruct! :xml, :version =>"1.0", :encoding => "utf-8"
339
+ xml.spreadsheet {
340
+ self.sheets.each do |sheet|
341
+ self.default_sheet = sheet
342
+ xml.sheet(:name => sheet) { |x|
343
+ if first_row and last_row and first_column and last_column
344
+ # sonst gibt es Fehler bei leeren Blaettern
345
+ first_row.upto(last_row) do |row|
346
+ first_column.upto(last_column) do |col|
347
+ unless empty?(row,col)
348
+ x.cell(cell(row,col),
349
+ :row =>row,
350
+ :column => col,
351
+ :type => celltype(row,col))
352
+ end
353
+ end
354
+ end
355
+ end
356
+ }
357
+ end
358
+ }
359
+ xml_document
360
+ end
361
+
362
+ protected
363
+
364
+ def file_type_check(filename, ext, name)
365
+ new_expression = {
366
+ '.ods' => 'Openoffice.new',
367
+ '.xls' => 'Excel.new',
368
+ '.xlsx' => 'Excelx.new',
369
+ '.xml' => 'Excel2003.new'
370
+ }
371
+ case ext
372
+ when '.ods', '.xls', '.xlsx', '.xml'
373
+ correct_class = "use #{new_expression[ext]} to handle #{ext} spreadsheet files"
374
+ else
375
+ raise "unknown file type: #{ext}"
376
+ end
377
+ if File.extname(filename).downcase != ext
378
+ case @file_warning
379
+ when :error
380
+ warn correct_class
381
+ raise TypeError, "#{filename} is not #{name} file"
382
+ when :warning
383
+ warn "are you sure, this is #{name} spreadsheet file?"
384
+ warn correct_class
385
+ when :ignore
386
+ # ignore
387
+ else
388
+ raise "#{@file_warning} illegal state of file_warning"
389
+ end
390
+ end
391
+ end
392
+
393
+ # konvertiert einen Key in der Form "12,45" (=row,column) in
394
+ # ein Array mit numerischen Werten ([12,45])
395
+ # Diese Methode ist eine temp. Loesung, um zu erforschen, ob der
396
+ # Zugriff mit numerischen Keys schneller ist.
397
+ def key_to_num(str)
398
+ r,c = str.split(',')
399
+ r = r.to_i
400
+ c = c.to_i
401
+ [r,c]
402
+ end
403
+
404
+ # siehe: key_to_num
405
+ def key_to_string(arr)
406
+ "#{arr[0]},#{arr[1]}"
407
+ end
408
+
409
+ private
410
+
411
+ # converts cell coordinate to numeric values of row,col
412
+ def normalize(row,col)
413
+ if row.class == String
414
+ if col.class == Fixnum
415
+ # ('A',1):
416
+ # ('B', 5) -> (5, 2)
417
+ row, col = col, row
418
+ else
419
+ raise ArgumentError
420
+ end
421
+ end
422
+ if col.class == String
423
+ col = GenericSpreadsheet.letter_to_number(col)
424
+ end
425
+ return row,col
426
+ end
427
+
428
+ # def open_from_uri(uri)
429
+ # require 'open-uri' ;
430
+ # tempfilename = File.join(@tmpdir, File.basename(uri))
431
+ # f = File.open(tempfilename,"wb")
432
+ # begin
433
+ # open(uri) do |net|
434
+ # f.write(net.read)
435
+ # end
436
+ # rescue
437
+ # raise "could not open #{uri}"
438
+ # end
439
+ # f.close
440
+ # File.join(@tmpdir, File.basename(uri))
441
+ # end
442
+
443
+ # OpenURI::HTTPError
444
+ # def open_from_uri(uri)
445
+ # require 'open-uri'
446
+ # #existiert URL?
447
+ # r = Net::HTTP.get_response(URI.parse(uri))
448
+ # raise "URL nicht verfuegbar" unless r.is_a? Net::HTTPOK
449
+ # tempfilename = File.join(@tmpdir, File.basename(uri))
450
+ # f = File.open(tempfilename,"wb")
451
+ # open(uri) do |net|
452
+ # f.write(net.read)
453
+ # end
454
+ # # rescue
455
+ # # raise "could not open #{uri}"
456
+ # # end
457
+ # f.close
458
+ # File.join(@tmpdir, File.basename(uri))
459
+ # end
460
+
461
+ def open_from_uri(uri)
462
+ require 'open-uri'
463
+ response = ''
464
+ begin
465
+ open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") { |net|
466
+ response = net.read
467
+ tempfilename = File.join(@tmpdir, File.basename(uri))
468
+ f = File.open(tempfilename,"wb")
469
+ f.write(response)
470
+ f.close
471
+ }
472
+ rescue OpenURI::HTTPError
473
+ raise "could not open #{uri}"
474
+ end
475
+ File.join(@tmpdir, File.basename(uri))
476
+ end
477
+
478
+ def open_from_stream(stream)
479
+ tempfilename = File.join(@tmpdir, "spreadsheet")
480
+ f = File.open(tempfilename,"wb")
481
+ f.write(stream[7..-1])
482
+ f.close
483
+ File.join(@tmpdir, "spreadsheet")
484
+ end
485
+
486
+ # convert a number to something like 'AB' (1 => 'A', 2 => 'B', ...)
487
+ def self.number_to_letter(n)
488
+ letters=""
489
+ while n > 0
490
+ num = n%26
491
+ letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[num-1,1] + letters
492
+ n = n.div(26)
493
+ end
494
+ letters
495
+ end
496
+
497
+ # convert letters like 'AB' to a number ('A' => 1, 'B' => 2, ...)
498
+ def self.letter_to_number(letters)
499
+ result = 0
500
+ while letters && letters.length > 0
501
+ character = letters[0,1].upcase
502
+ num = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".index(character)
503
+ raise ArgumentError, "invalid column character '#{letters[0,1]}'" if num == nil
504
+ num += 1
505
+ result = result * 26 + num
506
+ letters = letters[1..-1]
507
+ end
508
+ result
509
+ end
510
+
511
+ def unzip(filename)
512
+ ret = nil
513
+ Zip::ZipFile.open(filename) do |zip|
514
+ ret = process_zipfile_packed zip
515
+ end
516
+ ret
517
+ end
518
+
519
+ # check if default_sheet was set and exists in sheets-array
520
+ def check_default_sheet
521
+ sheet_found = false
522
+ raise ArgumentError, "Error: default_sheet not set" if @default_sheet == nil
523
+ if sheets.index(@default_sheet)
524
+ sheet_found = true
525
+ end
526
+ if ! sheet_found
527
+ raise RangeError, "sheet '#{@default_sheet}' not found"
528
+ end
529
+ #raise ArgumentError, "Error: default_sheet not set" if @default_sheet == nil
530
+ end
531
+
532
+ def process_zipfile_packed(zip, path='')
533
+ ret=nil
534
+ if zip.file.file? path
535
+ # extract and return filename
536
+ @tmpdir = "oo_"+$$.to_s
537
+ unless File.exists?(@tmpdir)
538
+ FileUtils::mkdir(@tmpdir)
539
+ end
540
+ file = File.open(File.join(@tmpdir, path),"wb")
541
+ file.write(zip.read(path))
542
+ file.close
543
+ return File.join(@tmpdir, path)
544
+ else
545
+ unless path.empty?
546
+ path += '/'
547
+ end
548
+ zip.dir.foreach(path) do |filename|
549
+ ret = process_zipfile_packed(zip, path + filename)
550
+ end
551
+ end
552
+ ret
553
+ end
554
+
555
+ def write_csv_content(file=nil,sheet=nil)
556
+ file = STDOUT unless file
557
+ if first_row(sheet) # sheet is not empty
558
+ # first_row(sheet).upto(last_row(sheet)) do |row|
559
+ 1.upto(last_row(sheet)) do |row|
560
+ 1.upto(last_column(sheet)) do |col|
561
+ file.print(",") if col > 1
562
+ onecell = cell(row,col,sheet)
563
+ onecelltype = celltype(row,col,sheet)
564
+ file.print one_cell_output(onecelltype,onecell,empty?(row,col,sheet))
565
+ end
566
+ file.print("\n")
567
+ end # sheet not empty
568
+ end
569
+ end
570
+
571
+ def one_cell_output(onecelltype,onecell,empty)
572
+ str = ""
573
+ if empty
574
+ str += ''
575
+ else
576
+ case onecelltype
577
+ when :string
578
+ if onecell == ""
579
+ str << ''
580
+ else
581
+ onecell.gsub!(/"/,'""')
582
+ str << ('"'+onecell+'"')
583
+ end
584
+ when :float,:percentage
585
+ if onecell == onecell.to_i
586
+ str << onecell.to_i.to_s
587
+ else
588
+ str << onecell.to_s
589
+ end
590
+ when :formula
591
+ if onecell.class == String
592
+ if onecell == ""
593
+ str << ''
594
+ else
595
+ onecell.gsub!(/"/,'""')
596
+ str << '"'+onecell+'"'
597
+ end
598
+ elsif onecell.class == Float
599
+ if onecell == onecell.to_i
600
+ str << onecell.to_i.to_s
601
+ else
602
+ str << onecell.to_s
603
+ end
604
+ else
605
+ raise "unhandled onecell-class "+onecell.class.to_s
606
+ end
607
+ when :date
608
+ str << onecell.to_s
609
+ when :time
610
+ str << GenericSpreadsheet.integer_to_timestring(onecell)
611
+ else
612
+ raise "unhandled celltype "+onecelltype.to_s
613
+ end
614
+ end
615
+ str
616
+ end
617
+
618
+ # converts an integer value to a time string like '02:05:06'
619
+ def self.integer_to_timestring(content)
620
+ return content if String === content
621
+ h = (content/3600.0).floor
622
+ content = content - h*3600
623
+ m = (content/60.0).floor
624
+ content = content - m*60
625
+ s = content
626
+ sprintf("%02d:%02d:%02d",h,m,s)
627
+ end
628
+ end