Class: Excelx

Inherits:
GenericSpreadsheet show all
Defined in:
lib/roo/excelx.rb

Defined Under Namespace

Classes: Font

Constant Summary collapse

FORMATS =
{
  'General' => :float,
  '0' => :float,
  '0.00' => :float,
  '#,##0' => :float,
  '#,##0.00' => :float,
  '0%' => :percentage,
  '0.00%' => :percentage,
  '0.00E+00' => :float,
  '# ?/?' => :float, #??? TODO:
  '# ??/??' => :float, #??? TODO:
  'mm-dd-yy' => :date,
  'd-mmm-yy' => :date,
  'd-mmm' => :date,
  'mmm-yy' => :date,
  'h:mm AM/PM' => :date,
  'h:mm:ss AM/PM' => :date,
  'h:mm' => :time,
  'h:mm:ss' => :time,
  'm/d/yy h:mm' => :date,
  '#,##0 ;(#,##0)' => :float,
  '#,##0 ;[Red](#,##0)' => :float,
  '#,##0.00;(#,##0.00)' => :float,
  '#,##0.00;[Red](#,##0.00)' => :float,
  'mm:ss' => :time,
  '[h]:mm:ss' => :time,
  'mmss.0' => :time,
  '##0.0E+0' => :float,
  '@' => :float,
  #-- zusaetzliche Formate, die nicht standardmaessig definiert sind:
  "yyyy\\-mm\\-dd" => :date,
  'dd/mm/yy' => :date,
  'hh:mm:ss' => :time,
  "dd/mm/yy\\ hh:mm" => :datetime,
}
STANDARD_FORMATS =
{ 
  0 => 'General',
  1 => '0',
  2 => '0.00',
  3 => '#,##0',
  4 => '#,##0.00',
  9 => '0%',
  10 => '0.00%',
  11 => '0.00E+00',
  12 => '# ?/?',
  13 => '# ??/??',
  14 => 'mm-dd-yy',
  15 => 'd-mmm-yy',
  16 => 'd-mmm',
  17 => 'mmm-yy',
  18 => 'h:mm AM/PM',
  19 => 'h:mm:ss AM/PM',
  20 => 'h:mm',
  21 => 'h:mm:ss',
  22 => 'm/d/yy h:mm',
  37 => '#,##0 ;(#,##0)',
  38 => '#,##0 ;[Red](#,##0)',
  39 => '#,##0.00;(#,##0.00)',
  40 => '#,##0.00;[Red](#,##0.00)',
  45 => 'mm:ss',
  46 => '[h]:mm:ss',
  47 => 'mmss.0',
  48 => '##0.0E+0',
  49 => '@',
}
@@nr =
0

Instance Attribute Summary

Attributes inherited from GenericSpreadsheet

#default_sheet, #header_line

Instance Method Summary collapse

Methods inherited from GenericSpreadsheet

#column, #empty?, #find, #first_column_as_letter, #info, #last_column_as_letter, #method_missing, #reload, #remove_tmp, #to_csv, #to_xml, #to_yaml

Constructor Details

#initialize(filename, packed = nil, file_warning = :error) ⇒ Excelx

initialization and opening of a spreadsheet file values for packed: :zip



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/roo/excelx.rb', line 86

def initialize(filename, packed=nil, file_warning = :error) #, create = false)
  super()
  @file_warning = file_warning
  @tmpdir = "oo_"+$$.to_s
  @tmpdir = File.join(ENV['ROO_TMP'], @tmpdir) if ENV['ROO_TMP'] 
  unless File.exists?(@tmpdir)
    FileUtils::mkdir(@tmpdir)
  end
  filename = open_from_uri(filename) if filename[0,7] == "http://"
  filename = unzip(filename) if packed and packed == :zip
  begin
    file_type_check(filename,'.xlsx','an Excel-xlsx')
    @cells_read = Hash.new
    @read_first_100_rows = Hash.new
    @filename = filename
    unless File.file?(@filename)
      raise IOError, "file #{@filename} does not exist"
    end
    @@nr += 1
    @file_nr = @@nr
    extract_content(@filename)
    file = File.new(File.join(@tmpdir, @file_nr.to_s+"_roo_workbook.xml"))
    # TODO: @workbook_doc = XML::Parser.io(file).parse
    @workbook_doc = Nokogiri::XML(file)
    file.close
    
    if File.exist?(File.join(@tmpdir, @file_nr.to_s+'_roo_sharedStrings.xml'))
      file = File.new(File.join(@tmpdir, @file_nr.to_s+'_roo_sharedStrings.xml'))
      #TODO: @sharedstring_doc = XML::Parser.io(file).parse
      @sharedstring_doc = Nokogiri::XML(file)
      file.close
    end
    @styles_table = []
    @style_definitions = Array.new # TODO: ??? { |h,k| h[k] = {} }
    if File.exist?(File.join(@tmpdir, @file_nr.to_s+'_roo_styles.xml'))
      file = File.new(File.join(@tmpdir, @file_nr.to_s+'_roo_styles.xml'))
      #TODO: @styles_doc = XML::Parser.io(file).parse
      @styles_doc = Nokogiri::XML(file)
      file.close
      read_styles(@styles_doc)
    end
    @sheet_doc = []
    @sheet_files.each_with_index do |item, i|
      file = File.new(item)
      #TODO: @sheet_doc[i] = XML::Parser.io(file).parse
      @sheet_doc[i] = Nokogiri::XML(file)
      file.close
    end
  ensure
    #if ENV["roo_local"] != "thomas-p"
    FileUtils::rm_r(@tmpdir)
    #end
  end
  @default_sheet = self.sheets.first
  @cell = Hash.new
  @cell_type = Hash.new
  @formula = Hash.new
  @first_row = Hash.new
  @last_row = Hash.new
  @first_column = Hash.new
  @last_column = Hash.new
  @header_line = 1
  @excelx_type = Hash.new
  @excelx_value = Hash.new
  @s_attribute = Hash.new # TODO: ggf. wieder entfernen nur lokal benoetigt
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class GenericSpreadsheet

Instance Method Details

#cell(row, col, sheet = nil) ⇒ Object

Returns the content of a spreadsheet-cell. (1,1) is the upper left corner. (1,1), (1,‘A’), (‘A’,1), (‘a’,1) all refers to the cell at the first line and first row.



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/roo/excelx.rb', line 157

def cell(row, col, sheet=nil)
  sheet = @default_sheet unless sheet
  unless @cells_read[sheet] or (@read_first_100_rows[sheet] and row <= 100)
    read_cells(sheet) 
  end
  row,col = normalize(row,col)
  if celltype(row,col,sheet) == :date
    yyyy,mm,dd = @cell[sheet][[row,col]].split('-')
    return Date.new(yyyy.to_i,mm.to_i,dd.to_i)
  elsif celltype(row,col,sheet) == :datetime
    date_part,time_part = @cell[sheet][[row,col]].split(' ')
    yyyy,mm,dd = date_part.split('-')
    hh,mi,ss = time_part.split(':')
    return DateTime.civil(yyyy.to_i,mm.to_i,dd.to_i,hh.to_i,mi.to_i,ss.to_i)
  end
  @cell[sheet][[row,col]]
end

#celltype(row, col, sheet = nil) ⇒ Object

returns the type of a cell:

  • :float

  • :string,

  • :date

  • :percentage

  • :formula

  • :time

  • :datetime



264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/roo/excelx.rb', line 264

def celltype(row,col,sheet=nil)
  sheet = @default_sheet unless sheet
  unless @cells_read[sheet] or (@read_first_100_rows[sheet] and row <= 100)
      read_cells(sheet) 
  end
  row,col = normalize(row,col)
  if @formula[sheet][[row,col]]
    return :formula
  else
    @cell_type[sheet][[row,col]]
  end
end

#excelx_format(row, col, sheet = nil) ⇒ Object

returns the internal format of an excel cell



298
299
300
301
302
303
304
305
# File 'lib/roo/excelx.rb', line 298

def excelx_format(row,col,sheet=nil)
  sheet = @default_sheet unless sheet
  read_cells(sheet) unless @cells_read[sheet]
  row,col = normalize(row,col)
  s = @s_attribute[sheet][[row,col]]
  result = attribute2format(s).to_s
  result
end

#excelx_type(row, col, sheet = nil) ⇒ Object

returns the internal type of an excel cell

  • :numeric_or_formula

  • :string

Note: this is only available within the Excelx class



281
282
283
284
285
286
# File 'lib/roo/excelx.rb', line 281

def excelx_type(row,col,sheet=nil)
  sheet = @default_sheet unless sheet
  read_cells(sheet) unless @cells_read[sheet]
  row,col = normalize(row,col)
  return @excelx_type[sheet][[row,col]]
end

#excelx_value(row, col, sheet = nil) ⇒ Object

returns the internal value of an excelx cell Note: this is only available within the Excelx class



290
291
292
293
294
295
# File 'lib/roo/excelx.rb', line 290

def excelx_value(row,col,sheet=nil)
  sheet = @default_sheet unless sheet
  read_cells(sheet) unless @cells_read[sheet]
  row,col = normalize(row,col)
  return @excelx_value[sheet][[row,col]]
end

#first_column(sheet = nil) ⇒ Object



385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/roo/excelx.rb', line 385

def first_column(sheet=nil)
  if sheet == nil
    sheet = @default_sheet
  end
  read_first_100_rows(sheet) unless @read_first_100_rows[sheet]
  if @first_column[sheet]
    return @first_column[sheet]
  end
  impossible_value = 999_999 # more than a spreadsheet can hold
  result = impossible_value
  @cell[sheet].each_pair {|key,value|
    y,x = key # _to_string(key).split(',')
    x = x # .to_i
    result = [result, x].min if value
  } if @cell[sheet]
  result = nil if result == impossible_value
  @first_column[sheet] = result
  result
end

#first_row(sheet = nil) ⇒ Object



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/roo/excelx.rb', line 342

def first_row(sheet=nil)
  if sheet == nil
    sheet = @default_sheet
  end
  read_first_100_rows(sheet) unless @read_first_100_rows[sheet] or @cells_read[sheet]
  if @first_row[sheet]
    return @first_row[sheet]
  end
  impossible_value = 999_999 # more than a spreadsheet can hold
  result = impossible_value
  @cell[sheet].each_pair {|key,value|
    y,x = key # _to_string(key).split(',')
    y = y.to_i
    result = [result, y].min if value
  } if @cell[sheet]
  result = nil if result == impossible_value
  @first_row[sheet] = result
  result
end

#font(row, col, sheet = nil) ⇒ Object

Given a cell, return the cell’s style



228
229
230
231
232
233
234
235
236
# File 'lib/roo/excelx.rb', line 228

def font(row, col, sheet=nil)
  sheet = @default_sheet unless sheet
  read_cells(sheet) unless @cells_read[sheet]
  row,col = normalize(row,col)
  s_attribute = @s_attribute[sheet][[row,col]]
  s_attribute ||= 0
  s_attribute = s_attribute.to_i
  @style_definitions[s_attribute]
end

#formula(row, col, sheet = nil) ⇒ Object

Returns the formula at (row,col). Returns nil if there is no formula. The method #formula? checks if there is a formula.



192
193
194
195
196
197
198
199
200
201
# File 'lib/roo/excelx.rb', line 192

def formula(row,col,sheet=nil)
  sheet = @default_sheet unless sheet
  read_cells(sheet) unless @cells_read[sheet]
  row,col = normalize(row,col)
  if @formula[sheet][[row,col]] == nil
    return nil
  else
    return @formula[sheet][[row,col]]
  end
end

#formula?(row, col, sheet = nil) ⇒ Boolean

true, if there is a formula

Returns:

  • (Boolean)


204
205
206
207
208
209
# File 'lib/roo/excelx.rb', line 204

def formula?(row,col,sheet=nil)
  sheet = @default_sheet unless sheet
  read_cells(sheet) unless @cells_read[sheet]
  row,col = normalize(row,col)
  formula(row,col) != nil
end

#formulas(sheet = nil) ⇒ Object

returns each formula in the selected sheet as an array of elements

row, col, formula


327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/roo/excelx.rb', line 327

def formulas(sheet=nil)
  theformulas = Array.new
  sheet = @default_sheet unless sheet
  read_cells(sheet) unless @cells_read[sheet]
  first_row(sheet).upto(last_row(sheet)) {|row|
    first_column(sheet).upto(last_column(sheet)) {|col|
      if formula?(row,col,sheet)
        f = [row, col, formula(row,col,sheet)]
        theformulas << f
      end
    }
  }
  theformulas
end

#last_column(sheet = nil) ⇒ Object

returns the number of the last non-empty column



407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/roo/excelx.rb', line 407

def last_column(sheet=nil)
  sheet = @default_sheet unless sheet
  read_first_100_rows(sheet) unless @read_first_100_rows[sheet]
  if @last_column[sheet]
    return @last_column[sheet]
  end
  impossible_value = 0
  result = impossible_value
  @cell[sheet].each_pair {|key,value|
    y,x = key # _to_string(key).split(',')
    x = x.to_i
    result = [result, x].max if value
  } if @cell[sheet]
  result = nil if result == impossible_value
  @last_column[sheet] = result
  result
end

#last_row(sheet = nil) ⇒ Object

returns the number of the last non-empty row



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/roo/excelx.rb', line 363

def last_row(sheet=nil)
  sheet = @default_sheet unless sheet
  if @last_row[sheet]
    return @last_row[sheet]
  end
  # read a few columns
  unless @cells_read[sheet]
    fst_column = first_column(sheet)
    read_column(sheet,GenericSpreadsheet.number_to_letter(fst_column),3)
  end
  impossible_value = 0
  result = impossible_value
  @cell[sheet].each_pair {|key,value|
    y,x = key # _to_string(key).split(',')
    y = y.to_i
    result = [result, y].max if value
  } if @cell[sheet]
  result = nil if result == impossible_value
  @last_row[sheet] = result
  result
end

#row(rownumber, sheet = nil) ⇒ Object

row numbers are 1,2,3,… like in the spreadsheet



177
178
179
180
181
182
183
184
185
186
187
# File 'lib/roo/excelx.rb', line 177

def row(rownumber,sheet=nil)
  sheet = @default_sheet unless sheet
  unless @cells_read[sheet] or (@read_first_100_rows[sheet] and rownumber <= 100)
    read_cells(sheet) 
  end
  result = []
  first_column(sheet).upto(last_column(sheet)) do |col|
    result << cell(rownumber,col,sheet)
  end
  result
end

#set(row, col, value, sheet = nil) ⇒ Object

set a cell to a certain value (this will not be saved back to the spreadsheet file!)



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/roo/excelx.rb', line 240

def set(row,col,value,sheet=nil) #:nodoc:
  sheet = @default_sheet unless sheet
  read_cells(sheet) unless @cells_read[sheet]
  row,col = normalize(row,col)
  set_value(row,col,value,sheet)
  if value.class == Fixnum
    set_type(row,col,:float,sheet)
  elsif value.class == String
    set_type(row,col,:string,sheet)
  elsif value.class == Float
    set_type(row,col,:string,sheet)
  else
    raise ArgumentError, "Type for "+value.to_s+" not set"
  end
end

#sheetsObject

returns an array of sheet names in the spreadsheet



308
309
310
311
312
313
314
315
316
# File 'lib/roo/excelx.rb', line 308

def sheets
  return_sheets = []
  #TODO:    @workbook_doc.find("//*[local-name()='sheet']").each do |sheet|
  @workbook_doc.xpath("//*[local-name()='sheet']").each do |sheet|
    #TODO:      return_sheets << sheet.attributes.to_h['name']
    return_sheets << sheet['name']
  end
  return_sheets
end

#to_s(sheet = nil) ⇒ Object

shows the internal representation of all cells for debugging purposes



319
320
321
322
323
# File 'lib/roo/excelx.rb', line 319

def to_s(sheet=nil)
  sheet = @default_sheet unless sheet
  read_cells(sheet) unless @cells_read[sheet]
  @cell[sheet].inspect
end