hmcgowan-roo 1.2.4

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 (62) hide show
  1. data/History.txt +225 -0
  2. data/README.txt +43 -0
  3. data/lib/roo/excel.rb +455 -0
  4. data/lib/roo/excelx.rb +654 -0
  5. data/lib/roo/generic_spreadsheet.rb +636 -0
  6. data/lib/roo/google.rb +411 -0
  7. data/lib/roo/openoffice.rb +508 -0
  8. data/lib/roo/roo_rails_helper.rb +81 -0
  9. data/lib/roo/version.rb +9 -0
  10. data/lib/roo.rb +11 -0
  11. data/test/Bibelbund.csv +3741 -0
  12. data/test/Bibelbund.ods +0 -0
  13. data/test/Bibelbund.xls +0 -0
  14. data/test/Bibelbund.xlsx +0 -0
  15. data/test/Bibelbund1.ods +0 -0
  16. data/test/bbu.ods +0 -0
  17. data/test/bbu.xls +0 -0
  18. data/test/bbu.xlsx +0 -0
  19. data/test/bode-v1.ods.zip +0 -0
  20. data/test/bode-v1.xls.zip +0 -0
  21. data/test/boolean.ods +0 -0
  22. data/test/boolean.xls +0 -0
  23. data/test/boolean.xlsx +0 -0
  24. data/test/borders.ods +0 -0
  25. data/test/borders.xls +0 -0
  26. data/test/borders.xlsx +0 -0
  27. data/test/bug-row-column-fixnum-float.xls +0 -0
  28. data/test/datetime.ods +0 -0
  29. data/test/datetime.xls +0 -0
  30. data/test/datetime.xlsx +0 -0
  31. data/test/emptysheets.ods +0 -0
  32. data/test/emptysheets.xls +0 -0
  33. data/test/false_encoding.xls +0 -0
  34. data/test/formula.ods +0 -0
  35. data/test/formula.xls +0 -0
  36. data/test/formula.xlsx +0 -0
  37. data/test/html-escape.ods +0 -0
  38. data/test/no_spreadsheet_file.txt +1 -0
  39. data/test/numbers1.csv +18 -0
  40. data/test/numbers1.ods +0 -0
  41. data/test/numbers1.xls +0 -0
  42. data/test/numbers1.xlsx +0 -0
  43. data/test/numbers1_excel.csv +18 -0
  44. data/test/only_one_sheet.ods +0 -0
  45. data/test/only_one_sheet.xls +0 -0
  46. data/test/only_one_sheet.xlsx +0 -0
  47. data/test/ric.ods +0 -0
  48. data/test/simple_spreadsheet.ods +0 -0
  49. data/test/simple_spreadsheet.xls +0 -0
  50. data/test/simple_spreadsheet.xlsx +0 -0
  51. data/test/simple_spreadsheet_from_italo.ods +0 -0
  52. data/test/simple_spreadsheet_from_italo.xls +0 -0
  53. data/test/style.ods +0 -0
  54. data/test/style.xls +0 -0
  55. data/test/style.xlsx +0 -0
  56. data/test/test_helper.rb +19 -0
  57. data/test/test_roo.rb +4946 -0
  58. data/test/time-test.csv +2 -0
  59. data/test/time-test.ods +0 -0
  60. data/test/time-test.xls +0 -0
  61. data/test/time-test.xlsx +0 -0
  62. metadata +225 -0
data/History.txt ADDED
@@ -0,0 +1,225 @@
1
+ == 1.2.3 2009-01-04
2
+
3
+ * bugfix
4
+ * fixed encoding in #cell at exported Google-spreadsheets (.xls)
5
+
6
+ == 1.2.2 2008-12-14
7
+
8
+ * 2 enhancements
9
+ * added celltype :datetime in Excelx
10
+ * added celltype :datetime in Google
11
+
12
+ == 1.2.1 2008-11-13
13
+
14
+ * 1 enhancement
15
+ * added celltype :datetime in Openoffice and Excel
16
+
17
+ == 1.2.0 2008-08-24
18
+ * 3 major enhancements
19
+ * Excelx: improved the detection of cell type and conversion into roo types
20
+ * All: to_csv: changed boundaries from first_row,1..last_row,last_column to 1,1..last_row,last_column
21
+ * All: Environment variable "ROO_TMP" indicate where temporary directories will be created (if not set the default is the current working directory)
22
+ * 2 bugfixes
23
+ * Excel: improved the detection of last_row/last_column (parseexcel-gem bug?)
24
+ * Excel/Excelx/Openoffice: temporary directories were not removed at opening a file of the wrong type
25
+ == 1.1.0 2008-07-26
26
+ * 2 major enhancements
27
+ * Excel: speed improvements
28
+ * Changed the behavior of reading files with the wrong type
29
+ * 3 bugfixes
30
+ * Google: added normalize in set_value method
31
+ * Excel: last_row in Excel class did not work properly under some circumstances
32
+ * all: fixed a bug in #to_xml if there is an empty sheet
33
+ == 1.0.2 2008-07-04
34
+ * 2 bugfixes
35
+ * Excelx: fixed a bug when there are .xml.rels files in the XLSX archive
36
+ * Excelx: fixed a bug with celltype recognition (see comment with "2008-07-03")
37
+ == 1.0.1 2008-06-30
38
+ * 1 bugfix
39
+ * Excel: row/column method Fixnum/Float confusion
40
+ == 1.0.0 2008-05-28
41
+ * 2 major enhancements
42
+ * support of Excel's new .xlsx file format
43
+ * method #to_xml for exporting a spreadsheet to an xml representation
44
+ * 1 bugfix
45
+ * fixed a bug with excel-spreadsheet character conversion under Macintosh Darwin
46
+ == 0.9.4 2008-04-22
47
+ * 1 bugfix
48
+ * fixed a bug with excel-spreadsheet character conversion under Solaris
49
+ == 0.9.3 2008-03-25
50
+ * 1 bugfix
51
+ * no more tmp directories if an invalid spreadsheet file was openend
52
+ == 0.9.2 2008-03-24
53
+ * 1 enhancement
54
+ * new celltype :time
55
+ * 1 bugfix
56
+ * time values like '23:15' are handled as seconds from midnight
57
+ == 0.9.1 2008-03-23
58
+ * 1 enhancement
59
+ * additional 'sheet' parameter in Google#set_value
60
+ * 1 bugfix
61
+ * fixed a bug within Google#set_value. thanks to davecahill <dpcahill@gmail.com> for the patch.
62
+ == 0.9.0 2008-01-24
63
+ * 1 enhancement:
64
+ * better support of roo spreadsheets in rails views
65
+ == 0.8.5 2008-01-16
66
+ * 1 bugfix
67
+ * fixed a bug within #to_cvs and explicit call of a sheet
68
+ == 0.8.4 2008-01-01
69
+ * 1 bugfix
70
+ * fixed 'find_by_condition' for excel sheets (header_line= --> GenericSpredsheet)
71
+ == 0.8.3 2007-12-31
72
+ * 2 bugfixes
73
+ * another fix for the encoding issue in excel sheet-names
74
+ * reactived the Excel#find method which has been disappeared in the last restructoring, moved to GenericSpreadsheet
75
+ == 0.8.2 2007-12-28
76
+ * 1 enhancement:
77
+ * basename() only in method #info
78
+ * 2 bugfixes
79
+ * changed logging-method to mysql-database in test code with AR, table column 'class' => 'class_name'
80
+ * reactived the Excel#to_csv method which has been disappeared in the last restructoring
81
+ == 0.8.1 2007-12-22
82
+ * 3 bugfixes
83
+ * fixed a bug with first/last-row/column in empty sheet
84
+ * #info prints now '- empty -' if a sheet within a document is empty
85
+ * tried to fix the iconv conversion problem
86
+ == 0.8.0 2007-12-15
87
+ * 2 enhancements:
88
+ * Google online spreadsheets were implemented
89
+ * some methods common to more than one class were factored out to the GenericSpreadsheet (virtual) class
90
+ == 0.7.0 2007-11-23
91
+ * 6 enhancements:
92
+ * Openoffice/Excel: the most methods can be called with an option 'sheet'
93
+ parameter which will be used instead of the default sheet
94
+ * Excel: improved the speed of CVS output
95
+ * Openoffice/Excel: new method #column
96
+ * Openoffice/Excel: new method #find
97
+ * Openoffice/Excel: new method #info
98
+ * better exception if a spreadsheet file does not exist
99
+ == 0.6.1 2007-10-06
100
+ * 2 enhancements:
101
+ * Openoffice: percentage-values are now treated as numbers (not strings)
102
+ * Openoffice: refactoring
103
+ * 1 bugfix
104
+ * Openoffice: repeating date-values in a line are now handled correctly
105
+ == 0.6.0 2007-10-06
106
+ * 1 enhancement:
107
+ * csv-output to stdout or file
108
+ == 0.5.4 2007-08-27
109
+ * 1 bugfix
110
+ * Openoffice: fixed a bug with internal representation of a spreadsheet (thanks to Ric Kamicar for the patch)
111
+ == 0.5.3 2007-08-26
112
+ * 2 enhancements:
113
+ * Openoffice: can now read zip-ed files
114
+ * Openoffice: can now read files from http://-URL over the net
115
+ == 0.5.2 2007-08-26
116
+ * 1 bugfix
117
+ * excel: removed debugging output
118
+ == 0.5.1 2007-08-26
119
+ * 4 enhancements:
120
+ * Openoffice: Exception if an illegal sheet-name is selected
121
+ * Openoffice/Excel: no need to set a default_sheet if there is only one in
122
+ the document
123
+ * Excel: can now read zip-ed files
124
+ * Excel: can now read files from http://-URL over the net
125
+
126
+ == 0.5.0 2007-07-20
127
+ * 3 enhancements:
128
+ * Excel-objects: the methods default_sheet= and sheets can now handle names instead of numbers
129
+ * changed the celltype methods to return symbols, not strings anymore (possible values are :formula, :float, :string, :date, :percentage (if you need strings you can convert it with .to_s)
130
+ * tests can now run on the client machine (not only my machine), if there are not public released files involved these tests are skipped
131
+
132
+ == 0.4.1 2007-06-27
133
+ * 1 bugfix
134
+ * there was ONE false require-statement which led to misleading error messageswhen this gem was used
135
+
136
+ == 0.4.0 2007-06-27
137
+ * 7 enhancements:
138
+ * robustness: Exception if no default_sheet was set
139
+ * new method reload() implemented
140
+ * about 15 % more method documentation
141
+ * optimization: huge increase of speed (no need to use fixed borders anymore)
142
+ * added the method 'formulas' which gives you all formulas in a spreadsheet
143
+ * added the method 'set' which can set cells to a certain value
144
+ * added the method 'to_yaml' which can produce output for importing in a (rails) database
145
+ * 4 bugfixes
146
+ * ..row_as_letter methods were nonsense - removed
147
+ * @cells_read should be reset if the default_sheet is changed
148
+ * error in excel-part: strings are now converted to utf-8 (the parsexcel-gem gave me an error with my test data, which could not converted to .to_s using latin1 encoding)
149
+ * fixed bug when default_sheet is changed
150
+
151
+ == 0.3.0 2007-06-20
152
+ * 1 enhancement:
153
+ * Openoffice: formula support
154
+
155
+ == 0.2.7 2007-06-20
156
+ * 1 bugfix:
157
+ * Excel: float-numbers were truncated to integer
158
+
159
+ == 0.2.6 2007-06-19
160
+ * 1 bugfix:
161
+ * Openoffice: two or more consecutive cells with string content failed
162
+
163
+ == 0.2.5 2007-06-17
164
+
165
+ * 2 enhancements:
166
+ * Excel: row method implemented
167
+ * more tests
168
+ * 1 bugfix:
169
+ * Openoffice: row method fixed
170
+
171
+ == 0.2.4 2007-06-16
172
+ * 1 bugfix:
173
+ * ID 11605 Two cols with same value: crash roo (openoffice version only)
174
+
175
+ == 0.2.3 2007-06-02
176
+ * 3 enhancements:
177
+ * more robust call att Excel#default_sheet= when called with a name
178
+ * new method empty?
179
+ * refactoring
180
+ * 1 bugfix:
181
+ * bugfix in Excel#celltype
182
+ * bugfix (running under windows only) in closing the temp file before removing it
183
+
184
+ == 0.2.2 2007-06-01
185
+ * 1 bugfix:
186
+ * correct pathname for running with windows
187
+
188
+
189
+ == 0.2.2 2007-06-01
190
+ * 1 bugfix:
191
+ * incorrect dependencies fixed
192
+
193
+ == 0.2.0 2007-06-01
194
+ * 1 major enhancement:
195
+ * support for MS-Excel Spreadsheets
196
+
197
+ == 0.1.2 2007-05-31
198
+ * 1 major enhancement:
199
+ * cells with more than one character, like 'AA' can now be handled
200
+
201
+ == 0.1.1 2007-05-31
202
+ * 1 Bugfix
203
+ * Bugfix in first/last methods
204
+
205
+ == 0.1.0 2007-05-31
206
+
207
+ * 1 major enhancement:
208
+ * new methods first/last row/column
209
+ * new method officeversion
210
+
211
+ == 0.0.3 2007-05-30
212
+
213
+ * 1 minor enhancement:
214
+ * new method row()
215
+
216
+ == 0.0.2 2007-05-30
217
+
218
+ * 2 major enhancement:
219
+ * fixed some bugs
220
+ * more ways to access a cell
221
+
222
+ == 0.0.1 2007-05-25
223
+
224
+ * 1 major enhancement:
225
+ * Initial release
data/README.txt ADDED
@@ -0,0 +1,43 @@
1
+ README for roo
2
+ ==============
3
+
4
+ Installation:
5
+
6
+ sudo gem install roo
7
+
8
+ Usage:
9
+
10
+ require 'rubygems'
11
+ require 'roo'
12
+
13
+ s = Openoffice.new("myspreadsheet.ods") # creates an Openoffice Spreadsheet instance
14
+ s = Excel.new("myspreadsheet.xls") # creates an Excel Spreadsheet instance
15
+ s = Google.new("myspreadsheetkey_at_google") # creates an Google Spreadsheet instance
16
+ s = Excelx.new("myspreadsheet.xlsx") # creates an Excel Spreadsheet instance for Excel .xlsx files
17
+
18
+ s.default_sheet = s.sheets.first # first sheet in the spreadsheet file will be used
19
+
20
+ # s.sheet is an array which holds the names of the sheets within
21
+ # a spreadsheet.
22
+ # you can also write
23
+ # s.default_sheet = s.sheets[3] or
24
+ # s.default_sheet = 'Sheet 3'
25
+
26
+ s.cell(1,1) # returns the content of the first row/first cell in the sheet
27
+ s.cell('A',1) # same cell
28
+ s.cell(1,'A') # same cell
29
+ s.cell(1,'A',s.sheets[0]) # same cell
30
+
31
+ # almost all methods have an optional argument 'sheet'.
32
+ # If this parameter is ommitted, the default_sheet will be used.
33
+
34
+ s.info # prints infos about the spreadsheet file
35
+
36
+ s.first_row # the number of the first row
37
+ s.last_row # the number of the last row
38
+ s.first_column # the number of the first column
39
+ s.last_column # the number of the last column
40
+
41
+
42
+ see http://roo.rubyforge.org for a more complete tutorial
43
+
data/lib/roo/excel.rb ADDED
@@ -0,0 +1,455 @@
1
+ require 'rubygems'
2
+ gem 'spreadsheet', '>= 0.6.3.1'
3
+ require 'spreadsheet'
4
+ CHARGUESS = false
5
+ require 'charguess' if CHARGUESS
6
+
7
+ # ruby-spreadsheet has a font object so we're extending it
8
+ # with our own functionality but still providing full access
9
+ # to the user for other font information
10
+ module ExcelFontExtensions
11
+ def bold?(*args)
12
+ #From ruby-spreadsheet doc: 100 <= weight <= 1000, bold => 700, normal => 400
13
+ case weight
14
+ when 700
15
+ true
16
+ else
17
+ false
18
+ end
19
+ end
20
+
21
+ def italic?
22
+ italic
23
+ end
24
+
25
+ def underline?
26
+ underline != :none
27
+ end
28
+
29
+ end
30
+
31
+ # Class for handling Excel-Spreadsheets
32
+ class Excel < GenericSpreadsheet
33
+
34
+ EXCEL_NO_FORMULAS = 'formulas are not supported for excel spreadsheets'
35
+
36
+ # Creates a new Excel spreadsheet object.
37
+ # Parameter packed: :zip - File is a zip-file
38
+ def initialize(filename, packed = nil, file_warning = :error)
39
+ super()
40
+ @file_warning = file_warning
41
+ @tmpdir = "oo_"+$$.to_s
42
+ @tmpdir = File.join(ENV['ROO_TMP'], @tmpdir) if ENV['ROO_TMP']
43
+ unless File.exists?(@tmpdir)
44
+ FileUtils::mkdir(@tmpdir)
45
+ end
46
+ filename = open_from_uri(filename) if filename[0,7] == "http://"
47
+ filename = open_from_stream(filename[7..-1]) if filename[0,7] == "stream:"
48
+ filename = unzip(filename) if packed and packed == :zip
49
+ begin
50
+ file_type_check(filename,'.xls','an Excel')
51
+ @filename = filename
52
+ unless File.file?(@filename)
53
+ raise IOError, "file #{@filename} does not exist"
54
+ end
55
+ @workbook = Spreadsheet.open(filename)
56
+ @default_sheet = nil
57
+ # no need to set default_sheet if there is only one sheet in the document
58
+ if self.sheets.size == 1
59
+ @default_sheet = self.sheets.first
60
+ end
61
+ ensure
62
+ #if ENV["roo_local"] != "thomas-p"
63
+ FileUtils::rm_r(@tmpdir)
64
+ #end
65
+ end
66
+ @cell = Hash.new
67
+ @cell_type = Hash.new
68
+ @formula = Hash.new
69
+ @first_row = Hash.new
70
+ @last_row = Hash.new
71
+ @first_column = Hash.new
72
+ @last_column = Hash.new
73
+ @header_line = 1
74
+ @cells_read = Hash.new
75
+ @fonts = Hash.new
76
+ end
77
+
78
+ # returns an array of sheet names in the spreadsheet
79
+ def sheets
80
+ result = []
81
+ @workbook.worksheets.each do |worksheet|
82
+ # TODO: is there a better way to do conversion?
83
+ if CHARGUESS
84
+ encoding = CharGuess::guess(worksheet.name)
85
+ encoding = 'unicode' unless encoding
86
+
87
+
88
+ result << Iconv.new('utf-8',encoding).iconv(
89
+ worksheet.name
90
+ )
91
+ else
92
+ result << platform_specific_iconv(worksheet.name)
93
+ end
94
+ end
95
+ return result
96
+ end
97
+
98
+ # returns the content of a cell. The upper left corner is (1,1) or ('A',1)
99
+ def cell(row,col,sheet=nil)
100
+ sheet = @default_sheet unless sheet
101
+ raise ArgumentError unless sheet
102
+ read_cells(sheet) unless @cells_read[sheet]
103
+ raise "should be read" unless @cells_read[sheet]
104
+ row,col = normalize(row,col)
105
+ if celltype(row,col,sheet) == :date
106
+ yyyy,mm,dd = @cell[sheet][[row,col]].split('-')
107
+ return Date.new(yyyy.to_i,mm.to_i,dd.to_i)
108
+ end
109
+ if celltype(row,col,sheet) == :string
110
+ return platform_specific_iconv(@cell[sheet][[row,col]])
111
+ else
112
+ return @cell[sheet][[row,col]]
113
+ end
114
+ end
115
+
116
+ # returns the type of a cell:
117
+ # * :float
118
+ # * :string,
119
+ # * :date
120
+ # * :percentage
121
+ # * :formula
122
+ # * :time
123
+ # * :datetime
124
+ def celltype(row,col,sheet=nil)
125
+ sheet = @default_sheet unless sheet
126
+ read_cells(sheet) unless @cells_read[sheet]
127
+ row,col = normalize(row,col)
128
+ begin
129
+ if @formula[sheet][[row,col]]
130
+ return :formula
131
+ else
132
+ @cell_type[sheet][[row,col]]
133
+ end
134
+ rescue
135
+ puts "Error in sheet #{sheet}, row #{row}, col #{col}"
136
+ raise
137
+ end
138
+ end
139
+
140
+ # returns the first non empty column
141
+ def first_column(sheet=nil)
142
+ sheet = @default_sheet unless sheet
143
+ return @first_column[sheet] if @first_column[sheet]
144
+ fr, lr, fc, lc = get_firsts_lasts(sheet)
145
+ fc
146
+ end
147
+
148
+ # returns the last non empty column
149
+ def last_column(sheet=nil)
150
+ sheet = @default_sheet unless sheet
151
+ return @last_column[sheet] if @last_column[sheet]
152
+ fr, lr, fc, lc = get_firsts_lasts(sheet)
153
+ lc
154
+ end
155
+
156
+ # returns the first non empty row
157
+ def first_row(sheet=nil)
158
+ sheet = @default_sheet unless sheet
159
+ return @first_row[sheet] if @first_row[sheet]
160
+ fr, lr, fc, lc = get_firsts_lasts(sheet)
161
+ fr
162
+ end
163
+
164
+ # returns the last non empty row
165
+ def last_row(sheet=nil)
166
+ sheet = @default_sheet unless sheet
167
+ return @last_row[sheet] if @last_row[sheet]
168
+ fr, lr, fc, lc = get_firsts_lasts(sheet)
169
+ lr
170
+ end
171
+
172
+ # returns NO formula in excel spreadsheets
173
+ def formula(row,col,sheet=nil)
174
+ raise EXCEL_NO_FORMULAS
175
+ end
176
+
177
+ # raises an exception because formulas are not supported for excel files
178
+ def formula?(row,col,sheet=nil)
179
+ raise EXCEL_NO_FORMULAS
180
+ end
181
+
182
+ # returns NO formulas in excel spreadsheets
183
+ def formulas(sheet=nil)
184
+ raise EXCEL_NO_FORMULAS
185
+ end
186
+
187
+ # Given a cell, return the cell's font
188
+ def font(row, col, sheet=nil)
189
+ sheet = @default_sheet unless sheet
190
+ read_cells(sheet) unless @cells_read[sheet]
191
+ row,col = normalize(row,col)
192
+ @fonts[sheet][[row,col]]
193
+ end
194
+
195
+ # shows the internal representation of all cells
196
+ # mainly for debugging purposes
197
+ def to_s(sheet=nil)
198
+ sheet = @default_sheet unless sheet
199
+ read_cells(sheet) unless @cells_read[sheet]
200
+ @cell[sheet].inspect
201
+ end
202
+
203
+ private
204
+ # determine the first and last boundaries
205
+ def get_firsts_lasts(sheet=nil)
206
+
207
+ # 2008-09-14 BEGINf
208
+ fr=lr=fc=lc=nil
209
+ sheet = @default_sheet unless sheet
210
+ if ! @cells_read[sheet]
211
+ read_cells(sheet)
212
+ end
213
+ if @cell[sheet] # nur wenn ueberhaupt Zellen belegt sind
214
+ @cell[sheet].each {|cellitem|
215
+ key = cellitem.first
216
+ y,x = key
217
+
218
+ if cellitem[1].class != String or
219
+ (cellitem[1].class == String and cellitem[1] != "")
220
+ fr = y unless fr
221
+ fr = y if y < fr
222
+
223
+ lr = y unless lr
224
+ lr = y if y > lr
225
+
226
+ fc = x unless fc
227
+ fc = x if x < fc
228
+
229
+ lc = x unless lc
230
+ lc = x if x > lc
231
+ end
232
+ }
233
+ end
234
+ @first_row[sheet] = fr
235
+ @last_row[sheet] = lr
236
+ @first_column[sheet] = fc
237
+ @last_column[sheet] = lc
238
+ return fr, lr, fc, lc
239
+ end
240
+
241
+ # converts name of a sheet to index (0,1,2,..)
242
+ def sheet_no(name)
243
+ return name-1 if name.kind_of?(Fixnum)
244
+ i = 0
245
+ @workbook.worksheets.each do |worksheet|
246
+ # TODO: is there a better way to do conversion?
247
+ return i if name == platform_specific_iconv(worksheet.name)
248
+ #Iconv.new('utf-8','unicode').iconv(
249
+ # @workbook.worksheet(i).name
250
+ # )
251
+ i += 1
252
+ end
253
+ raise StandardError, "sheet '#{name}' not found"
254
+ end
255
+
256
+ def empty_row?(row)
257
+ content = false
258
+ row.compact.each {|elem|
259
+ if elem != ''
260
+ content = true
261
+ end
262
+ }
263
+ ! content
264
+ end
265
+
266
+ def empty_column?(col)
267
+ content = false
268
+ col.compact.each {|elem|
269
+ if elem != ''
270
+ content = true
271
+ end
272
+ }
273
+ ! content
274
+ end
275
+
276
+ def platform_specific_iconv(value)
277
+ case RUBY_PLATFORM.downcase
278
+ when /darwin/
279
+ result = Iconv.new('utf-8','utf-8').iconv(value)
280
+ when /solaris/
281
+ result = Iconv.new('utf-8','utf-8').iconv(value)
282
+ when /mswin32/
283
+ result = Iconv.new('utf-8','iso-8859-1').iconv(value)
284
+ else
285
+ result = value
286
+ end # case
287
+ if every_second_null?(result)
288
+ result = remove_every_second_null(result)
289
+ end
290
+ result
291
+ end
292
+
293
+ def every_second_null?(str)
294
+ result = true
295
+ return false if str.length < 2
296
+ 0.upto(str.length/2-1) do |i|
297
+ c = str[i*2,1]
298
+ n = str[i*2+1,1]
299
+ if n != "\000"
300
+ result = false
301
+ break
302
+ end
303
+ end
304
+ result
305
+ end
306
+
307
+ def remove_every_second_null(str)
308
+ result = ''
309
+ 0.upto(str.length/2-1) do |i|
310
+ c = str[i*2,1]
311
+ result += c
312
+ end
313
+ result
314
+ end
315
+
316
+ # helper function to set the internal representation of cells
317
+ def set_cell_values(sheet,row,col,i,v,vt,formula,tr,font)
318
+ #key = "#{y},#{x+i}"
319
+ key = [row,col+i]
320
+ @cell_type[sheet] = {} unless @cell_type[sheet]
321
+ @cell_type[sheet][key] = vt
322
+ @formula[sheet] = {} unless @formula[sheet]
323
+ @formula[sheet][key] = formula if formula
324
+ @cell[sheet] = {} unless @cell[sheet]
325
+ @fonts[sheet] = {} unless @fonts[sheet]
326
+ @fonts[sheet][key] = font
327
+
328
+ case vt # @cell_type[sheet][key]
329
+ when :float
330
+ @cell[sheet][key] = v.to_f
331
+ when :string
332
+ @cell[sheet][key] = v
333
+ when :date
334
+ @cell[sheet][key] = v
335
+ when :datetime
336
+ @cell[sheet][key] = DateTime.new(v.year,v.month,v.day,v.hour,v.min,v.sec)
337
+ when :percentage
338
+ @cell[sheet][key] = v.to_f
339
+ when :time
340
+ @cell[sheet][key] = v
341
+ else
342
+ @cell[sheet][key] = v
343
+ end
344
+ end
345
+
346
+ # read all cells in the selected sheet
347
+ def read_cells(sheet=nil)
348
+ sheet = @default_sheet unless sheet
349
+ raise ArgumentError, "Error: sheet '#{sheet||'nil'}' not valid" if @default_sheet == nil and sheet==nil
350
+ raise RangeError unless self.sheets.include? sheet
351
+
352
+ if @cells_read[sheet]
353
+ raise "sheet #{sheet} already read"
354
+ end
355
+
356
+ worksheet = @workbook.worksheet(sheet_no(sheet))
357
+ row_index=1
358
+ worksheet.each(0) do |row|
359
+ (0..row.size).each do |cell_index|
360
+ cell = row.at(cell_index)
361
+ next if cell.nil? #skip empty cells
362
+ next if cell.class == Spreadsheet::Formula
363
+ if date_or_time?(row, cell_index)
364
+ vt, v = read_cell_date_or_time(row, cell_index)
365
+ else
366
+ vt, v = read_cell(row, cell_index)
367
+ end
368
+ formula = tr = nil #TODO:???
369
+ col_index = cell_index + 1
370
+ font = row.format(cell_index).font
371
+ font.extend(ExcelFontExtensions)
372
+ set_cell_values(sheet,row_index,col_index,0,v,vt,formula,tr,font)
373
+ end #row
374
+ row_index += 1
375
+ end # worksheet
376
+ @cells_read[sheet] = true
377
+ end
378
+
379
+ # Test the cell to see if it's a valid date/time.
380
+ def date_or_time?(row, idx)
381
+ format = row.format(idx)
382
+ if format.date_or_time?
383
+ cell = row.at(idx)
384
+ cell.to_s.to_f > 0 ? true : false # cell value must be numeric
385
+ else
386
+ false
387
+ end
388
+ end
389
+ private :date_or_time?
390
+
391
+ # Read the date-time cell and convert to,
392
+ # the date-time values for Roo
393
+ def read_cell_date_or_time(row, idx)
394
+ cell = row.at(idx).to_s.to_f
395
+ if cell < 1.0
396
+ value_type = :time
397
+ f = cell*24.0*60.0*60.0
398
+ secs = f.round
399
+ h = (secs / 3600.0).floor
400
+ secs = secs - 3600*h
401
+ m = (secs / 60.0).floor
402
+ secs = secs - 60*m
403
+ s = secs
404
+ value = h*3600+m*60+s
405
+ else
406
+ datetime = row.datetime(idx)
407
+ if datetime.hour != 0 or
408
+ datetime.min != 0 or
409
+ datetime.sec != 0
410
+ value_type = :datetime
411
+ value = datetime
412
+ else
413
+ value_type = :date
414
+ value = row.date(idx)
415
+ value = sprintf("%04d-%02d-%02d",value.year,value.month,value.day)
416
+ end
417
+ end
418
+ return value_type, value
419
+ end
420
+ private :read_cell_date_or_time
421
+
422
+ # Read the cell and based on the class,
423
+ # return the values for Roo
424
+ def read_cell(row, idx)
425
+ cell = row.at(idx)
426
+ case cell
427
+ when Float, Integer, Fixnum, Bignum
428
+ value_type = :float
429
+ value = cell.to_f
430
+ when String, TrueClass, FalseClass
431
+ value_type = :string
432
+ value = cell.to_s
433
+ else
434
+ value_type = cell.class.to_s.downcase.to_sym
435
+ value = nil
436
+ end # case
437
+ return value_type, value
438
+ end
439
+ private :read_cell
440
+
441
+ #TODO: testing only
442
+ # def inject_null_characters(str)
443
+ # if str.class != String
444
+ # return str
445
+ # end
446
+ # new_str=''
447
+ # 0.upto(str.size-1) do |i|
448
+ # new_str += str[i,1]
449
+ # new_str += "\000"
450
+ # end
451
+ # new_str
452
+ # end
453
+ #
454
+
455
+ end