roo 1.2.3 → 1.3.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  require 'gdata/spreadsheet'
2
+ require 'xml'
2
3
 
3
4
  # overwrite some methods from the gdata-gem:
4
5
  module GData
@@ -45,7 +46,7 @@ XML
45
46
  def add_to_cell_roo(row,col,value, sheet_no=1)
46
47
  save_entry_roo(entry_roo(value,row,col), sheet_no)
47
48
  end
48
-
49
+
49
50
  #-- new
50
51
  def get_one_sheet
51
52
  path = "/feeds/cells/#{@spreadsheet_id}/1/private/full"
@@ -70,11 +71,13 @@ XML
70
71
  doc = Hpricot(request(path))
71
72
  return doc
72
73
  end
74
+
73
75
  end # class
74
76
  end # module
75
77
 
76
78
  class Google < GenericSpreadsheet
77
-
79
+ attr_accessor :date_format, :datetime_format
80
+
78
81
  # Creates a new Google spreadsheet object.
79
82
  def initialize(spreadsheetkey,user=nil,password=nil)
80
83
  @filename = spreadsheetkey
@@ -88,8 +91,8 @@ class Google < GenericSpreadsheet
88
91
  password = ENV['GOOGLE_PASSWORD']
89
92
  end
90
93
  @default_sheet = nil
91
- @cell = Hash.new
92
- @cell_type = Hash.new
94
+ @cell = Hash.new {|h,k| h[k]=Hash.new}
95
+ @cell_type = Hash.new {|h,k| h[k]=Hash.new}
93
96
  @formula = Hash.new
94
97
  @first_row = Hash.new
95
98
  @last_row = Hash.new
@@ -97,10 +100,12 @@ class Google < GenericSpreadsheet
97
100
  @last_column = Hash.new
98
101
  @cells_read = Hash.new
99
102
  @header_line = 1
100
-
103
+ @date_format = '%d/%m/%Y'
104
+ @datetime_format = '%d/%m/%Y %H:%M:%S'
105
+ @time_format = '%H:%M:%S'
101
106
  @gs = GData::Spreadsheet.new(spreadsheetkey)
102
107
  @gs.authenticate(user, password)
103
-
108
+ @sheetlist = @gs.sheetlist
104
109
  #-- ----------------------------------------------------------------------
105
110
  #-- TODO: Behandlung von Berechtigungen hier noch einbauen ???
106
111
  #-- ----------------------------------------------------------------------
@@ -112,32 +117,42 @@ class Google < GenericSpreadsheet
112
117
 
113
118
  # returns an array of sheet names in the spreadsheet
114
119
  def sheets
115
- return @gs.sheetlist
120
+ @sheetlist
116
121
  end
117
122
 
118
- # is String a date with format DD/MM/YYYY
119
- def Google.date?(string)
120
- return false if string.class == Float
121
- return true if string.class == Date
122
- return string.strip =~ /^([0-9]+)\/([0-9]+)\/([0-9]+)$/
123
+ def date?(string)
124
+ begin
125
+ Date.strptime(string, @date_format)
126
+ true
127
+ rescue
128
+ false
129
+ end
123
130
  end
124
131
 
125
132
  # is String a time with format HH:MM:SS?
126
- def Google.time?(string)
127
- return false if string.class == Float
128
- return true if string.class == Date
129
- return string.strip =~ /^([0-9]+):([0-9]+):([0-9]+)$/
133
+ def time?(string)
134
+ begin
135
+ DateTime.strptime(string, @time_format)
136
+ true
137
+ rescue
138
+ false
139
+ end
130
140
  end
131
141
 
132
- # is String a date+time with format DD/MM/YYYY HH:MM:SS
133
- def Google.datetime?(string)
134
- return false if string.class == Float
135
- return true if string.class == Date
136
- return string.strip =~ /^([0-9]+)\/([0-9]+)\/([0-9]+)\ ([0-9]+):([0-9]+):([0-9]+)$/
142
+ def datetime?(string)
143
+ begin
144
+ DateTime.strptime(string, @datetime_format)
145
+ true
146
+ rescue
147
+ false
148
+ end
137
149
  end
138
150
 
151
+ def numeric?(string)
152
+ string =~ /^[0-9]+[\.]*[0-9]*$/
153
+ end
139
154
 
140
- def Google.timestring_to_seconds(value)
155
+ def timestring_to_seconds(value)
141
156
  hms = value.split(':')
142
157
  hms[0].to_i*3600 + hms[1].to_i*60 + hms[2].to_i
143
158
  end
@@ -151,24 +166,21 @@ class Google < GenericSpreadsheet
151
166
  check_default_sheet #TODO: 2007-12-16
152
167
  read_cells(sheet) unless @cells_read[sheet]
153
168
  row,col = normalize(row,col)
169
+ value = @cell[sheet]["#{row},#{col}"]
154
170
  if celltype(row,col,sheet) == :date
155
- yyyy,mm,dd = @cell[sheet]["#{row},#{col}"].split('-')
156
171
  begin
157
- return Date.new(yyyy.to_i,mm.to_i,dd.to_i)
172
+ return Date.strptime(value, @date_format)
158
173
  rescue ArgumentError
159
- raise "Invalid date parameter: #{yyyy}, #{mm}, #{dd}"
174
+ raise "Invalid Date #{sheet}[#{row},#{col}] #{value} using format '{@date_format}'"
160
175
  end
161
176
  elsif celltype(row,col,sheet) == :datetime
162
177
  begin
163
- date_part,time_part = @cell[sheet]["#{row},#{col}"].split(' ')
164
- yyyy,mm,dd = date_part.split('-')
165
- hh,mi,ss = time_part.split(':')
166
- return DateTime.civil(yyyy.to_i,mm.to_i,dd.to_i,hh.to_i,mi.to_i,ss.to_i)
178
+ return DateTime.strptime(value, @datetime_format)
167
179
  rescue ArgumentError
168
- raise "Invalid date parameter: #{yyyy}, #{mm}, #{dd}, #{hh}, #{mi}, #{ss}"
180
+ raise "Invalid DateTime #{sheet}[#{row},#{col}] #{value} using format '{@datetime_format}'"
169
181
  end
170
182
  end
171
- return @cell[sheet]["#{row},#{col}"]
183
+ return value
172
184
  end
173
185
 
174
186
  # returns the type of a cell:
@@ -291,8 +303,15 @@ class Google < GenericSpreadsheet
291
303
  end
292
304
  row,col = normalize(row,col)
293
305
  @gs.add_to_cell_roo(row,col,value,sheet_no)
306
+ # re-read the portion of the document that has changed
307
+ if @cells_read[sheet]
308
+ key = "#{row},#{col}"
309
+ (value, value_type) = determine_datatype(value.to_s)
310
+ @cell[sheet][key] = value
311
+ @cell_type[sheet][key] = value_type
312
+ end
294
313
  end
295
-
314
+
296
315
  # returns the first non-empty row in a sheet
297
316
  def first_row(sheet=nil)
298
317
  sheet = @default_sheet unless sheet
@@ -335,77 +354,52 @@ class Google < GenericSpreadsheet
335
354
 
336
355
  private
337
356
 
338
- # read all cells in a sheet
357
+ # read all cells in a sheet.
339
358
  def read_cells(sheet=nil)
340
359
  sheet = @default_sheet unless sheet
341
360
  raise RangeError, "illegal sheet <#{sheet}>" unless sheets.index(sheet)
342
361
  sheet_no = sheets.index(sheet)+1
343
- doc = @gs.fulldoc(sheet_no)
344
- (doc/"gs:cell").each {|item|
362
+ xml = @gs.fulldoc(sheet_no).to_s
363
+ doc = XML::Parser.string(xml).parse
364
+ doc.find("//*[local-name()='cell']").each do |item|
345
365
  row = item['row']
346
366
  col = item['col']
347
- value = item['inputvalue']
348
- numericvalue = item['numericvalue']
349
- if value[0,1] == '='
350
- formula = value
367
+ key = "#{row},#{col}"
368
+ string_value = item['inputvalue'] || item['inputValue']
369
+ numeric_value = item['numericvalue'] || item['numericValue']
370
+ (value, value_type) = determine_datatype(string_value, numeric_value)
371
+ @cell[sheet][key] = value unless value == "" or value == nil
372
+ @cell_type[sheet][key] = value_type
373
+ @formula[sheet] = {} unless @formula[sheet]
374
+ @formula[sheet][key] = string_value if value_type == :formula
375
+ end
376
+ @cells_read[sheet] = true
377
+ end
378
+
379
+ def determine_datatype(val, numval=nil)
380
+ if val[0,1] == '='
381
+ ty = :formula
382
+ if numeric?(numval)
383
+ val = numval.to_f
351
384
  else
352
- formula = nil
385
+ val = numval
353
386
  end
354
- @cell_type[sheet] = {} unless @cell_type[sheet]
355
- if formula
356
- ty = :formula
357
- if numeric?(numericvalue)
358
- value = numericvalue.to_f
359
- else
360
- value = numericvalue
361
- end
362
- elsif Google.date?(value)
363
- ty = :date
364
- elsif Google.datetime?(value)
387
+ else
388
+ if datetime?(val)
365
389
  ty = :datetime
366
- elsif numeric?(value) # or o.class ???
390
+ elsif date?(val)
391
+ ty = :date
392
+ elsif numeric?(val)
367
393
  ty = :float
368
- value = value.to_f
369
- elsif Google.time?(value)
394
+ val = val.to_f
395
+ elsif time?(val)
370
396
  ty = :time
371
- value = Google.timestring_to_seconds(value)
397
+ val = timestring_to_seconds(val)
372
398
  else
373
399
  ty = :string
374
400
  end
375
- key = "#{row},#{col}"
376
- @cell[sheet] = {} unless @cell[sheet]
377
- if ty == :date
378
- dd,mm,yyyy = value.split('/')
379
- @cell[sheet][key] = sprintf("%04d-%02d-%02d",yyyy.to_i,mm.to_i,dd.to_i)
380
- elsif ty == :datetime
381
- date_part,time_part = value.split(' ')
382
- dd,mm,yyyy = date_part.split('/')
383
- hh,mi,ss = time_part.split(':')
384
- @cell[sheet][key] = sprintf("%04d-%02d-%02d %02d:%02d:%02d",
385
- yyyy.to_i,mm.to_i,dd.to_i,hh.to_i,mi.to_i,ss.to_i)
386
- else
387
- @cell[sheet][key] = value unless value == "" or value == nil
388
- end
389
- @cell_type[sheet][key] = ty # Openoffice.oo_type_2_roo_type(vt)
390
- @formula[sheet] = {} unless @formula[sheet]
391
- @formula[sheet][key] = formula if formula
392
- }
393
- @cells_read[sheet] = true
401
+ end
402
+ return val, ty
394
403
  end
395
-
396
- def numeric?(string)
397
- string =~ /^[0-9]+[\.]*[0-9]*$/
398
- end
399
-
400
- # convert string DD/MM/YYYY into a Date-object
401
- #TODO: was ist mit verschiedenen Typen der Datumseingabe bei Google?
402
- def Google.to_date(string)
403
- if string.strip =~ /^([0-9]+)\/([0-9]+)\/([0-9]+)$/
404
- return Date.new($3.to_i,$2.to_i,$1.to_i)
405
- else
406
- return nil
407
- end
408
- end
409
-
410
-
404
+
411
405
  end # class
@@ -1,10 +1,9 @@
1
-
2
- require 'rubygems'
3
- require 'rexml/document'
1
+ require 'xml'
4
2
  require 'fileutils'
5
3
  require 'zip/zipfilesystem'
6
4
  require 'date'
7
5
  require 'base64'
6
+ require 'cgi'
8
7
 
9
8
  class Openoffice < GenericSpreadsheet
10
9
 
@@ -37,7 +36,7 @@ class Openoffice < GenericSpreadsheet
37
36
  @file_nr = @@nr
38
37
  extract_content
39
38
  file = File.new(File.join(@tmpdir, @file_nr.to_s+"_roo_content.xml"))
40
- @doc = REXML::Document.new file
39
+ @doc = XML::Parser.io(file).parse
41
40
  file.close
42
41
  ensure
43
42
  #if ENV["roo_local"] != "thomas-p"
@@ -56,6 +55,9 @@ class Openoffice < GenericSpreadsheet
56
55
  @last_row = Hash.new
57
56
  @first_column = Hash.new
58
57
  @last_column = Hash.new
58
+ @style = Hash.new
59
+ @style_defaults = Hash.new { |h,k| h[k] = [] }
60
+ @style_definitions = Hash.new
59
61
  @header_line = 1
60
62
  end
61
63
 
@@ -107,7 +109,32 @@ class Openoffice < GenericSpreadsheet
107
109
  row,col = normalize(row,col)
108
110
  formula(row,col) != nil
109
111
  end
112
+
113
+ class Font
114
+ attr_accessor :bold, :italic, :underline
115
+
116
+ def bold?
117
+ @bold == 'bold'
118
+ end
119
+
120
+ def italic?
121
+ @italic == 'italic'
122
+ end
123
+
124
+ def underline?
125
+ @underline != nil
126
+ end
127
+ end
110
128
 
129
+ # Given a cell, return the cell's style
130
+ def font(row, col, sheet=nil)
131
+ sheet = @default_sheet unless sheet
132
+ read_cells(sheet) unless @cells_read[sheet]
133
+ row,col = normalize(row,col)
134
+ style_name = @style[sheet][[row,col]] || @style_defaults[sheet][col - 1] || 'Default'
135
+ @style_definitions[style_name]
136
+ end
137
+
111
138
  # set a cell to a certain value
112
139
  # (this will not be saved back to the spreadsheet file!)
113
140
  def set(row,col,value,sheet=nil) #:nodoc:
@@ -145,31 +172,14 @@ class Openoffice < GenericSpreadsheet
145
172
  end
146
173
  end
147
174
 
148
- # returns an array of sheet names in the spreadsheet
149
175
  def sheets
150
176
  return_sheets = []
151
- oo_document_count = 0
152
- @doc.each_element do |oo_document|
153
- oo_document_count += 1
154
- oo_element_count = 0
155
- oo_document.each_element do |oo_element|
156
- oo_element_count += 1
157
- if oo_element.name == "body"
158
- oo_element.each_element do |be|
159
- if be.name == "spreadsheet"
160
- be.each_element do |se|
161
- if se.name == "table"
162
- return_sheets << se.attributes['name']
163
- end
164
- end
165
- end
166
- end
167
- end
168
- end
177
+ @doc.find("//*[local-name()='table']").each do |sheet|
178
+ return_sheets << sheet.attributes['name']
169
179
  end
170
180
  return_sheets
171
181
  end
172
-
182
+
173
183
  # version of the openoffice document
174
184
  # at 2007 this is always "1.0"
175
185
  def officeversion
@@ -211,32 +221,34 @@ class Openoffice < GenericSpreadsheet
211
221
 
212
222
  # read the version of the OO-Version
213
223
  def oo_version
214
- @doc.each_element do |oo_document|
215
- @officeversion = oo_document.attributes['version']
224
+ @doc.find("//*[local-name()='document-content']").each do |office|
225
+ @officeversion = office.attributes['version']
216
226
  end
217
227
  end
218
228
 
219
229
  # helper function to set the internal representation of cells
220
- def set_cell_values(sheet,x,y,i,v,vt,formula,tr,str_v)
221
- key = [y,x+i]
230
+ def set_cell_values(sheet,x,y,i,v,vt,formula,table_cell,str_v,style_name)
231
+ key = [y,x+i]
222
232
  @cell_type[sheet] = {} unless @cell_type[sheet]
223
233
  @cell_type[sheet][key] = Openoffice.oo_type_2_roo_type(vt)
224
234
  @formula[sheet] = {} unless @formula[sheet]
225
235
  @formula[sheet][key] = formula if formula
226
236
  @cell[sheet] = {} unless @cell[sheet]
237
+ @style[sheet] = {} unless @style[sheet]
238
+ @style[sheet][key] = style_name
227
239
  case @cell_type[sheet][key]
228
240
  when :float
229
241
  @cell[sheet][key] = v.to_f
230
242
  when :string
231
243
  @cell[sheet][key] = str_v
232
244
  when :date
233
- if tr.attributes['date-value'].size != "XXXX-XX-XX".size
245
+ if table_cell.attributes['date-value'].size != "XXXX-XX-XX".size
234
246
  #-- dann ist noch eine Uhrzeit vorhanden
235
247
  #-- "1961-11-21T12:17:18"
236
- @cell[sheet][key] = DateTime.parse(tr.attributes['date-value'])
248
+ @cell[sheet][key] = DateTime.parse(table_cell.attributes['date-value'])
237
249
  @cell_type[sheet][key] = :datetime
238
250
  else
239
- @cell[sheet][key] = tr.attributes['date-value']
251
+ @cell[sheet][key] = table_cell.attributes['date-value']
240
252
  end
241
253
  when :percentage
242
254
  @cell[sheet][key] = v.to_f
@@ -258,130 +270,115 @@ class Openoffice < GenericSpreadsheet
258
270
  sheet_found = false
259
271
  raise ArgumentError, "Error: sheet '#{sheet||'nil'}' not valid" if @default_sheet == nil and sheet==nil
260
272
  raise RangeError unless self.sheets.include? sheet
261
- oo_document_count = 0
262
- @doc.each_element do |oo_document|
263
- # @officeversion = oo_document.attributes['version']
264
- oo_document_count += 1
265
- oo_element_count = 0
266
- oo_document.each_element do |oo_element|
267
- oo_element_count += 1
268
- if oo_element.name == "body"
269
- oo_element.each_element do |be|
270
- if be.name == "spreadsheet"
271
- be.each_element do |se|
272
- if se.name == "table"
273
- if se.attributes['name']==sheet
274
- sheet_found = true
275
- x=1
276
- y=1
277
- se.each_element do |te|
278
- if te.name == "table-column"
279
- rep = te.attributes["number-columns-repeated"]
280
- elsif te.name == "table-row"
281
- if te.attributes['number-rows-repeated']
282
- skip_y = te.attributes['number-rows-repeated'].to_i
283
- y = y + skip_y - 1 # minus 1 because this line will be counted as a line element
284
- end
285
- te.each_element do |tr|
286
- if tr.name == 'table-cell'
287
- skip = tr.attributes['number-columns-repeated']
288
- formula = tr.attributes['formula']
289
- vt = tr.attributes['value-type']
290
- v = tr.attributes['value']
291
- if vt == 'string'
292
- str_v = ''
293
- # insert \n if there is more than one paragraph
294
- para_count = 0
295
- tr.each_element do |str|
296
- if str.name == 'p'
297
- v = str.text
298
- str_v += "\n" if para_count > 0
299
- para_count += 1
300
- if str.children.size > 1
301
- str_v = children_to_string(str.children)
302
- else
303
- str.children.each {|child|
304
- str_v = str_v + child.to_s #.text
305
- }
306
- end
307
- str_v.gsub!(/&quot;/,'"')
308
- str_v.gsub!(/&amp;/,'&')
309
- str_v.gsub!(/&apos;/,"'")
310
- end # == 'p'
311
- end
312
- elsif vt == 'time'
313
- tr.each_element do |str|
314
- if str.name == 'p'
315
- v = str.text
316
- end
317
- end
318
- elsif vt == '' or vt == nil
319
- #
320
- elsif vt == 'date'
321
- #
322
- elsif vt == 'percentage'
323
- #
324
- elsif vt == 'float'
325
- #
326
- elsif vt == 'boolean'
327
- #
328
- else
329
- # raise "unknown type #{vt}"
330
- end
331
- if skip
332
- if v != nil or tr.attributes['date-value']
333
- 0.upto(skip.to_i-1) do |i|
334
- set_cell_values(sheet,x,y,i,v,vt,formula,tr,str_v)
335
- end
336
- end
337
- x += (skip.to_i - 1)
338
- end # if skip
339
- set_cell_values(sheet,x,y,0,v,vt,formula,tr,str_v)
340
- x += 1
341
- end
342
- end
343
- y += 1
344
- x = 1
273
+
274
+ @doc.find("//*[local-name()='table']").each do |ws|
275
+ if sheet == ws.attributes['name']
276
+ sheet_found = true
277
+ col = 1
278
+ row = 1
279
+ ws.each_element do |table_element|
280
+ case table_element.name
281
+ when 'table-column'
282
+ @style_defaults[sheet] << table_element.attributes['default-cell-style-name']
283
+ when 'table-row'
284
+ if table_element.attributes['number-rows-repeated']
285
+ skip_row = table_element.attributes['number-rows-repeated'].to_i
286
+ row = row + skip_row - 1
287
+ end
288
+ table_element.each_element do |cell|
289
+ skip_col = cell.attributes['number-columns-repeated']
290
+ formula = cell.attributes['formula']
291
+ vt = cell.attributes['value-type']
292
+ v = cell.attributes['value']
293
+ style_name = cell.attributes['style-name']
294
+ if vt == 'string'
295
+ str_v = ''
296
+ # insert \n if there is more than one paragraph
297
+ para_count = 0
298
+ cell.each_element do |str|
299
+ if str.name == 'p'
300
+ v = str.content
301
+ str_v += "\n" if para_count > 0
302
+ para_count += 1
303
+ if str.children.size > 1
304
+ str_v += children_to_string(str.children)
305
+ else
306
+ str.children.each do |child|
307
+ str_v += child.content #.text
345
308
  end
346
309
  end
347
- end # sheet
310
+ str_v.gsub!(/&apos;/,"'") # special case not supported by unescapeHTML
311
+ str_v = CGI.unescapeHTML(str_v)
312
+ end # == 'p'
313
+ end
314
+ elsif vt == 'time'
315
+ cell.each_element do |str|
316
+ if str.name == 'p'
317
+ v = str.content
318
+ end
348
319
  end
320
+ elsif vt == '' or vt == nil
321
+ #
322
+ elsif vt == 'date'
323
+ #
324
+ elsif vt == 'percentage'
325
+ #
326
+ elsif vt == 'float'
327
+ #
328
+ elsif vt == 'boolean'
329
+ v = cell.attributes['boolean-value']
330
+ #
331
+ else
332
+ # raise "unknown type #{vt}"
349
333
  end
350
- end
334
+ if skip_col
335
+ if v != nil or cell.attributes['date-value']
336
+ 0.upto(skip_col.to_i-1) do |i|
337
+ set_cell_values(sheet,col,row,i,v,vt,formula,cell,str_v,style_name)
338
+ end
339
+ end
340
+ col += (skip_col.to_i - 1)
341
+ end # if skip
342
+ set_cell_values(sheet,col,row,0,v,vt,formula,cell,str_v,style_name)
343
+ col += 1
344
+ end
345
+ row += 1
346
+ col = 1
351
347
  end
352
348
  end
353
349
  end
354
350
  end
351
+
352
+ @doc.find("//*[local-name()='automatic-styles']").each do |style|
353
+ read_styles(style)
354
+ end
355
355
  if !sheet_found
356
356
  raise RangeError
357
357
  end
358
358
  @cells_read[sheet] = true
359
359
  end
360
360
 
361
+ def read_styles(style_elements)
362
+ @style_definitions['Default'] = Openoffice::Font.new
363
+ style_elements.each do |style|
364
+ next unless style.name == 'style'
365
+ style_name = style.attributes['name']
366
+ style.each do |properties|
367
+ font = Openoffice::Font.new
368
+ font.bold = properties.attributes['font-weight']
369
+ font.italic = properties.attributes['font-style']
370
+ font.underline = properties.attributes['text-underline-style']
371
+ @style_definitions[style_name] = font
372
+ end
373
+ end
374
+ end
375
+
361
376
  # Checks if the default_sheet exists. If not an RangeError exception is
362
377
  # raised
363
378
  def check_default_sheet
364
379
  sheet_found = false
365
380
  raise ArgumentError, "Error: default_sheet not set" if @default_sheet == nil
366
- @doc.each_element do |oo_document|
367
- oo_element_count = 0
368
- oo_document.each_element do |oo_element|
369
- oo_element_count += 1
370
- if oo_element.name == "body"
371
- oo_element.each_element do |be|
372
- if be.name == "spreadsheet"
373
- be.each_element do |se|
374
- if se.name == "table"
375
- if se.attributes['name'] == @default_sheet
376
- sheet_found = true
377
- end # sheet
378
- end
379
- end
380
- end
381
- end
382
- end
383
- end
384
- end
381
+ sheet_found = true if sheets.include?(@default_sheet)
385
382
  if ! sheet_found
386
383
  raise RangeError, "sheet '#{@default_sheet}' not found"
387
384
  end
@@ -437,8 +434,8 @@ class Openoffice < GenericSpreadsheet
437
434
  def children_to_string(children)
438
435
  result = ''
439
436
  children.each {|child|
440
- if child.class == REXML::Text
441
- result = result + child.to_s
437
+ if child.text?
438
+ result = result + child.content
442
439
  else
443
440
  if child.name == 's'
444
441
  compressed_spaces = child.attributes['c'].to_i
@@ -448,7 +445,7 @@ class Openoffice < GenericSpreadsheet
448
445
  end
449
446
  result = result + " "*compressed_spaces
450
447
  else
451
- result = result + child.to_s
448
+ result = result + child.content
452
449
  end
453
450
  end
454
451
  }