├── .gitignore
├── VERSION.yml
├── test
├── bbu.ods
├── bbu.xls
├── ric.ods
├── bbu.xlsx
├── no_spreadsheet_file.txt
├── style.ods
├── style.xls
├── time-test.csv
├── boolean.ods
├── boolean.xls
├── boolean.xlsx
├── borders.ods
├── borders.xls
├── borders.xlsx
├── datetime.ods
├── datetime.xls
├── formula.ods
├── formula.xls
├── formula.xlsx
├── numbers1.ods
├── numbers1.xls
├── style.xlsx
├── 1900_base.xls
├── 1904_base.xls
├── Bibelbund.ods
├── Bibelbund.xls
├── Bibelbund.xlsx
├── Bibelbund1.ods
├── datetime.xlsx
├── numbers1.xlsx
├── paragraph.ods
├── paragraph.xls
├── paragraph.xlsx
├── time-test.ods
├── time-test.xls
├── time-test.xlsx
├── whitespace.ods
├── whitespace.xls
├── bode-v1.ods.zip
├── bode-v1.xls.zip
├── emptysheets.ods
├── emptysheets.xls
├── html-escape.ods
├── whitespace.xlsx
├── bad_excel_date.xls
├── false_encoding.xls
├── only_one_sheet.ods
├── only_one_sheet.xls
├── only_one_sheet.xlsx
├── datetime_floatconv.xls
├── simple_spreadsheet.ods
├── simple_spreadsheet.xls
├── formula_parse_error.xls
├── simple_spreadsheet.xlsx
├── bug-row-column-fixnum-float.xls
├── simple_spreadsheet_from_italo.ods
├── simple_spreadsheet_from_italo.xls
├── test_helper.rb
├── numbers1.csv
├── only_one_sheet.xml
├── boolean.xml
├── emptysheets.xml
├── bug-row-column-fixnum-float.xml
├── formula.xml
├── time-test.xml
├── paragraph.xml
├── datetime_floatconv.xml
├── borders.xml
├── false_encoding.xml
├── style.xml
├── datetime.xml
├── bbu.xml
├── whitespace.xml
├── simple_spreadsheet.xml
├── simple_spreadsheet_from_italo.xml
└── numbers1.xml
├── lib
├── roo
│ ├── version.rb
│ ├── roo_rails_helper.rb
│ ├── google.rb
│ ├── excel2003xml.rb
│ ├── excel.rb
│ └── openoffice.rb
└── roo.rb
├── examples
├── roo_soap_server.rb
├── write_me.rb
└── roo_soap_client.rb
├── License.txt
├── Rakefile
├── website
├── template.rhtml
└── stylesheets
│ └── screen.css
├── Manifest.txt
├── scripts
└── txt2html
├── README.markdown
├── roo.gemspec
├── History.txt
└── base64include.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | pkg/
2 |
--------------------------------------------------------------------------------
/VERSION.yml:
--------------------------------------------------------------------------------
1 | ---
2 | :minor: 4
3 | :patch: 0
4 | :major: 1
5 |
--------------------------------------------------------------------------------
/test/bbu.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/bbu.ods
--------------------------------------------------------------------------------
/test/bbu.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/bbu.xls
--------------------------------------------------------------------------------
/test/ric.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/ric.ods
--------------------------------------------------------------------------------
/test/bbu.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/bbu.xlsx
--------------------------------------------------------------------------------
/test/no_spreadsheet_file.txt:
--------------------------------------------------------------------------------
1 | this is intentionally not a spreadsheet file
2 |
--------------------------------------------------------------------------------
/test/style.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/style.ods
--------------------------------------------------------------------------------
/test/style.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/style.xls
--------------------------------------------------------------------------------
/test/time-test.csv:
--------------------------------------------------------------------------------
1 | "Mittags:",12:13:14,15:16:00,23:00:00
2 | 2007-11-21,,,
3 |
--------------------------------------------------------------------------------
/test/boolean.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/boolean.ods
--------------------------------------------------------------------------------
/test/boolean.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/boolean.xls
--------------------------------------------------------------------------------
/test/boolean.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/boolean.xlsx
--------------------------------------------------------------------------------
/test/borders.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/borders.ods
--------------------------------------------------------------------------------
/test/borders.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/borders.xls
--------------------------------------------------------------------------------
/test/borders.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/borders.xlsx
--------------------------------------------------------------------------------
/test/datetime.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/datetime.ods
--------------------------------------------------------------------------------
/test/datetime.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/datetime.xls
--------------------------------------------------------------------------------
/test/formula.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/formula.ods
--------------------------------------------------------------------------------
/test/formula.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/formula.xls
--------------------------------------------------------------------------------
/test/formula.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/formula.xlsx
--------------------------------------------------------------------------------
/test/numbers1.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/numbers1.ods
--------------------------------------------------------------------------------
/test/numbers1.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/numbers1.xls
--------------------------------------------------------------------------------
/test/style.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/style.xlsx
--------------------------------------------------------------------------------
/test/1900_base.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/1900_base.xls
--------------------------------------------------------------------------------
/test/1904_base.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/1904_base.xls
--------------------------------------------------------------------------------
/test/Bibelbund.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/Bibelbund.ods
--------------------------------------------------------------------------------
/test/Bibelbund.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/Bibelbund.xls
--------------------------------------------------------------------------------
/test/Bibelbund.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/Bibelbund.xlsx
--------------------------------------------------------------------------------
/test/Bibelbund1.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/Bibelbund1.ods
--------------------------------------------------------------------------------
/test/datetime.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/datetime.xlsx
--------------------------------------------------------------------------------
/test/numbers1.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/numbers1.xlsx
--------------------------------------------------------------------------------
/test/paragraph.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/paragraph.ods
--------------------------------------------------------------------------------
/test/paragraph.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/paragraph.xls
--------------------------------------------------------------------------------
/test/paragraph.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/paragraph.xlsx
--------------------------------------------------------------------------------
/test/time-test.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/time-test.ods
--------------------------------------------------------------------------------
/test/time-test.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/time-test.xls
--------------------------------------------------------------------------------
/test/time-test.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/time-test.xlsx
--------------------------------------------------------------------------------
/test/whitespace.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/whitespace.ods
--------------------------------------------------------------------------------
/test/whitespace.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/whitespace.xls
--------------------------------------------------------------------------------
/test/bode-v1.ods.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/bode-v1.ods.zip
--------------------------------------------------------------------------------
/test/bode-v1.xls.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/bode-v1.xls.zip
--------------------------------------------------------------------------------
/test/emptysheets.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/emptysheets.ods
--------------------------------------------------------------------------------
/test/emptysheets.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/emptysheets.xls
--------------------------------------------------------------------------------
/test/html-escape.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/html-escape.ods
--------------------------------------------------------------------------------
/test/whitespace.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/whitespace.xlsx
--------------------------------------------------------------------------------
/test/bad_excel_date.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/bad_excel_date.xls
--------------------------------------------------------------------------------
/test/false_encoding.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/false_encoding.xls
--------------------------------------------------------------------------------
/test/only_one_sheet.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/only_one_sheet.ods
--------------------------------------------------------------------------------
/test/only_one_sheet.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/only_one_sheet.xls
--------------------------------------------------------------------------------
/test/only_one_sheet.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/only_one_sheet.xlsx
--------------------------------------------------------------------------------
/test/datetime_floatconv.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/datetime_floatconv.xls
--------------------------------------------------------------------------------
/test/simple_spreadsheet.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/simple_spreadsheet.ods
--------------------------------------------------------------------------------
/test/simple_spreadsheet.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/simple_spreadsheet.xls
--------------------------------------------------------------------------------
/test/formula_parse_error.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/formula_parse_error.xls
--------------------------------------------------------------------------------
/test/simple_spreadsheet.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/simple_spreadsheet.xlsx
--------------------------------------------------------------------------------
/test/bug-row-column-fixnum-float.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/bug-row-column-fixnum-float.xls
--------------------------------------------------------------------------------
/test/simple_spreadsheet_from_italo.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/simple_spreadsheet_from_italo.ods
--------------------------------------------------------------------------------
/test/simple_spreadsheet_from_italo.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shigeya/roo/master/test/simple_spreadsheet_from_italo.xls
--------------------------------------------------------------------------------
/lib/roo/version.rb:
--------------------------------------------------------------------------------
1 | module Roo #:nodoc:
2 | module VERSION #:nodoc:
3 | MAJOR = 1
4 | MINOR = 3
5 | TINY = 9
6 |
7 | STRING = [MAJOR, MINOR, TINY].join('.')
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | require 'test/unit'
2 | require File.dirname(__FILE__) + '/../lib/roo'
3 |
4 | # helper method
5 | def after(d)
6 | yield if DateTime.now > d
7 | end
8 |
9 | # helper method
10 | def before(d)
11 | yield if DateTime.now <= d
12 | end
13 |
14 | # helper method
15 | def local_only
16 | if ENV["roo_local"] == "thomas-p"
17 | yield
18 | end
19 | end
--------------------------------------------------------------------------------
/test/numbers1.csv:
--------------------------------------------------------------------------------
1 | 1,2,3,4,10,,
2 | 5,6,7,8,9,"test",11
3 | ,,,,,,
4 | 10,11,12,13,14,,
5 | 1961-11-21,,,,,,
6 | "tata",,,,,,
7 | ,,,,,,
8 | ,,"thisisc8",,,,
9 | ,,,"thisisd9",,,
10 | ,,,,,,
11 | "thisisa11",,,,,,
12 | 41,42,43,44,45,,
13 | ,,,,,,
14 | ,,,,,,
15 | 41,42,43,44,45,,
16 | "einundvierzig","zweiundvierzig","dreiundvierzig","vierundvierzig","fuenfundvierzig",,
17 | ,,,,,,
18 | 2007-05-31,"dies hier als Date-Objekt",,,,,
19 |
--------------------------------------------------------------------------------
/lib/roo.rb:
--------------------------------------------------------------------------------
1 | module Roo
2 | class Spreadsheet
3 | class << self
4 | def open(file)
5 | case File.extname(file)
6 | when '.xls'
7 | Excel.new(file)
8 | when '.xlsx'
9 | Excelx.new(file)
10 | when '.ods'
11 | Openoffice.new(file)
12 | when '.xml'
13 | Excel2003XML.new(file)
14 | when ''
15 | Google.new(file)
16 | else
17 | raise ArgumentError, "Don't know how to open file #{file}"
18 | end
19 | end
20 | end
21 | end
22 | end
23 |
24 | require 'roo/version'
25 | # require 'roo/spreadsheetparser' TODO:
26 | require 'roo/generic_spreadsheet'
27 | require 'roo/openoffice'
28 | require 'roo/excel'
29 | require 'roo/excelx'
30 | require 'roo/google'
31 | require 'roo/excel2003xml'
32 | require 'roo/roo_rails_helper'
33 |
--------------------------------------------------------------------------------
/examples/roo_soap_server.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'roo'
3 | require 'soap/rpc/standaloneServer'
4 |
5 | NS = "spreadsheetserver" # name of your service = namespace
6 | class Server2 < SOAP::RPC::StandaloneServer
7 |
8 | def on_init
9 | spreadsheet = Openoffice.new("./Ferien-de.ods")
10 | add_method(spreadsheet, 'cell', 'row', 'col')
11 | add_method(spreadsheet, 'officeversion')
12 | add_method(spreadsheet, 'first_row')
13 | add_method(spreadsheet, 'last_row')
14 | add_method(spreadsheet, 'first_column')
15 | add_method(spreadsheet, 'last_column')
16 | add_method(spreadsheet, 'sheets')
17 | #add_method(spreadsheet, 'default_sheet=', 's')
18 | # method with '...=' did not work? alias method 'set_default_sheet' created
19 | add_method(spreadsheet, 'set_default_sheet', 's')
20 | end
21 |
22 | end
23 |
24 | PORT = 12321
25 | puts "serving at port #{PORT}"
26 | svr = Server2.new('Roo', NS, '0.0.0.0', PORT)
27 |
28 | trap('INT') { svr.shutdown }
29 | svr.start
30 |
--------------------------------------------------------------------------------
/License.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2007 Thomas Preymesser
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/examples/write_me.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'roo'
3 |
4 | #-- create a new spreadsheet within your google-spreadsheets and paste
5 | #-- the 'key' parameter in the spreadsheet URL
6 | MAXTRIES = 1000
7 | print "what's your name? "
8 | my_name = gets.chomp
9 | print "where do you live? "
10 | my_location = gets.chomp
11 | print "your message? (if left blank, only your name and location will be inserted) "
12 | my_message = gets.chomp
13 | spreadsheet = Google.new('ptu6bbahNZpY0N0RrxQbWdw')
14 | spreadsheet.default_sheet = 'Sheet1'
15 | success = false
16 | MAXTRIES.times do
17 | col = rand(10)+1
18 | row = rand(10)+1
19 | if spreadsheet.empty?(row,col)
20 | if my_message.empty?
21 | text = Time.now.to_s+" "+"Greetings from #{my_name} (#{my_location})"
22 | else
23 | text = Time.now.to_s+" "+"#{my_message} from #{my_name} (#{my_location})"
24 | end
25 | spreadsheet.set_value(row,col,text)
26 | puts "message written to row #{row}, column #{col}"
27 | success = true
28 | break
29 | end
30 | puts "Row #{row}, column #{col} already occupied, trying again..."
31 | end
32 | puts "no empty cell found within #{MAXTRIES} tries" if !success
33 |
34 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | $:.unshift('lib')
2 |
3 | require 'rake/testtask'
4 |
5 | begin
6 | require 'jeweler'
7 | Jeweler::Tasks.new do |s|
8 | s.name = 'roo'
9 | s.rubyforge_project = 'roo'
10 | s.platform = Gem::Platform::RUBY
11 | s.email = 'hugh_mcgowan@yahoo.com'
12 | s.homepage = "http://roo.rubyforge.org"
13 | s.summary = "roo"
14 | s.description = "roo can access the contents of OpenOffice-, Excel- or Google-Spreadsheets"
15 | s.authors = ['Hugh McGowan','Thomas Preymesser']
16 | s.files = FileList[ "{lib,test}/**/*"]
17 | s.has_rdoc = true
18 | s.extra_rdoc_files = ["README.markdown", "History.txt"]
19 | s.rdoc_options = ["--main","README.markdown"]
20 | s.add_dependency "spreadsheet", [">= 0.6.4"]
21 | s.add_dependency "rubyzip", [">= 0.9.1"]
22 | s.add_dependency "hpricot", [">= 0.6"]
23 | s.add_dependency "GData", [">= 0.0.4"]
24 | s.add_dependency "libxml-ruby", [">= 1.1.3"]
25 | end
26 | rescue LoadError
27 | puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
28 | end
29 |
30 | Rake::TestTask.new do |t|
31 | t.libs << "test"
32 | t.test_files = FileList['test/test_roo.rb']
33 | t.verbose = true
34 | end
35 |
36 |
37 | task :default => :test
38 |
--------------------------------------------------------------------------------
/website/template.rhtml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 | <%= title %>
9 |
10 |
11 |
14 |
29 |
30 |
31 |
32 |
33 |
<%= title %>
34 |
38 | <%= body %>
39 |
40 | Thomas Preymesser , <%= modified.pretty %>
41 | Theme extended from Paul Battley
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/Manifest.txt:
--------------------------------------------------------------------------------
1 | History.txt
2 | License.txt
3 | Manifest.txt
4 | README.txt
5 | Rakefile
6 | base64include.rb
7 | examples/roo_soap_server.rb
8 | examples/roo_soap_client.rb
9 | examples/write_me.rb
10 | lib/roo.rb
11 | lib/roo/version.rb
12 | lib/roo/generic_spreadsheet.rb
13 | lib/roo/openoffice.rb
14 | lib/roo/excel.rb
15 | lib/roo/excelx.rb
16 | lib/roo/google.rb
17 | lib/roo/roo_rails_helper.rb
18 | scripts/txt2html
19 | setup.rb
20 | test/false_encoding.xls
21 | test/Bibelbund1.ods
22 | test/Bibelbund.ods
23 | test/Bibelbund.xls
24 | test/Bibelbund.xlsx
25 | test/Bibelbund.csv
26 | test/bbu.xls
27 | test/bbu.xlsx
28 | test/bbu.ods
29 | test/no_spreadsheet_file.txt
30 | test/simple_spreadsheet.ods
31 | test/simple_spreadsheet.xls
32 | test/simple_spreadsheet.xlsx
33 | test/simple_spreadsheet_from_italo.ods
34 | test/simple_spreadsheet_from_italo.xls
35 | test/test_helper.rb
36 | test/test_roo.rb
37 | test/time-test.ods
38 | test/time-test.xls
39 | test/time-test.xlsx
40 | test/time-test.csv
41 | test/numbers1.csv
42 | test/numbers1_excel.csv
43 | test/numbers1.ods
44 | test/numbers1.xls
45 | test/numbers1.xlsx
46 | test/borders.ods
47 | test/borders.xls
48 | test/borders.xlsx
49 | test/formula.ods
50 | test/formula.xls
51 | test/formula.xlsx
52 | test/only_one_sheet.ods
53 | test/only_one_sheet.xls
54 | test/only_one_sheet.xlsx
55 | test/bode-v1.xls.zip
56 | test/bode-v1.ods.zip
57 | test/ric.ods
58 | test/bug-row-column-fixnum-float.xls
59 | test/emptysheets.ods
60 | test/emptysheets.xls
61 | test/datetime.ods
62 | test/datetime.xls
63 | test/datetime.xlsx
64 | website/index.html
65 | website/index.txt
66 | website/javascripts/rounded_corners_lite.inc.js
67 | website/stylesheets/screen.css
68 | website/template.rhtml
69 |
--------------------------------------------------------------------------------
/examples/roo_soap_client.rb:
--------------------------------------------------------------------------------
1 | require 'soap/rpc/driver'
2 |
3 | def ferien_fuer_region(proxy, region, year=nil)
4 | proxy.first_row.upto(proxy.last_row) { |row|
5 | if proxy.cell(row, 2) == region
6 | jahr = proxy.cell(row,1).to_i
7 | if year == nil || jahr == year
8 | bis_datum = proxy.cell(row,5)
9 | if DateTime.now > bis_datum
10 | print '('
11 | end
12 | print jahr.to_s+" "
13 | print proxy.cell(row,2)+" "
14 | print proxy.cell(row,3)+" "
15 | print proxy.cell(row,4).to_s+" "
16 | print bis_datum.to_s+" "
17 | print (proxy.cell(row,6) || '')+" "
18 | if DateTime.now > bis_datum
19 | print ')'
20 | end
21 | puts
22 | end
23 | end
24 | }
25 | end
26 |
27 | proxy = SOAP::RPC::Driver.new("http://localhost:12321","spreadsheetserver")
28 | proxy.add_method('cell','row','col')
29 | proxy.add_method('officeversion')
30 | proxy.add_method('last_row')
31 | proxy.add_method('last_column')
32 | proxy.add_method('first_row')
33 | proxy.add_method('first_column')
34 | proxy.add_method('sheets')
35 | proxy.add_method('set_default_sheet','s')
36 | proxy.add_method('ferien_fuer_region', 'region')
37 |
38 | sheets = proxy.sheets
39 | proxy.set_default_sheet(sheets.first)
40 |
41 | puts "first row: #{proxy.first_row}"
42 | puts "first column: #{proxy.first_column}"
43 | puts "last row: #{proxy.last_row}"
44 | puts "last column: #{proxy.last_column}"
45 | puts "cell: #{proxy.cell('C',8)}"
46 | puts "cell: #{proxy.cell('F',12)}"
47 | puts "officeversion: #{proxy.officeversion}"
48 | puts "Berlin:"
49 |
50 | ferien_fuer_region(proxy, "Berlin")
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/scripts/txt2html:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'rubygems'
4 | require 'redcloth'
5 | require 'syntax/convertors/html'
6 | require 'erb'
7 | require File.dirname(__FILE__) + '/../lib/roo/version.rb'
8 |
9 | version = Roo::VERSION::STRING
10 | download = 'http://rubyforge.org/projects/roo'
11 |
12 | class Fixnum
13 | def ordinal
14 | # teens
15 | return 'th' if (10..19).include?(self % 100)
16 | # others
17 | case self % 10
18 | when 1: return 'st'
19 | when 2: return 'nd'
20 | when 3: return 'rd'
21 | else return 'th'
22 | end
23 | end
24 | end
25 |
26 | class Time
27 | def pretty
28 | return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
29 | end
30 | end
31 |
32 | def convert_syntax(syntax, source)
33 | return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^| $!,'')
34 | end
35 |
36 | if ARGV.length >= 1
37 | src, template = ARGV
38 | template ||= File.dirname(__FILE__) + '/../website/template.rhtml'
39 |
40 | else
41 | puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
42 | exit!
43 | end
44 |
45 | template = ERB.new(File.open(template).read)
46 |
47 | title = nil
48 | body = nil
49 | File.open(src) do |fsrc|
50 | title_text = fsrc.readline
51 | body_text = fsrc.read
52 | syntax_items = []
53 | body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)>!m){
54 | ident = syntax_items.length
55 | element, syntax, source = $1, $2, $3
56 | syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}#{element}>"
57 | "syntax-temp-#{ident}"
58 | }
59 | title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
60 | body = RedCloth.new(body_text).to_html
61 | body.gsub!(%r!(?:)?syntax-temp-(d+)(?: )?!){ syntax_items[$1.to_i] }
62 | end
63 | stat = File.stat(src)
64 | created = stat.ctime
65 | modified = stat.mtime
66 |
67 | $stdout << template.result(binding)
68 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | I'm no longer maintaining this code. It's under active development here and new releases should be coming from that repository:
2 |
3 | https://github.com/Empact/roo
4 |
5 |
6 |
7 |
8 | # README for Roo
9 |
10 | Roo is available here and on Rubyforge. You can install the official release with 'gem install roo' or refer to the installation instructions below for the latest development gem.
11 |
12 | Homepage: http://roo.rubyforge.org/
13 |
14 | Gemcutter: http://rubygems.org/gems/roo
15 |
16 | ## Installation
17 |
18 | # Run the following if you haven't done so before:
19 | gem sources -a http://gems.github.com/
20 |
21 | # Install the gem:
22 | sudo gem install roo
23 |
24 | ## Usage:
25 |
26 | require 'rubygems'
27 | require 'roo'
28 |
29 | s = Openoffice.new("myspreadsheet.ods") # creates an Openoffice Spreadsheet instance
30 | s = Excel.new("myspreadsheet.xls") # creates an Excel Spreadsheet instance
31 | s = Google.new("myspreadsheetkey_at_google") # creates an Google Spreadsheet instance
32 | s = Excelx.new("myspreadsheet.xlsx") # creates an Excel Spreadsheet instance for Excel .xlsx files
33 |
34 | s.default_sheet = s.sheets.first # first sheet in the spreadsheet file will be used
35 |
36 | # s.sheet is an array which holds the names of the sheets within
37 | # a spreadsheet.
38 | # you can also write
39 | # s.default_sheet = s.sheets[3] or
40 | # s.default_sheet = 'Sheet 3'
41 |
42 | s.cell(1,1) # returns the content of the first row/first cell in the sheet
43 | s.cell('A',1) # same cell
44 | s.cell(1,'A') # same cell
45 | s.cell(1,'A',s.sheets[0]) # same cell
46 |
47 | # almost all methods have an optional argument 'sheet'.
48 | # If this parameter is omitted, the default_sheet will be used.
49 |
50 | s.info # prints infos about the spreadsheet file
51 |
52 | s.first_row # the number of the first row
53 | s.last_row # the number of the last row
54 | s.first_column # the number of the first column
55 | s.last_column # the number of the last column
56 |
57 | # limited font information is available
58 |
59 | s.font(1,1).bold?
60 | s.font(1,1).italic?
61 | s.font(1,1).underline?
62 |
63 |
64 | see http://roo.rubyforge.org for a more complete tutorial
65 |
66 |
--------------------------------------------------------------------------------
/test/only_one_sheet.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:40:00Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 395
19 | False
20 | False
21 |
22 |
23 |
31 |
32 |
33 |
35 |
36 | 42 |
37 | 43 |
38 | 44 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
48 |
49 |
50 |
51 | 9
52 | 300
53 | 300
54 |
55 |
56 |
57 |
58 | 3
59 | 4
60 | 3
61 |
62 |
63 | False
64 | False
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/lib/roo/roo_rails_helper.rb:
--------------------------------------------------------------------------------
1 | def spreadsheet(spreadsheet, sheets, options={})
2 | @rspreadsheet = spreadsheet
3 | coordinates = true # default
4 | o=""
5 | if options[:coordinates] != nil
6 | o << ":coordinates uebergeben: #{options[:coordinates]}"
7 | coordinates = options[:coordinates]
8 | end
9 | if options[:bgcolor]
10 | bgcolor = options[:bgcolor]
11 | else
12 | bgcolor = false
13 | end
14 |
15 | sheets.each { |sheet|
16 | @rspreadsheet.default_sheet = sheet
17 | linenumber = @rspreadsheet.first_row(sheet)
18 | if options[:first_row]
19 | linenumber += (options[:first_row]-1)
20 | end
21 | o << ''
22 | if options[:first_row]
23 | first_row = options[:first_row]
24 | end
25 | if options[:last_row]
26 | last_row = options[:last_row]
27 | end
28 | if options[:first_column]
29 | first_column = options[:first_column]
30 | end
31 | if options[:last_column]
32 | last_column = options[:last_column]
33 | end
34 | first_row = @rspreadsheet.first_row(sheet) unless first_row
35 | last_row = @rspreadsheet.last_row(sheet) unless last_row
36 | first_column = @rspreadsheet.first_column(sheet) unless first_column
37 | last_column = @rspreadsheet.last_column(sheet) unless last_column
38 | if coordinates
39 | o << " "
40 | o << " "
41 | @rspreadsheet.first_column(sheet).upto(@rspreadsheet.last_column(sheet)) {|c|
42 | if c < first_column or c > last_column
43 | next
44 | end
45 | o << " "
46 | o << " #{GenericSpreadsheet.number_to_letter(c)} "
47 | o << " "
48 | }
49 | o << " "
50 | end
51 | @rspreadsheet.first_row.upto(@rspreadsheet.last_row) do |y|
52 | if first_row and (y < first_row or y > last_row)
53 | next
54 | end
55 | o << ""
56 | if coordinates
57 | o << "#{linenumber.to_s} "
58 | end
59 | linenumber += 1
60 | @rspreadsheet.first_column(sheet).upto(@rspreadsheet.last_column(sheet)) do |x|
61 | if x < first_column or x > last_column
62 | next
63 | end
64 | if bgcolor
65 | o << ""
66 | else
67 | o << ' '
68 | end
69 | if @rspreadsheet.cell(y,x).to_s.empty?
70 | o << " "
71 | else
72 | o << "#{@rspreadsheet.cell(y,x)}"
73 | end
74 | o << " "
75 | end
76 | o << " "
77 | end
78 | o << "
"
79 | } # each sheet
80 | return o
81 | end
82 |
83 |
--------------------------------------------------------------------------------
/website/stylesheets/screen.css:
--------------------------------------------------------------------------------
1 | body {
2 | /*background-color: #E1D1F1;*/
3 | background-image: url(../spreadsheetbackground.png);
4 | font-family: "Georgia", sans-serif;
5 | font-size: 16px;
6 | line-height: 1.6em;
7 | padding: 1.6em 0 0 0;
8 | color: #333;
9 | }
10 | h1, h2, h3, h4, h5, h6 {
11 | color: #444;
12 | }
13 | h1 {
14 | font-family: sans-serif;
15 | font-weight: normal;
16 | font-size: 4em;
17 | line-height: 0.8em;
18 | letter-spacing: -0.1ex;
19 | margin: 5px;
20 | }
21 | li {
22 | padding: 0;
23 | margin: 0;
24 | list-style-type: square;
25 | }
26 | a {
27 | color: #5E5AFF;
28 | background-color: #DAC;
29 | font-weight: normal;
30 | text-decoration: underline;
31 | }
32 | blockquote {
33 | font-size: 90%;
34 | font-style: italic;
35 | border-left: 1px solid #111;
36 | padding-left: 1em;
37 | }
38 | .caps {
39 | font-size: 80%;
40 | }
41 |
42 | #main {
43 | width: 45em;
44 | padding: 0;
45 | margin: 0 auto;
46 | }
47 | .coda {
48 | text-align: right;
49 | color: #77f;
50 | font-size: smaller;
51 | }
52 |
53 | table {
54 | font-size: 90%;
55 | line-height: 1.4em;
56 | color: #ff8;
57 | background-color: #111;
58 | padding: 2px 10px 2px 10px;
59 | border-style: dashed;
60 | }
61 |
62 | th {
63 | color: #fff;
64 | }
65 |
66 | td {
67 | padding: 2px 10px 2px 10px;
68 | }
69 |
70 | .success {
71 | color: #0CC52B;
72 | }
73 |
74 | .failed {
75 | color: #E90A1B;
76 | }
77 |
78 | .unknown {
79 | color: #995000;
80 | }
81 | pre, code {
82 | font-family: monospace;
83 | font-size: 90%;
84 | line-height: 1.4em;
85 | color: #ff8;
86 | background-color: #111;
87 | padding: 2px 10px 2px 10px;
88 | }
89 | .comment { color: #aaa; font-style: italic; }
90 | .keyword { color: #eff; font-weight: bold; }
91 | .punct { color: #eee; font-weight: bold; }
92 | .symbol { color: #0bb; }
93 | .string { color: #6b4; }
94 | .ident { color: #ff8; }
95 | .constant { color: #66f; }
96 | .regex { color: #ec6; }
97 | .number { color: #F99; }
98 | .expr { color: #227; }
99 |
100 | #version {
101 | float: right;
102 | text-align: right;
103 | font-family: sans-serif;
104 | font-weight: normal;
105 | background-color: #B3ABFF;
106 | color: #141331;
107 | padding: 15px 20px 10px 20px;
108 | margin: 0 auto;
109 | margin-top: 15px;
110 | border: 3px solid #141331;
111 | }
112 |
113 | #version .numbers {
114 | display: block;
115 | font-size: 4em;
116 | line-height: 0.8em;
117 | letter-spacing: -0.1ex;
118 | }
119 |
120 | #version a {
121 | text-decoration: none;
122 | color: #141331;
123 | background-color: #B3ABFF;
124 | }
125 |
126 | .clickable {
127 | cursor: pointer;
128 | cursor: hand;
129 | }
130 |
131 |
--------------------------------------------------------------------------------
/test/boolean.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:37:13Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | False
19 | False
20 |
21 |
22 |
30 |
33 |
37 |
38 |
39 |
41 |
42 |
43 | 1 |
44 |
45 |
46 | 0 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
55 |
56 |
57 |
58 | 300
59 | 300
60 |
61 |
62 |
63 |
64 | 3
65 | 2
66 |
67 |
68 | False
69 | False
70 |
71 |
72 |
73 |
76 |
77 |
78 |
79 |
80 |
82 |
83 |
84 |
85 | 300
86 | 300
87 |
88 | False
89 | False
90 |
91 |
92 |
93 |
96 |
97 |
98 |
99 |
100 |
102 |
103 |
104 |
105 | 300
106 | 300
107 |
108 | False
109 | False
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/test/emptysheets.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:38:46Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 344
19 | False
20 | False
21 |
22 |
23 |
31 |
32 |
33 |
36 |
37 |
38 |
39 |
41 |
43 |
45 |
46 |
47 |
48 | 9
49 | 300
50 | 300
51 |
52 |
53 | False
54 | False
55 |
56 |
57 |
58 |
61 |
62 |
63 |
65 |
67 |
69 |
70 |
71 |
72 | 9
73 | 300
74 | 300
75 |
76 | 60
77 | False
78 | False
79 |
80 |
81 |
82 |
85 |
86 |
87 |
89 |
91 |
93 |
94 |
95 |
96 | 9
97 | 300
98 | 300
99 |
100 | 60
101 | False
102 | False
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/roo.gemspec:
--------------------------------------------------------------------------------
1 | # Generated by jeweler
2 | # DO NOT EDIT THIS FILE
3 | # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4 | # -*- encoding: utf-8 -*-
5 |
6 | Gem::Specification.new do |s|
7 | s.name = %q{roo}
8 | s.version = "1.4.0"
9 |
10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11 | s.authors = ["Hugh McGowan", "Thomas Preymesser"]
12 | s.date = %q{2010-01-16}
13 | s.description = %q{roo can access the contents of OpenOffice-, Excel- or Google-Spreadsheets}
14 | s.email = %q{hugh_mcgowan@yahoo.com}
15 | s.extra_rdoc_files = [
16 | "History.txt",
17 | "README.markdown"
18 | ]
19 | s.files = [
20 | "lib/roo.rb",
21 | "lib/roo/excel.rb",
22 | "lib/roo/excel2003xml.rb",
23 | "lib/roo/excelx.rb",
24 | "lib/roo/generic_spreadsheet.rb",
25 | "lib/roo/google.rb",
26 | "lib/roo/openoffice.rb",
27 | "lib/roo/roo_rails_helper.rb",
28 | "lib/roo/version.rb",
29 | "test/1900_base.xls",
30 | "test/1904_base.xls",
31 | "test/Bibelbund.csv",
32 | "test/Bibelbund.ods",
33 | "test/Bibelbund.xls",
34 | "test/Bibelbund.xlsx",
35 | "test/Bibelbund.xml",
36 | "test/Bibelbund1.ods",
37 | "test/bad_excel_date.xls",
38 | "test/bbu.ods",
39 | "test/bbu.xls",
40 | "test/bbu.xlsx",
41 | "test/bbu.xml",
42 | "test/bode-v1.ods.zip",
43 | "test/bode-v1.xls.zip",
44 | "test/boolean.ods",
45 | "test/boolean.xls",
46 | "test/boolean.xlsx",
47 | "test/boolean.xml",
48 | "test/borders.ods",
49 | "test/borders.xls",
50 | "test/borders.xlsx",
51 | "test/borders.xml",
52 | "test/bug-row-column-fixnum-float.xls",
53 | "test/bug-row-column-fixnum-float.xml",
54 | "test/datetime.ods",
55 | "test/datetime.xls",
56 | "test/datetime.xlsx",
57 | "test/datetime.xml",
58 | "test/datetime_floatconv.xls",
59 | "test/datetime_floatconv.xml",
60 | "test/emptysheets.ods",
61 | "test/emptysheets.xls",
62 | "test/emptysheets.xml",
63 | "test/excel2003.xml",
64 | "test/false_encoding.xls",
65 | "test/false_encoding.xml",
66 | "test/formula.ods",
67 | "test/formula.xls",
68 | "test/formula.xlsx",
69 | "test/formula.xml",
70 | "test/formula_parse_error.xls",
71 | "test/formula_parse_error.xml",
72 | "test/html-escape.ods",
73 | "test/no_spreadsheet_file.txt",
74 | "test/numbers1.csv",
75 | "test/numbers1.ods",
76 | "test/numbers1.xls",
77 | "test/numbers1.xlsx",
78 | "test/numbers1.xml",
79 | "test/only_one_sheet.ods",
80 | "test/only_one_sheet.xls",
81 | "test/only_one_sheet.xlsx",
82 | "test/only_one_sheet.xml",
83 | "test/paragraph.ods",
84 | "test/paragraph.xls",
85 | "test/paragraph.xlsx",
86 | "test/paragraph.xml",
87 | "test/ric.ods",
88 | "test/simple_spreadsheet.ods",
89 | "test/simple_spreadsheet.xls",
90 | "test/simple_spreadsheet.xlsx",
91 | "test/simple_spreadsheet.xml",
92 | "test/simple_spreadsheet_from_italo.ods",
93 | "test/simple_spreadsheet_from_italo.xls",
94 | "test/simple_spreadsheet_from_italo.xml",
95 | "test/skipped_tests.rb",
96 | "test/style.ods",
97 | "test/style.xls",
98 | "test/style.xlsx",
99 | "test/style.xml",
100 | "test/test_helper.rb",
101 | "test/test_roo.rb",
102 | "test/time-test.csv",
103 | "test/time-test.ods",
104 | "test/time-test.xls",
105 | "test/time-test.xlsx",
106 | "test/time-test.xml",
107 | "test/whitespace.ods",
108 | "test/whitespace.xls",
109 | "test/whitespace.xlsx",
110 | "test/whitespace.xml"
111 | ]
112 | s.homepage = %q{http://roo.rubyforge.org}
113 | s.rdoc_options = ["--main", "README.markdown"]
114 | s.require_paths = ["lib"]
115 | s.rubyforge_project = %q{roo}
116 | s.rubygems_version = %q{1.3.5}
117 | s.summary = %q{roo}
118 | s.test_files = [
119 | "test/skipped_tests.rb",
120 | "test/test_helper.rb",
121 | "test/test_roo.rb",
122 | "examples/roo_soap_client.rb",
123 | "examples/roo_soap_server.rb",
124 | "examples/write_me.rb"
125 | ]
126 |
127 | if s.respond_to? :specification_version then
128 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
129 | s.specification_version = 3
130 |
131 | if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
132 | s.add_runtime_dependency(%q, [">= 0.6.4"])
133 | s.add_runtime_dependency(%q, [">= 0.9.1"])
134 | s.add_runtime_dependency(%q, [">= 0.6"])
135 | s.add_runtime_dependency(%q, [">= 0.0.4"])
136 | s.add_runtime_dependency(%q, [">= 1.1.3"])
137 | else
138 | s.add_dependency(%q, [">= 0.6.4"])
139 | s.add_dependency(%q, [">= 0.9.1"])
140 | s.add_dependency(%q, [">= 0.6"])
141 | s.add_dependency(%q, [">= 0.0.4"])
142 | s.add_dependency(%q, [">= 1.1.3"])
143 | end
144 | else
145 | s.add_dependency(%q, [">= 0.6.4"])
146 | s.add_dependency(%q, [">= 0.9.1"])
147 | s.add_dependency(%q, [">= 0.6"])
148 | s.add_dependency(%q, [">= 0.0.4"])
149 | s.add_dependency(%q, [">= 1.1.3"])
150 | end
151 | end
152 |
--------------------------------------------------------------------------------
/test/bug-row-column-fixnum-float.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:37:56Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 344
19 | False
20 | False
21 |
22 |
23 |
31 |
32 |
33 |
35 |
36 | abc |
37 | def |
38 | 42 |
39 |
40 |
41 | hij |
42 | 42.5 |
43 | 43 |
44 |
45 |
46 | klm |
47 | nop |
48 | xyz |
49 |
50 |
51 |
52 |
53 |
54 |
56 |
58 |
60 |
61 |
62 |
63 | 9
64 | 300
65 | 300
66 |
67 |
68 |
69 |
70 | 3
71 | 3
72 | 2
73 |
74 |
75 | False
76 | False
77 |
78 |
79 |
80 |
83 |
84 |
85 |
87 |
89 |
91 |
92 |
93 |
94 | 9
95 | 300
96 | 300
97 |
98 | 60
99 | False
100 | False
101 |
102 |
103 |
104 |
107 |
108 |
109 |
111 |
113 |
115 |
116 |
117 |
118 | 9
119 | 300
120 | 300
121 |
122 | 60
123 | False
124 | False
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/test/formula.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:39:20Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 395
19 | False
20 | False
21 |
22 |
23 |
31 |
32 |
33 |
35 |
36 | 1 |
37 |
38 |
39 | 2 |
40 |
41 |
42 | 3 |
43 |
44 |
45 | 4 |
46 |
47 |
48 | 5 |
49 |
50 |
51 | 6 |
52 |
53 |
54 | 21 |
55 | 21 |
56 |
57 |
58 |
59 |
60 |
61 |
63 |
65 |
67 |
68 |
69 |
70 | 9
71 | 300
72 | 300
73 |
74 |
75 |
76 |
77 | 3
78 | 6
79 | 1
80 |
81 |
82 | False
83 | False
84 |
85 |
86 |
87 |
90 |
91 |
92 |
94 |
96 |
98 |
99 |
100 |
101 | 9
102 | 300
103 | 300
104 |
105 | 60
106 | False
107 | False
108 |
109 |
110 |
111 |
114 |
115 |
116 |
118 |
120 |
122 |
123 |
124 |
125 | 9
126 | 300
127 | 300
128 |
129 | 60
130 | False
131 | False
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/test/time-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:41:04Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 344
19 | False
20 | False
21 |
22 |
23 |
31 |
34 |
37 |
40 |
41 |
42 |
44 |
45 | Mittags: |
46 | 1899-12-31T12:13:14.000 |
47 | 1899-12-31T15:16:00.000 |
48 | 1899-12-31T23:00:00.000 |
49 |
50 |
51 | 2007-11-21T00:00:00.000 |
52 |
53 |
54 | |
55 |
56 |
57 |
58 |
59 |
60 |
62 |
64 |
66 |
67 |
68 |
69 | 9
70 | 300
71 | 300
72 |
73 |
74 |
75 |
76 | 3
77 | 5
78 | 1
79 |
80 |
81 | False
82 | False
83 |
84 |
85 |
86 |
89 |
90 |
91 |
93 |
95 |
97 |
98 |
99 |
100 | 9
101 | 300
102 | 300
103 |
104 | False
105 | False
106 |
107 |
108 |
109 |
112 |
113 |
114 |
116 |
118 |
120 |
121 |
122 |
123 | 9
124 | 300
125 | 300
126 |
127 | False
128 | False
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/test/paragraph.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:40:13Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 176
19 | False
20 | False
21 |
22 |
23 |
31 |
34 |
37 |
41 |
42 |
43 |
45 |
46 |
47 |
48 |
49 | This is a test
of a multiline
Cell |
50 | This is a test
¶
of a multiline
Cell |
51 | |
52 |
53 |
54 | first p
second p
last p |
55 |
56 |
57 | |
58 | |
59 | |
60 |
61 |
62 | |
63 | |
64 | |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
74 |
75 |
76 |
77 | 300
78 | 300
79 |
80 |
81 | False
82 | False
83 |
84 |
85 |
86 |
89 |
90 |
91 |
92 |
93 |
95 |
96 |
97 |
98 | 300
99 | 300
100 |
101 | 60
102 | False
103 | False
104 |
105 |
106 |
107 |
110 |
111 |
112 |
113 |
114 |
116 |
117 |
118 |
119 | 300
120 | 300
121 |
122 | 60
123 | False
124 | False
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/test/datetime_floatconv.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:38:34Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 233
19 | False
20 | False
21 |
22 |
23 |
31 |
34 |
38 |
41 |
42 |
43 |
45 |
46 |
47 |
48 |
49 | 2009-01-01T00:00:00.000 |
50 |
51 |
52 | 01/02/2009 |
53 |
54 |
55 | |
56 | |
57 | |
58 |
59 |
60 | |
61 | |
62 | |
63 |
64 |
65 | |
66 | |
67 | |
68 |
69 |
70 | |
71 | |
72 | |
73 |
74 |
75 | |
76 | |
77 | |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
87 |
88 |
89 |
90 | 9
91 | 300
92 | 300
93 |
94 |
95 |
96 |
97 | 3
98 | 8
99 | 3
100 |
101 |
102 | False
103 | False
104 |
105 |
106 |
107 |
110 |
111 |
112 |
113 |
114 |
116 |
117 |
118 |
119 | 9
120 | 300
121 | 300
122 |
123 | False
124 | False
125 |
126 |
127 |
128 |
131 |
132 |
133 |
134 |
135 |
137 |
138 |
139 |
140 | 9
141 | 300
142 | 300
143 |
144 | False
145 | False
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/test/borders.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:37:33Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 395
19 | 1
20 | False
21 | False
22 |
23 |
24 |
32 |
33 |
34 |
36 |
37 | lalala |
38 |
39 |
40 | huhuhu |
41 |
42 |
43 |
44 |
45 |
46 |
48 |
50 |
52 |
53 |
54 |
55 | 9
56 | 300
57 | 300
58 |
59 |
60 |
61 | 3
62 | 9
63 | 6
64 |
65 |
66 | False
67 | False
68 |
69 |
70 |
71 |
73 |
74 | lalala |
75 |
76 |
77 | huhuhu |
78 |
79 |
80 |
81 |
82 |
84 |
86 |
88 |
89 |
90 |
91 | 9
92 | 300
93 | 300
94 |
95 |
96 |
97 |
98 | 3
99 | 19
100 | 1
101 |
102 |
103 | False
104 | False
105 |
106 |
107 |
108 |
110 |
111 | lalala |
112 |
113 |
114 | huhuhu |
115 |
116 |
117 |
118 |
119 |
121 |
123 |
125 |
126 |
127 |
128 | 9
129 | 300
130 | 300
131 |
132 | 2
133 |
134 |
135 | 3
136 | 6
137 | 8
138 |
139 |
140 | False
141 | False
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/test/false_encoding.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:39:06Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 211
19 | False
20 | False
21 |
22 |
23 |
31 |
34 |
37 |
38 |
39 |
41 |
42 |
43 |
44 | 2009-12-22T00:00:00.000 |
45 | 2010-04-01T00:00:00.000 |
46 | 2009-12-22T11:41:08.380 |
47 | 2009-12-22T11:41:08.380 |
48 |
49 |
50 | H1 |
51 | H2 |
52 | H3 |
53 | H4 |
54 |
55 |
56 | R1 |
57 | R2 |
58 | R3 |
59 | R4 |
60 |
61 |
62 |
63 |
64 |
65 |
67 |
69 |
71 |
72 |
73 |
74 | 300
75 | 300
76 |
77 |
78 |
79 |
80 | 3
81 | 3
82 |
83 |
84 | False
85 | False
86 |
87 |
88 |
89 |
92 |
93 |
94 |
96 |
98 |
100 |
101 |
102 |
103 | 300
104 | 300
105 |
106 | False
107 | False
108 |
109 |
110 |
111 |
114 |
115 |
116 |
118 |
120 |
122 |
123 |
124 |
125 | 300
126 | 300
127 |
128 | False
129 | False
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/test/style.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:40:52Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | False
19 | False
20 |
21 |
22 |
30 |
33 |
36 |
39 |
42 |
45 |
48 |
49 |
50 |
52 |
53 |
54 |
55 |
56 | Bold |
57 |
58 |
59 | Italic |
60 |
61 |
62 | Normal |
63 |
64 |
65 | Underline |
66 |
67 |
68 | Bold-Italic |
69 |
70 |
71 | Bold-Underline |
72 |
73 |
74 | Italic-Underline |
75 |
76 |
77 | Bolded Row |
78 | |
79 |
80 |
81 | Bolded Column |
82 |
83 |
84 | Bolded Row Italic Column |
85 |
86 |
87 | Normal |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
96 |
97 |
98 |
99 | 300
100 | 300
101 |
102 |
103 |
104 |
105 | 3
106 | 3
107 | 2
108 |
109 |
110 | False
111 | False
112 |
113 |
114 |
115 |
118 |
119 |
120 |
121 |
122 |
124 |
125 |
126 |
127 | 300
128 | 300
129 |
130 | False
131 | False
132 |
133 |
134 |
135 |
138 |
139 |
140 |
141 |
142 |
144 |
145 |
146 |
147 | 300
148 | 300
149 |
150 | False
151 | False
152 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/test/datetime.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:38:10Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 233
19 | False
20 | False
21 |
22 |
23 |
31 |
34 |
37 |
38 |
39 |
41 |
42 |
43 |
44 |
45 | 1961-11-21T00:00:00.000 |
46 |
47 |
48 | 1961-11-21T12:17:18.000 |
49 | 1961-11-21T12:17:18.000 |
50 | 1961-11-21T12:17:18.000 |
51 |
52 |
53 | 1961-11-21T12:17:18.000 |
54 | 1961-11-21T12:17:18.000 |
55 | 1961-11-21T12:17:18.000 |
56 |
57 |
58 | 1961-11-21T12:17:18.000 |
59 | 1961-11-21T12:17:18.000 |
60 | 1961-11-21T12:17:18.000 |
61 |
62 |
63 | 1961-11-21T00:00:00.000 |
64 | 1961-11-21T00:00:00.000 |
65 | 1961-11-21T00:00:00.000 |
66 |
67 |
68 | 1961-11-21T00:00:00.000 |
69 | 1961-11-21T00:00:00.000 |
70 | 1961-11-21T00:00:00.000 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
80 |
81 |
82 |
83 | 9
84 | 300
85 | 300
86 |
87 |
88 |
89 |
90 | 3
91 | 7
92 |
93 |
94 | False
95 | False
96 |
97 |
98 |
99 |
102 |
103 |
104 |
105 |
106 |
108 |
109 |
110 |
111 | 9
112 | 300
113 | 300
114 |
115 | 60
116 | False
117 | False
118 |
119 |
120 |
121 |
124 |
125 |
126 |
127 |
128 |
130 |
131 |
132 |
133 | 9
134 | 300
135 | 300
136 |
137 | 60
138 | False
139 | False
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/test/bbu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:36:10Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 344
19 | 1
20 | False
21 | False
22 |
23 |
24 |
32 |
35 |
38 |
41 |
42 |
43 |
45 |
46 | Datum |
47 | von |
48 | bis |
49 | Pause |
50 | total |
51 | Bemerkung |
52 |
53 |
54 | 2007-12-17T00:00:00.000 |
55 | 10 |
56 | 14 |
57 | 0 |
58 | 4 |
59 | so vereinbart, gilt als 8 h Tag |
60 |
61 |
62 | 2007-12-18T00:00:00.000 |
63 | 12 |
64 | 20 |
65 | 0 |
66 | 8 |
67 | so vereinbart, gilt als 8 h Tag |
68 |
69 |
70 | 2007-12-19T00:00:00.000 |
71 | 10 |
72 | 20.5 |
73 | 0 |
74 | 10.5 |
75 |
76 |
77 |
78 |
79 |
80 |
82 |
84 |
86 |
87 |
88 |
89 | 9
90 | 300
91 | 300
92 |
93 |
94 |
95 | 3
96 | 3
97 | 5
98 |
99 |
100 | False
101 | False
102 |
103 |
104 |
105 |
108 |
109 |
110 |
112 |
114 |
116 |
117 |
118 |
119 | 9
120 | 300
121 | 300
122 |
123 |
124 | False
125 | False
126 |
127 |
128 |
129 |
132 |
133 |
134 |
136 |
138 |
140 |
141 |
142 |
143 | 9
144 | 300
145 | 300
146 |
147 | 60
148 | False
149 | False
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/test/whitespace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:41:27Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | False
19 | False
20 |
21 |
22 |
30 |
34 |
38 |
41 |
44 |
47 |
48 |
49 |
51 |
52 |
53 | |
54 | |
55 | |
56 | |
57 | |
58 | |
59 |
60 |
61 | |
62 | |
63 | |
64 | |
65 | |
66 | |
67 |
68 |
69 | Date |
70 | Start time |
71 | End time |
72 | Pause |
73 | Sum |
74 | Comment |
75 |
76 |
77 | 2007-05-07T00:00:00.000 |
78 | 9.25 |
79 | 10.25 |
80 | 0 |
81 | 1 |
82 | Task 1 |
83 |
84 |
85 | |
86 | |
87 | |
88 |
89 |
90 | 2007-05-07T00:00:00.000 |
91 | 10.75 |
92 | 10.75 |
93 | 0 |
94 | 0 |
95 | Task 1 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
104 |
105 |
106 |
107 | 300
108 | 300
109 |
110 |
111 |
112 |
113 | 3
114 | 6
115 |
116 |
117 | False
118 | False
119 |
120 |
121 |
122 |
124 |
125 |
126 | Date |
127 | |
128 | Start time |
129 |
130 |
131 | 2007-05-07T00:00:00.000 |
132 | |
133 | 9.25 |
134 |
135 |
136 | 2007-05-07T00:00:00.000 |
137 | |
138 | 10.75 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
147 |
148 |
149 |
150 | 300
151 | 300
152 |
153 |
154 |
155 | 3
156 | 10
157 | 2
158 |
159 |
160 | False
161 | False
162 |
163 |
164 |
165 |
168 |
169 |
170 |
171 |
172 |
174 |
175 |
176 |
177 | 300
178 | 300
179 |
180 | False
181 | False
182 |
183 |
184 |
185 |
--------------------------------------------------------------------------------
/History.txt:
--------------------------------------------------------------------------------
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 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
226 |
--------------------------------------------------------------------------------
/base64include.rb:
--------------------------------------------------------------------------------
1 | @@empty_spreadsheet=<<_ENDOFBASE64CODE_
2 | UEsDBBQAAAAAABWJ4jaFbDmKLgAAAC4AAAAIAAAAbWltZXR5cGVhcHBsaWNh
3 | dGlvbi92bmQub2FzaXMub3BlbmRvY3VtZW50LnNwcmVhZHNoZWV0UEsDBBQA
4 | AAAAABWJ4jYAAAAAAAAAAAAAAAAaAAAAQ29uZmlndXJhdGlvbnMyL3N0YXR1
5 | c2Jhci9QSwMEFAAIAAgAFYniNgAAAAAAAAAAAAAAACcAAABDb25maWd1cmF0
6 | aW9uczIvYWNjZWxlcmF0b3IvY3VycmVudC54bWwDAFBLBwgAAAAAAgAAAAAA
7 | AABQSwMEFAAAAAAAFYniNgAAAAAAAAAAAAAAABgAAABDb25maWd1cmF0aW9u
8 | czIvZmxvYXRlci9QSwMEFAAAAAAAFYniNgAAAAAAAAAAAAAAABoAAABDb25m
9 | aWd1cmF0aW9uczIvcG9wdXBtZW51L1BLAwQUAAAAAAAVieI2AAAAAAAAAAAA
10 | AAAAHAAAAENvbmZpZ3VyYXRpb25zMi9wcm9ncmVzc2Jhci9QSwMEFAAAAAAA
11 | FYniNgAAAAAAAAAAAAAAABgAAABDb25maWd1cmF0aW9uczIvbWVudWJhci9Q
12 | SwMEFAAAAAAAFYniNgAAAAAAAAAAAAAAABgAAABDb25maWd1cmF0aW9uczIv
13 | dG9vbGJhci9QSwMEFAAAAAAAFYniNgAAAAAAAAAAAAAAAB8AAABDb25maWd1
14 | cmF0aW9uczIvaW1hZ2VzL0JpdG1hcHMvUEsDBBQACAAIABWJ4jYAAAAAAAAA
15 | AAAAAAALAAAAY29udGVudC54bWzlV7ty2zAQ7PMVHBbpIOjhSWxGkptMKjuN
16 | k0xaCDxKiPHgAKBo/X0AgqRBWZRZJw01Ouze7S2Op9H6/kXw5AjaMCU36WI2
17 | TxOQVOVM7jfpzx/f0G16v/2wVkXBKGS5opUAaRFV0rrPxLGlycLpJq20zBQx
18 | zGSSCDCZpZkqQXasLEZnTa0QMfbEJ9MbcMy28GKnkj12wCW76ZUbcMzONamn
19 | kj3WmRrTCzWV/GI4KpRzXZTEsjMVL5zJ5016sLbMMK7relavZkrv8eLu7g43
20 | p71g2uPKSvMGlVMMHHwxgxezBe6wAiyZqs9jY0myEjvQk60hlry5VXPcT56I
21 | 437EGnogevJsNODh9a7y6de7ymOuIPYwcie3+NEdNo/Hh9dZ0GJqLY8dWEU1
22 | Kye3GdAxXynVS/WE8II2cpfz+Q0O3yN0fRVea2ZBR3B6FU4Jp73jSlwyzeEW
23 | 2CEQHP2Y9oPvjTAjhCUOxz3Y5KOpfz8+PNEDCPIKZu+DEZPGEumdaVfaYI9u
24 | u6UZDDe4DxRueaKCUEA5UG626zD8fTgJ3/0lbtLvTOwqkzwRaRI3LW7WO6Bg
25 | /LRJP5JSmS8DVAilySCtR6M9SNDM3YepmTEDRMksdRN7JJr5NZfi67K+wh/y
26 | q2oKjoqKMFMknYwF8Z4mPOZhGyeVVe7dYxQ1eXpzm+egA6oWfbFWdrPg3Srh
27 | lZBpx4yDqNRuhLVlYJJCZTsN5BntwA2aS+hLdxlbeM1yvwaWs+Wnz1Q0+iM5
28 | 49r0mDat6jNhLhKrCkc+eAC2P7itMJ/d3Lra1/VWBpAqLROEo5hsdQXTZVty
29 | WXYXFG7Ng0Yl2QPqxqggFbdnPUX9hN/cnJmSk1Orp83m94z7RUVC5S4T18ju
30 | 3krFo3PRHuxUfnp9VUvnUG4OAHa7DqWbZysjaH7yx67REGvSoaj/EC41k86/
31 | gnAD6SBXO0oX6M1Etg0HXxAFzlGM6QzDw5zuzi4k9GN0Vtvl8x6dcYeRd1pf
32 | /r+tr/7p1vGltwAP3hM88kdg+xdQSwcIHNYNSv0CAABJDAAAUEsDBBQACAAI
33 | ABWJ4jYAAAAAAAAAAAAAAAAKAAAAc3R5bGVzLnhtbN1YX5ObNhB/76dguJlM
34 | MlMM+JKc7djcS9rpQ9PJJE3fZSGwGoEYSfhPH/t5+qn6SboSCIMBm6TtpM3d
35 | zd2x+u1q9dvV7uL14zFjzp4ISXm+ccNZ4DokxzymebpxP/z8vbdwH6Nv1jxJ
36 | KCarmOMyI7nypDoxIh1QzuWqWty4pchXHEkqVznKiFwpvOIFya3Sqo1ema0q
37 | iTE2Vd2A29qKHNVUZY3t6KLt9J0NuK0dC3SYqqyxwGlbPeFTlY+SeQn3MM8K
38 | pOiFF0dG848bd6dUsfL9w+EwO9zPuEj9cLlc+ma1cRg3uKIUzKBi7BNG9GbS
39 | D2ehb7EZUWiqfxrbdikvsy0Rk6lBCvWiKvfp5IzYpyPU4B0Sk3PDgLvhvY+n
40 | h/c+butmSO1GYrLw38Ci+fXmx3MuiGzqXhrboQoLWkw+ZoVu63POG1e1QnVB
41 | jbvzIHjuV88t9OEq/CCoIqIFx1fhGDHcMM6zIdIAF/qA8Mhep6nr1CWkU7Yi
42 | W6MSDvUpQZh4McFMRusqtxqxUz1rjjbuTzTbltJ5j3LpQDAglSwwo+y0cZ+g
43 | gstXHVQlcp2OWY32UpITQeG48kCl7CAKqjAkxB4JqquI61936zX5Ff1Smg1H
44 | nWphprh0kopkt3zyxzis5VXZt77HJEElq5uBtVz7aIqlhwljroWfRV4hIBWE
45 | otBCrClMMwQLDDaVG3d+ZqhAAqUCFbu+FliEzXnhxVQqlOseFM7mL3B21tYV
46 | v69ozjeYAQlfMZSnJUphLSZGgHmZKwGHev2de2nAgwuH8suIGYy1YyEktyu1
47 | Qbvw4X3frC5ojByvG25AfdPNEhjXgR2IWLSu6nRdrjthrLnR16oDcuqnjOYe
48 | zRVJQS+mKVUQs9BsNGCzsYFLIWC6OA1tFQYv3wb2FHvOoJrr1qxEScac6CfN
49 | dffsaip4WZgBx1j3G/M6WSKnOYN57Pt+yracWVvdZLHYVsJEf/7+R2Pxwkg0
50 | sPAphLljWW6SlnFowXdJEsDX5SG94UN+dfxW9GSoaK5HHlNlmsYesZI8ffYk
51 | Va82TeKhomA1214nMf3xWFWa/Qi9ri6bO14Z/XHld0Re13Wa6tjM41533yvJ
52 | YepMPXZThRjF1p4Bl3lMBAyOxGIkZzQegRxorGcdVCo+gqhT0Wxq/ncbHw6E
53 | pjuYXSBYcatO3WC2Imf+Wex0idWzZy/c1+LyA0H63eifCcyVhmgohMCkuSd5
54 | KXRrS+jRGhekIEiTCXcwB/oSxCS50TKB8rPRjYtBEca00UbZZAn9TbfVl4Vy
55 | xzLn7wWzZjT8LEptOKZQKrhC+up7UFK0+8ug76R/MejUjzq5YaSn2OtOQIXu
56 | wgydeKk6Zyqy0B3A9H3SwzL472U8BjUmPLU9h2QHhzv30I4s4RyidxEvXZV3
57 | dQyC2cOLEAYhI0cihSVGEr3QFYoa3pVuuVJ6Fg+aacofd6n25Qu4CaPfgI9d
58 | f/xeFG4Hb/41BU+vbLmI9Qt5MAsWC5w5pp47d4H5MogCxdVnPgAJF1YN4Y+6
59 | meexreF3ONDfDT8tBIwKKellSu+s/4NU+q8ydjux/dF6VS9kSDYm5Hk8MkJt
60 | 6doA074L/TJXeR+tzUdsRf1X7gipwNHj4+PavxTWkuKCg4vQ6zDafk0lTKOn
61 | XserqGl2f6uPUj9or6vJLQrtfi1ZzwVrqsP5VRf8Ho23mH1HCi6uEjvvEWs7
62 | f6pbmPbnE7l2nlY4RRVrQ6rnZz0eOjt1ROYGXewOgxQZnalgUmtAnpm74WUi
63 | CB48/QMnDeaz4GGmJbUXGhh961iHwftwuQoWq+f3jdND2dP174ullOO3geZl
64 | JVou28BK9i+lnj981/3hz/CjvwBQSwcI7Cj9uWgFAAADGAAAUEsDBBQAAAAA
65 | ABWJ4jZx0iL8qgMAAKoDAAAIAAAAbWV0YS54bWw8P3htbCB2ZXJzaW9uPSIx
66 | LjAiIGVuY29kaW5nPSJVVEYtOCI/Pgo8b2ZmaWNlOmRvY3VtZW50LW1ldGEg
67 | eG1sbnM6b2ZmaWNlPSJ1cm46b2FzaXM6bmFtZXM6dGM6b3BlbmRvY3VtZW50
68 | OnhtbG5zOm9mZmljZToxLjAiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3Lncz
69 | Lm9yZy8xOTk5L3hsaW5rIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2Rj
70 | L2VsZW1lbnRzLzEuMS8iIHhtbG5zOm1ldGE9InVybjpvYXNpczpuYW1lczp0
71 | YzpvcGVuZG9jdW1lbnQ6eG1sbnM6bWV0YToxLjAiIHhtbG5zOm9vbz0iaHR0
72 | cDovL29wZW5vZmZpY2Uub3JnLzIwMDQvb2ZmaWNlIiBvZmZpY2U6dmVyc2lv
73 | bj0iMS4wIj48b2ZmaWNlOm1ldGE+PG1ldGE6Z2VuZXJhdG9yPk9wZW5PZmZp
74 | Y2Uub3JnLzIuMCRMaW51eCBPcGVuT2ZmaWNlLm9yZ19wcm9qZWN0LzY4MG01
75 | JEJ1aWxkLTkwNzM8L21ldGE6Z2VuZXJhdG9yPjxtZXRhOmluaXRpYWwtY3Jl
76 | YXRvcj5UaG9tYXMgUHJleW1lc3NlcjwvbWV0YTppbml0aWFsLWNyZWF0b3I+
77 | PG1ldGE6Y3JlYXRpb24tZGF0ZT4yMDA3LTA3LTAyVDE5OjA3OjUxPC9tZXRh
78 | OmNyZWF0aW9uLWRhdGU+PGRjOmxhbmd1YWdlPmRlLURFPC9kYzpsYW5ndWFn
79 | ZT48bWV0YTplZGl0aW5nLWN5Y2xlcz4xPC9tZXRhOmVkaXRpbmctY3ljbGVz
80 | PjxtZXRhOmVkaXRpbmctZHVyYXRpb24+UFQwUzwvbWV0YTplZGl0aW5nLWR1
81 | cmF0aW9uPjxtZXRhOnVzZXItZGVmaW5lZCBtZXRhOm5hbWU9IkluZm8gMSIv
82 | PjxtZXRhOnVzZXItZGVmaW5lZCBtZXRhOm5hbWU9IkluZm8gMiIvPjxtZXRh
83 | OnVzZXItZGVmaW5lZCBtZXRhOm5hbWU9IkluZm8gMyIvPjxtZXRhOnVzZXIt
84 | ZGVmaW5lZCBtZXRhOm5hbWU9IkluZm8gNCIvPjxtZXRhOmRvY3VtZW50LXN0
85 | YXRpc3RpYyBtZXRhOnRhYmxlLWNvdW50PSIzIi8+PC9vZmZpY2U6bWV0YT48
86 | L29mZmljZTpkb2N1bWVudC1tZXRhPlBLAwQUAAgACAAVieI2AAAAAAAAAAAA
87 | AAAAGAAAAFRodW1ibmFpbHMvdGh1bWJuYWlsLnBuZ+sM8HPn5ZLiYmBg4PX0
88 | cAkC0iVAvICDDUjeNfWQY2BgbPd0cQypmPP2oiMngwHPgQO2r+4XpScZcJRz
89 | Mc28a1ty5j3DPSOTYxcknJN5eg1HGTRizODOSmatE5azVwPGDIOnq5/LOqeE
90 | JgBQSwcItoVPfmoAAADAAQAAUEsDBBQACAAIABWJ4jYAAAAAAAAAAAAAAAAM
91 | AAAAc2V0dGluZ3MueG1s7Zltc6I6FIC/31/h8PWOFbXbVqe6g/i6Vdd3W78F
92 | SDVtSNgkiPTX3yC6Y1nZtYgzd3bqDCokec7h5OScE7j/urFxZg0ZR5RUlPyV
93 | qmQgMamFyLKiTCfN7J3ytfrPPX1+RiYsW9R0bUhElkMhZBeekcMJL4fNFcVl
94 | pEwBR7xMgA15WZhl6kCyH1Y+7F3eCguvbDAirxVlJYRTzuU8z7vyileULXP5
95 | UqmU27buu5qUPKPlqaLC3oeiKKU/BQUDQmW2wgqqep0Lz5XMTsl3pqnu7bC/
96 | /er9TkD4k0UC2oFtMrvLgWoVRYosrxH0flpNOTbu/ZgZ4sjAUGMQTKij7BuF
97 | 78hGRIRSVe9zv0I+BO7CZ3EZ8hxZYnUMXSh8uTub3oZouTqq+fXt7an0rA2c
98 | LCIW3EArKgl6x6doO0Y6F/NP0Rd6HSuiJBdMzr9SDRrzH9I0gEb0nABpjz8p
99 | +n7IeAWhyJ/gfrrLOGUDypGQ3v+Yope8Jz+lSG5Tht4oEQCPHYxEj1owav4V
100 | ZWf4N2QCmZeiR7TfGyjN9Xmo/wX4minQGm7pI0CWMeYpJIPv9U05Zu2xo7iI
101 | ciY33di9p9aoENT+APjIxcNYFte8DTqnTvw2HMXEuzDunL0wTEYxNgCLTy+3
102 | CS27oNSeSE6qSzqAzgB2o9Stqnk1qROAJQzyx2/pNwnh4xX1AgE1mWZfBwwG
103 | ZUuEb1CKISBK9RlgDpOLWUBGt/rzOAGCuWfw+1RcCt1iKJrZUyAHVJ1iyiJo
104 | TIMllC/cFGX1dJPCvF7AKm3ApequTUbUa0Ngyar5IkK2gURGmgvQO/y7K+RO
105 | A45926CYj2E0IaQiZEyAM6EjwAWMTnQaKysEd/iuXL6YhBHkcr5ji0MZ3RJG
106 | oCj+aIV4Ln7sGhZaIx6rfkrw48ondZ0Qr20QH/vEXDFK0BtMEIgSVgO7zdLx
107 | DnJre/pmOLzgMhBM8Ed2xZ954y/LG11EXqeOBQSM31AVP1PSZ0r6TEl/dUr6
108 | kyTNFVQH2HSxDBXp4wdymyrvoy9PYnawdX2QzRdUNSuP26x6k1UT2mwnSi5l
109 | N/powAAc3lzXEAHMV6qzYfvfnDqqT3V103vRvO6k4/XqpujVn+TR0P5vn2F+
110 | Npy+OY1hXhO9QFdd9Xt1rdidaAV5bA66zjWtp9leeJLL3dU0bTWWfxuW/Brb
111 | JTRqNdWnsbbRSc0H8y/q4rFTGjX6w+6kETBFYI++r3p9pHq9obM2bYwNMlpZ
112 | Lbw2XvJDo/hNNe2mY2nOi1GsOYvHXkn2e3sqNPliIu1Z1x6MedNfFLC7aDV/
113 | WI991ZjPXKuuet26xnu65+l2f220Sv6iNfOs1rLUexk+mO0+XzwusCH7HbQX
114 | wLyPBxPtYdqsNYaFkmu1ZtfbexjSSkKHdxzsTzlkdSBA+g6vrwADpvRDndoO
115 | gzyIDKk//+jwB8iIxhEgA5eYwgVHnjim8jgBrOEsfFHzneiY8kskmbBMaTJq
116 | T6Dt/C4OnSFEw1gWXEGI+EYNHRAT4gsUXRRYI1kRUYL9BPcQW/7nfnk7lot7
117 | b1j9D1BLBwhS6dOMhwQAAHkcAABQSwMEFAAIAAgAFYniNgAAAAAAAAAAAAAA
118 | ABUAAABNRVRBLUlORi9tYW5pZmVzdC54bWy1lUtqwzAQQPc9hdHeVttVMXEC
119 | LfQE6QEm8tgR6IdmFJLbVw7k0zaUplg7DUjvjUYaabHaW1PtMJL2rhNPzaOo
120 | 0Cnfazd24mP9Xr+I1fJhYcHpAYnb06DK6xydw06k6FoPpKl1YJFaVq0P6Hqv
121 | kkXH7df57WRaPlQX8KAN1nliPFQXGfYaaj4E7ASEYLQCznnKneubo6u5VjQU
122 | IkJPW0QWF8iQjKkD8LYTUsi7nLcpb94NekzxmAs9S2LgRBuIZfCgFBrMoY9S
123 | pRinneZiFncVEQzGA2MhePAhhXwTUiF89GNEKnfSU+rF4Oy9KQbXFkYk+arZ
124 | QqCijjvZ358NSm7qnibpRl0L/pbDnXLGPcupV2/Cs5//18y/c4kPBml2rEWG
125 | 2R6e9TbZjQNtSPJp2AQ3zg2ft7DInP/Fc2kX8se3uPwEUEsHCMwGTWZCAQAA
126 | UQcAAFBLAQIUABQAAAAAABWJ4jaFbDmKLgAAAC4AAAAIAAAAAAAAAAAAAAAA
127 | AAAAAABtaW1ldHlwZVBLAQIUABQAAAAAABWJ4jYAAAAAAAAAAAAAAAAaAAAA
128 | AAAAAAAAAAAAAFQAAABDb25maWd1cmF0aW9uczIvc3RhdHVzYmFyL1BLAQIU
129 | ABQACAAIABWJ4jYAAAAAAgAAAAAAAAAnAAAAAAAAAAAAAAAAAIwAAABDb25m
130 | aWd1cmF0aW9uczIvYWNjZWxlcmF0b3IvY3VycmVudC54bWxQSwECFAAUAAAA
131 | AAAVieI2AAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAADjAAAAQ29uZmlndXJh
132 | dGlvbnMyL2Zsb2F0ZXIvUEsBAhQAFAAAAAAAFYniNgAAAAAAAAAAAAAAABoA
133 | AAAAAAAAAAAAAAAAGQEAAENvbmZpZ3VyYXRpb25zMi9wb3B1cG1lbnUvUEsB
134 | AhQAFAAAAAAAFYniNgAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAUQEAAENv
135 | bmZpZ3VyYXRpb25zMi9wcm9ncmVzc2Jhci9QSwECFAAUAAAAAAAVieI2AAAA
136 | AAAAAAAAAAAAGAAAAAAAAAAAAAAAAACLAQAAQ29uZmlndXJhdGlvbnMyL21l
137 | bnViYXIvUEsBAhQAFAAAAAAAFYniNgAAAAAAAAAAAAAAABgAAAAAAAAAAAAA
138 | AAAAwQEAAENvbmZpZ3VyYXRpb25zMi90b29sYmFyL1BLAQIUABQAAAAAABWJ
139 | 4jYAAAAAAAAAAAAAAAAfAAAAAAAAAAAAAAAAAPcBAABDb25maWd1cmF0aW9u
140 | czIvaW1hZ2VzL0JpdG1hcHMvUEsBAhQAFAAIAAgAFYniNhzWDUr9AgAASQwA
141 | AAsAAAAAAAAAAAAAAAAANAIAAGNvbnRlbnQueG1sUEsBAhQAFAAIAAgAFYni
142 | Nuwo/bloBQAAAxgAAAoAAAAAAAAAAAAAAAAAagUAAHN0eWxlcy54bWxQSwEC
143 | FAAUAAAAAAAVieI2cdIi/KoDAACqAwAACAAAAAAAAAAAAAAAAAAKCwAAbWV0
144 | YS54bWxQSwECFAAUAAgACAAVieI2toVPfmoAAADAAQAAGAAAAAAAAAAAAAAA
145 | AADaDgAAVGh1bWJuYWlscy90aHVtYm5haWwucG5nUEsBAhQAFAAIAAgAFYni
146 | NlLp04yHBAAAeRwAAAwAAAAAAAAAAAAAAAAAig8AAHNldHRpbmdzLnhtbFBL
147 | AQIUABQACAAIABWJ4jbMBk1mQgEAAFEHAAAVAAAAAAAAAAAAAAAAAEsUAABN
148 | RVRBLUlORi9tYW5pZmVzdC54bWxQSwUGAAAAAA8ADwDuAwAA0BUAAAAA
149 | _ENDOFBASE64CODE_
150 |
--------------------------------------------------------------------------------
/test/simple_spreadsheet.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:40:27Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 344
19 | False
20 | False
21 |
22 |
23 |
31 |
34 |
37 |
41 |
45 |
48 |
49 |
50 |
52 |
53 |
54 |
55 | |
56 | |
57 | |
58 | |
59 | |
60 |
61 |
62 | |
63 | |
64 | |
65 | |
66 | |
67 |
68 |
69 | Date |
70 | Start time |
71 | End time |
72 | Pause |
73 | Sum |
74 | Comment |
75 | |
76 | |
77 |
78 |
79 | 2007-05-07T00:00:00.000 |
80 | 9.25 |
81 | 10.25 |
82 | 0 |
83 | 1 |
84 | Task 1 |
85 |
86 |
87 | 2007-05-07T00:00:00.000 |
88 | 10.75 |
89 | 12.5 |
90 | 0 |
91 | 1.75 |
92 | Task 1 |
93 |
94 |
95 | 2007-05-07T00:00:00.000 |
96 | 18 |
97 | 19 |
98 | 0 |
99 | 1 |
100 | Task 2 |
101 |
102 |
103 | 2007-05-08T00:00:00.000 |
104 | 9.25 |
105 | 10.25 |
106 | 0 |
107 | 1 |
108 | Task 2 |
109 |
110 |
111 | 2007-05-08T00:00:00.000 |
112 | 14.5 |
113 | 15.5 |
114 | 0 |
115 | 1 |
116 | Task 3 |
117 |
118 |
119 | 2007-05-08T00:00:00.000 |
120 | 8.75 |
121 | 9.25 |
122 | 0 |
123 | 0.5 |
124 | Task 3 |
125 |
126 |
127 | 2007-05-14T00:00:00.000 |
128 | 21.75 |
129 | 22.25 |
130 | 0 |
131 | 0.5 |
132 | Task 3 |
133 |
134 |
135 | 2007-05-14T00:00:00.000 |
136 | 22.5 |
137 | 23 |
138 | 0 |
139 | 0.5 |
140 | Task 3 |
141 |
142 |
143 | 2007-05-15T00:00:00.000 |
144 | 11.75 |
145 | 12.75 |
146 | 0 |
147 | 1 |
148 | Task 3 |
149 |
150 |
151 | 2007-05-07T00:00:00.000 |
152 | 10.75 |
153 | 10.75 |
154 | 0 |
155 | 0 |
156 | Task 1 |
157 |
158 |
159 | |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
169 |
170 |
171 |
172 | 9
173 | 300
174 | 300
175 |
176 |
177 | False
178 | False
179 |
180 |
181 |
182 |
185 |
186 |
187 |
188 |
189 |
191 |
192 |
193 |
194 | 9
195 | 300
196 | 300
197 |
198 | 60
199 | False
200 | False
201 |
202 |
203 |
204 |
207 |
208 |
209 |
210 |
211 |
213 |
214 |
215 |
216 | 9
217 | 300
218 | 300
219 |
220 | 60
221 | False
222 | False
223 |
224 |
225 |
226 |
--------------------------------------------------------------------------------
/test/simple_spreadsheet_from_italo.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:40:40Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 344
19 | False
20 | False
21 |
22 |
23 |
31 |
35 |
40 |
44 |
47 |
51 |
54 |
57 |
58 |
59 |
61 |
62 | 1 |
63 | 1 |
64 | 1 |
65 |
66 |
67 | 1 |
68 | 2 |
69 | 1 |
70 |
71 |
72 | 1 |
73 | 3 |
74 | 1 |
75 |
76 |
77 | A |
78 | A |
79 | A |
80 |
81 |
82 | 0.01 |
83 | 0.01 |
84 | 0.01 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
94 |
95 |
96 |
97 | 9
98 | 300
99 | 300
100 |
101 |
102 |
103 |
104 | 3
105 | 4
106 | 2
107 |
108 |
109 | False
110 | False
111 |
112 |
113 |
114 |
116 |
117 | Date |
118 | Start time |
119 | End time |
120 | Pause |
121 | Sum |
122 | Comment |
123 |
124 |
125 | 2007-05-07T00:00:00.000 |
126 | 9.25 |
127 | 10.25 |
128 | 0 |
129 | 1 |
130 | Task 1 |
131 |
132 |
133 | 2007-05-07T00:00:00.000 |
134 | 10.75 |
135 | 12.5 |
136 | 0 |
137 | 1.75 |
138 | Task 1 |
139 |
140 |
141 | 2007-05-07T00:00:00.000 |
142 | 18 |
143 | 19 |
144 | 0 |
145 | 1 |
146 | Task 2 |
147 |
148 |
149 | 2007-05-07T00:00:00.000 |
150 | 9.25 |
151 | 10.25 |
152 | 0 |
153 | 1 |
154 | Task 1 |
155 |
156 |
157 | 2007-05-07T00:00:00.000 |
158 | 9.25 |
159 | 10.25 |
160 | 0 |
161 | 1 |
162 | Task 1 |
163 |
164 |
165 | 2007-05-07T00:00:00.000 |
166 | 9.25 |
167 | 10.25 |
168 | 0 |
169 | 1 |
170 | Task 1 |
171 |
172 |
173 | 2007-05-07T00:00:00.000 |
174 | 9.25 |
175 | 10.25 |
176 | 0 |
177 | 1 |
178 | Task 1 |
179 |
180 |
181 | 2007-05-07T00:00:00.000 |
182 | 9.25 |
183 | 10.25 |
184 | 0 |
185 | 1 |
186 | Task 1 |
187 |
188 |
189 | 2007-05-07T00:00:00.000 |
190 | 9.25 |
191 | 10.25 |
192 | 0 |
193 | 1 |
194 | Task 1 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
203 |
204 |
205 |
206 | 9
207 | 300
208 | 300
209 |
210 |
211 |
212 | 3
213 | 21
214 | 3
215 |
216 |
217 | False
218 | False
219 |
220 |
221 |
222 |
225 |
226 |
227 |
228 |
229 |
231 |
232 |
233 |
234 | 9
235 | 300
236 | 300
237 |
238 | False
239 | False
240 |
241 |
242 |
243 |
--------------------------------------------------------------------------------
/lib/roo/google.rb:
--------------------------------------------------------------------------------
1 | require 'gdata/spreadsheet'
2 | require 'xml'
3 |
4 | class GoogleHTTPError < RuntimeError; end
5 | class GoogleReadError < RuntimeError; end
6 | class GoogleWriteError < RuntimeError; end
7 |
8 | # overwrite some methods from the gdata-gem:
9 | module GData
10 | class Spreadsheet < GData::Base
11 |
12 | def visibility
13 | @headers ? "private" : "public"
14 | end
15 |
16 | def projection
17 | @headers ? "full" : "values"
18 | end
19 |
20 | #-- modified
21 | def evaluate_cell(cell, sheet_no=1)
22 | raise ArgumentError, "invalid cell: #{cell}" unless cell
23 | raise ArgumentError, "invalid sheet_no: #{sheet_no}" unless sheet_no >0 and sheet_no.class == Fixnum
24 | path = "/feeds/cells/#{@spreadsheet_id}/#{sheet_no}/#{visibility}/#{projection}/#{cell}"
25 | doc = Hpricot(request(path))
26 | result = (doc/"content").inner_html
27 | end
28 |
29 | #-- new
30 | def sheetlist
31 | path = "/feeds/worksheets/#{@spreadsheet_id}/#{visibility}/#{projection}"
32 | doc = Hpricot(request(path))
33 | result = []
34 | (doc/"content").each { |elem|
35 | result << elem.inner_html
36 | }
37 | if result.size == 0
38 | if (doc/"h2").inner_html =~ /Error/
39 | raise GoogleHTTPError, "#{(doc/'h2').inner_html}: #{(doc/'title').inner_html} [key '#{@spreadsheet_id}']"
40 | else
41 | raise GoogleReadError, "#{doc} [key '#{@spreadsheet_id}']"
42 | end
43 | end
44 | result
45 | end
46 |
47 | #-- new
48 | #@@ added sheet_no to definition
49 | def save_entry_roo(entry, sheet_no)
50 | raise GoogleWriteError, "unable to write to public spreadsheets" if visibility == 'public'
51 | path = "/feeds/cells/#{@spreadsheet_id}/#{sheet_no}/#{visibility}/#{projection}"
52 | post(path, entry)
53 | end
54 |
55 | #-- new
56 | def entry_roo(formula, row=1, col=1)
57 | <<-XML
58 |
59 |
60 |
61 | XML
62 | end
63 |
64 | #-- new
65 | #@@ added sheet_no to definition
66 | def add_to_cell_roo(row,col,value, sheet_no=1)
67 | save_entry_roo(entry_roo(value,row,col), sheet_no)
68 | end
69 |
70 | #-- new
71 | def get_one_sheet
72 | path = "/feeds/cells/#{@spreadsheet_id}/1/#{visibility}/#{projection}"
73 | doc = Hpricot(request(path))
74 | end
75 |
76 | #new
77 | def oben_unten_links_rechts(sheet_no)
78 | path = "/feeds/cells/#{@spreadsheet_id}/#{sheet_no}/#{visibility}/#{projection}"
79 | doc = Hpricot(request(path))
80 | rows = []
81 | cols = []
82 | (doc/"gs:cell").each {|item|
83 | rows.push item['row'].to_i
84 | cols.push item['col'].to_i
85 | }
86 | return rows.min, rows.max, cols.min, cols.max
87 | end
88 |
89 | def fulldoc(sheet_no)
90 | path = "/feeds/cells/#{@spreadsheet_id}/#{sheet_no}/#{visibility}/#{projection}"
91 | doc = Hpricot(request(path))
92 | return doc
93 | end
94 |
95 | end # class
96 | end # module
97 |
98 | class Google < GenericSpreadsheet
99 | attr_accessor :date_format, :datetime_format
100 |
101 | # Creates a new Google spreadsheet object.
102 | def initialize(spreadsheetkey,user=nil,password=nil)
103 | @filename = spreadsheetkey
104 | @spreadsheetkey = spreadsheetkey
105 | @user = user
106 | @password = password
107 | unless user
108 | user = ENV['GOOGLE_MAIL']
109 | end
110 | unless password
111 | password = ENV['GOOGLE_PASSWORD']
112 | end
113 | @cell = Hash.new {|h,k| h[k]=Hash.new}
114 | @cell_type = Hash.new {|h,k| h[k]=Hash.new}
115 | @formula = Hash.new
116 | @first_row = Hash.new
117 | @last_row = Hash.new
118 | @first_column = Hash.new
119 | @last_column = Hash.new
120 | @cells_read = Hash.new
121 | @header_line = 1
122 | @date_format = '%d/%m/%Y'
123 | @datetime_format = '%d/%m/%Y %H:%M:%S'
124 | @time_format = '%H:%M:%S'
125 | @gs = GData::Spreadsheet.new(spreadsheetkey)
126 | @gs.authenticate(user, password) unless user.empty? || password.empty?
127 | @sheetlist = @gs.sheetlist
128 | @default_sheet = self.sheets.first
129 | end
130 |
131 | # returns an array of sheet names in the spreadsheet
132 | def sheets
133 | @sheetlist
134 | end
135 |
136 | def date?(string)
137 | begin
138 | Date.strptime(string, @date_format)
139 | true
140 | rescue
141 | false
142 | end
143 | end
144 |
145 | # is String a time with format HH:MM:SS?
146 | def time?(string)
147 | begin
148 | DateTime.strptime(string, @time_format)
149 | true
150 | rescue
151 | false
152 | end
153 | end
154 |
155 | def datetime?(string)
156 | begin
157 | DateTime.strptime(string, @datetime_format)
158 | true
159 | rescue
160 | false
161 | end
162 | end
163 |
164 | def numeric?(string)
165 | string =~ /^[0-9]+[\.]*[0-9]*$/
166 | end
167 |
168 | def timestring_to_seconds(value)
169 | hms = value.split(':')
170 | hms[0].to_i*3600 + hms[1].to_i*60 + hms[2].to_i
171 | end
172 |
173 | # Returns the content of a spreadsheet-cell.
174 | # (1,1) is the upper left corner.
175 | # (1,1), (1,'A'), ('A',1), ('a',1) all refers to the
176 | # cell at the first line and first row.
177 | def cell(row, col, sheet=nil)
178 | sheet = @default_sheet unless sheet
179 | check_default_sheet #TODO: 2007-12-16
180 | read_cells(sheet) unless @cells_read[sheet]
181 | row,col = normalize(row,col)
182 | value = @cell[sheet]["#{row},#{col}"]
183 | if celltype(row,col,sheet) == :date
184 | begin
185 | return Date.strptime(value, @date_format)
186 | rescue ArgumentError
187 | raise "Invalid Date #{sheet}[#{row},#{col}] #{value} using format '{@date_format}'"
188 | end
189 | elsif celltype(row,col,sheet) == :datetime
190 | begin
191 | return DateTime.strptime(value, @datetime_format)
192 | rescue ArgumentError
193 | raise "Invalid DateTime #{sheet}[#{row},#{col}] #{value} using format '{@datetime_format}'"
194 | end
195 | end
196 | return value
197 | end
198 |
199 | # returns the type of a cell:
200 | # * :float
201 | # * :string
202 | # * :date
203 | # * :percentage
204 | # * :formula
205 | # * :time
206 | # * :datetime
207 | def celltype(row, col, sheet=nil)
208 | sheet = @default_sheet unless sheet
209 | read_cells(sheet) unless @cells_read[sheet]
210 | row,col = normalize(row,col)
211 | if @formula.size > 0 && @formula[sheet]["#{row},#{col}"]
212 | return :formula
213 | else
214 | @cell_type[sheet]["#{row},#{col}"]
215 | end
216 | end
217 |
218 | # Returns the formula at (row,col).
219 | # Returns nil if there is no formula.
220 | # The method #formula? checks if there is a formula.
221 | def formula(row,col,sheet=nil)
222 | sheet = @default_sheet unless sheet
223 | read_cells(sheet) unless @cells_read[sheet]
224 | row,col = normalize(row,col)
225 | if @formula[sheet]["#{row},#{col}"] == nil
226 | return nil
227 | else
228 | return @formula[sheet]["#{row},#{col}"]
229 | end
230 | end
231 |
232 | # true, if there is a formula
233 | def formula?(row,col,sheet=nil)
234 | sheet = @default_sheet unless sheet
235 | read_cells(sheet) unless @cells_read[sheet]
236 | row,col = normalize(row,col)
237 | formula(row,col) != nil
238 | end
239 |
240 | # returns each formula in the selected sheet as an array of elements
241 | # [row, col, formula]
242 | def formulas(sheet=nil)
243 | theformulas = Array.new
244 | sheet = @default_sheet unless sheet
245 | read_cells(sheet) unless @cells_read[sheet]
246 | first_row(sheet).upto(last_row(sheet)) {|row|
247 | first_column(sheet).upto(last_column(sheet)) {|col|
248 | if formula?(row,col,sheet)
249 | f = [row, col, formula(row,col,sheet)]
250 | theformulas << f
251 | end
252 | }
253 | }
254 | theformulas
255 | end
256 |
257 | # true, if the cell is empty
258 | def empty?(row, col, sheet=nil)
259 | value = cell(row, col, sheet)
260 | return true unless value
261 | return false if value.class == Date # a date is never empty
262 | return false if value.class == Float
263 | return false if celltype(row,col,sheet) == :time
264 | value.empty?
265 | end
266 |
267 | # sets the cell to the content of 'value'
268 | # a formula can be set in the form of '=SUM(...)'
269 | def set_value(row,col,value,sheet=nil)
270 | sheet = @default_sheet unless sheet
271 | raise RangeError, "sheet not set" unless sheet
272 | #@@ Set and pass sheet_no
273 | begin
274 | sheet_no = sheets.index(sheet)+1
275 | rescue
276 | raise RangeError, "invalid sheet '"+sheet.to_s+"'"
277 | end
278 | row,col = normalize(row,col)
279 | @gs.add_to_cell_roo(row,col,value,sheet_no)
280 | # re-read the portion of the document that has changed
281 | if @cells_read[sheet]
282 | key = "#{row},#{col}"
283 | (value, value_type) = determine_datatype(value.to_s)
284 | @cell[sheet][key] = value
285 | @cell_type[sheet][key] = value_type
286 | end
287 | end
288 |
289 | # returns the first non-empty row in a sheet
290 | def first_row(sheet=nil)
291 | sheet = @default_sheet unless sheet
292 | unless @first_row[sheet]
293 | sheet_no = sheets.index(sheet) + 1
294 | @first_row[sheet], @last_row[sheet], @first_column[sheet], @last_column[sheet] = @gs.oben_unten_links_rechts(sheet_no)
295 | end
296 | return @first_row[sheet]
297 | end
298 |
299 | # returns the last non-empty row in a sheet
300 | def last_row(sheet=nil)
301 | sheet = @default_sheet unless sheet
302 | unless @last_row[sheet]
303 | sheet_no = sheets.index(sheet) + 1
304 | @first_row[sheet], @last_row[sheet], @first_column[sheet], @last_column[sheet] = @gs.oben_unten_links_rechts(sheet_no)
305 | end
306 | return @last_row[sheet]
307 | end
308 |
309 | # returns the first non-empty column in a sheet
310 | def first_column(sheet=nil)
311 | sheet = @default_sheet unless sheet
312 | unless @first_column[sheet]
313 | sheet_no = sheets.index(sheet) + 1
314 | @first_row[sheet], @last_row[sheet], @first_column[sheet], @last_column[sheet] = @gs.oben_unten_links_rechts(sheet_no)
315 | end
316 | return @first_column[sheet]
317 | end
318 |
319 | # returns the last non-empty column in a sheet
320 | def last_column(sheet=nil)
321 | sheet = @default_sheet unless sheet
322 | unless @last_column[sheet]
323 | sheet_no = sheets.index(sheet) + 1
324 | @first_row[sheet], @last_row[sheet], @first_column[sheet], @last_column[sheet] = @gs.oben_unten_links_rechts(sheet_no)
325 | end
326 | return @last_column[sheet]
327 | end
328 |
329 | private
330 |
331 | # read all cells in a sheet.
332 | def read_cells(sheet=nil)
333 | sheet = @default_sheet unless sheet
334 | raise RangeError, "illegal sheet <#{sheet}>" unless sheets.index(sheet)
335 | sheet_no = sheets.index(sheet)+1
336 | xml = @gs.fulldoc(sheet_no).to_s
337 | doc = XML::Parser.string(xml).parse
338 | doc.find("//*[local-name()='cell']").each do |item|
339 | row = item['row']
340 | col = item['col']
341 | key = "#{row},#{col}"
342 | string_value = item['inputvalue'] || item['inputValue']
343 | numeric_value = item['numericvalue'] || item['numericValue']
344 | (value, value_type) = determine_datatype(string_value, numeric_value)
345 | @cell[sheet][key] = value unless value == "" or value == nil
346 | @cell_type[sheet][key] = value_type
347 | @formula[sheet] = {} unless @formula[sheet]
348 | @formula[sheet][key] = string_value if value_type == :formula
349 | end
350 | @cells_read[sheet] = true
351 | end
352 |
353 | def determine_datatype(val, numval=nil)
354 | if val.nil? || val[0,1] == '='
355 | ty = :formula
356 | if numeric?(numval)
357 | val = numval.to_f
358 | else
359 | val = numval
360 | end
361 | else
362 | if datetime?(val)
363 | ty = :datetime
364 | elsif date?(val)
365 | ty = :date
366 | elsif numeric?(val)
367 | ty = :float
368 | val = val.to_f
369 | elsif time?(val)
370 | ty = :time
371 | val = timestring_to_seconds(val)
372 | else
373 | ty = :string
374 | end
375 | end
376 | return val, ty
377 | end
378 |
379 | end # class
380 |
--------------------------------------------------------------------------------
/test/numbers1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Licensed User
10 | 2009-12-22T17:39:48Z
11 | 12.00
12 |
13 |
14 | 8190
15 | 16380
16 | 0
17 | 0
18 | 344
19 | 4
20 | False
21 | False
22 |
23 |
24 |
32 |
35 |
38 |
41 |
42 |
43 |
45 |
46 | 1 |
47 | 2 |
48 | 3 |
49 | 4 |
50 | 10 |
51 |
52 |
53 | 5 |
54 | 6 |
55 | 7 |
56 | 8 |
57 | 9 |
58 | test |
59 | 11 |
60 |
61 |
62 | 10 |
63 | 11 |
64 | 12 |
65 | 13 |
66 | 14 |
67 |
68 |
69 | 1961-11-21T00:00:00.000 |
70 |
71 |
72 | tata |
73 |
74 |
75 | thisisc8 |
76 |
77 |
78 | thisisd9 |
79 |
80 |
81 | thisisa11 |
82 |
83 |
84 | 41 |
85 | 42 |
86 | 43 |
87 | 44 |
88 | 45 |
89 |
90 |
91 | 41 |
92 | 42 |
93 | 43 |
94 | 44 |
95 | 45 |
96 |
97 |
98 | einundvierzig |
99 | zweiundvierzig |
100 | dreiundvierzig |
101 | vierundvierzig |
102 | fuenfundvierzig |
103 |
104 |
105 | 2007-05-31T00:00:00.000 |
106 | dies hier als Date-Objekt |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
116 |
117 |
118 |
119 | 9
120 | 300
121 | 300
122 |
123 |
124 |
125 | 3
126 | 8
127 | 2
128 |
129 |
130 | False
131 | False
132 |
133 |
134 |
135 |
137 |
138 | I am sheet 2 |
139 |
140 |
141 | 3 |
142 |
143 |
144 | 7 |
145 |
146 |
147 | 9 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
156 |
157 |
158 |
159 | 9
160 | 300
161 | 300
162 |
163 |
164 |
165 | 3
166 | 4
167 | 1
168 |
169 |
170 | False
171 | False
172 |
173 |
174 |
175 |
177 |
178 | ganz weit rechts geht’s weiter |
179 | i am AA |
180 | i am AB |
181 | i am BA |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
190 |
191 |
192 |
193 | 9
194 | 300
195 | 300
196 |
197 | 48
198 |
199 |
200 | 3
201 | 1
202 | 52
203 |
204 |
205 | False
206 | False
207 |
208 |
209 |
210 |
212 |
213 | 2007-06-16T00:00:00.000 |
214 | 10 |
215 | 10 |
216 | 10 |
217 | 10 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
226 |
227 |
228 |
229 | 9
230 | 300
231 | 300
232 |
233 |
234 |
235 | 3
236 | 1
237 | 4
238 |
239 |
240 | False
241 | False
242 |
243 |
244 |
245 |
247 |
248 | 1 |
249 | 5 |
250 | 5 |
251 |
252 |
253 | 2 |
254 |
255 |
256 | 3 |
257 |
258 |
259 | 2007-11-21T00:00:00.000 |
260 | 2007-11-21T00:00:00.000 |
261 | 2007-11-21T00:00:00.000 |
262 | 2007-11-21T00:00:00.000 |
263 | 2007-11-21T00:00:00.000 |
264 |
265 |
266 | 42 |
267 | 42 |
268 | 42 |
269 | 42 |
270 | 42 |
271 |
272 |
273 | ABC |
274 | ABC |
275 | ABC |
276 | ABC |
277 | ABC |
278 |
279 |
280 | |
281 | |
282 | |
283 | |
284 | |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
293 |
294 |
295 |
296 | 9
297 | 300
298 | 300
299 |
300 |
301 |
302 |
303 | 3
304 | 6
305 | 4
306 |
307 |
308 | False
309 | False
310 |
311 |
312 |
313 |
--------------------------------------------------------------------------------
/lib/roo/excel2003xml.rb:
--------------------------------------------------------------------------------
1 | require 'xml'
2 | require 'fileutils'
3 | require 'zip/zipfilesystem'
4 | require 'date'
5 | require 'base64'
6 | require 'cgi'
7 |
8 | class Excel2003XML < GenericSpreadsheet
9 |
10 | @@nr = 0
11 |
12 | # initialization and opening of a spreadsheet file
13 | # values for packed: :zip
14 | def initialize(filename, packed=nil, file_warning=:error)
15 | @file_warning = file_warning
16 | super()
17 | @tmpdir = "oo_"+$$.to_s
18 | @tmpdir = File.join(ENV['ROO_TMP'], @tmpdir) if ENV['ROO_TMP']
19 | unless File.exists?(@tmpdir)
20 | FileUtils::mkdir(@tmpdir)
21 | end
22 | filename = open_from_uri(filename) if filename[0,7] == "http://"
23 | filename = unzip(filename) if packed and packed == :zip
24 | begin
25 | file_type_check(filename,'.xml','an Excel 2003 XML')
26 | @cells_read = Hash.new
27 | @filename = filename
28 | unless File.file?(@filename)
29 | raise IOError, "file #{@filename} does not exist"
30 | end
31 | @doc = XML::Parser.file(@filename).parse
32 | ensure
33 | FileUtils::rm_r(@tmpdir)
34 | end
35 | @default_sheet = self.sheets.first
36 | @cell = Hash.new
37 | @cell_type = Hash.new
38 | @formula = Hash.new
39 | @first_row = Hash.new
40 | @last_row = Hash.new
41 | @first_column = Hash.new
42 | @last_column = Hash.new
43 | @header_line = 1
44 | @style = Hash.new
45 | @style_defaults = Hash.new { |h,k| h[k] = [] }
46 | @style_definitions = Hash.new
47 | read_styles
48 | end
49 |
50 | # Returns the content of a spreadsheet-cell.
51 | # (1,1) is the upper left corner.
52 | # (1,1), (1,'A'), ('A',1), ('a',1) all refers to the
53 | # cell at the first line and first row.
54 | def cell(row, col, sheet=nil)
55 | sheet = @default_sheet unless sheet
56 | read_cells(sheet) unless @cells_read[sheet]
57 | row,col = normalize(row,col)
58 | if celltype(row,col,sheet) == :date
59 | yyyy,mm,dd = @cell[sheet][[row,col]].split('-')
60 | return Date.new(yyyy.to_i,mm.to_i,dd.to_i)
61 | end
62 | @cell[sheet][[row,col]]
63 | end
64 |
65 | # Returns the formula at (row,col).
66 | # Returns nil if there is no formula.
67 | # The method #formula? checks if there is a formula.
68 | def formula(row,col,sheet=nil)
69 | sheet = @default_sheet unless sheet
70 | read_cells(sheet) unless @cells_read[sheet]
71 | row,col = normalize(row,col)
72 | if @formula[sheet][[row,col]] == nil
73 | return nil
74 | else
75 | return @formula[sheet][[row,col]]["oooc:".length..-1]
76 | end
77 | end
78 |
79 | # true, if there is a formula
80 | def formula?(row,col,sheet=nil)
81 | sheet = @default_sheet unless sheet
82 | read_cells(sheet) unless @cells_read[sheet]
83 | row,col = normalize(row,col)
84 | formula(row,col) != nil
85 | end
86 |
87 | class Font
88 | attr_accessor :bold, :italic, :underline
89 |
90 | def bold?
91 | @bold == '1'
92 | end
93 |
94 | def italic?
95 | @italic == '1'
96 | end
97 |
98 | def underline?
99 | @underline != nil
100 | end
101 | end
102 |
103 | # Given a cell, return the cell's style
104 | def font(row, col, sheet=nil)
105 | sheet = @default_sheet unless sheet
106 | read_cells(sheet) unless @cells_read[sheet]
107 | row,col = normalize(row,col)
108 | style_name = @style[sheet][[row,col]] || @style_defaults[sheet][col - 1] || 'Default'
109 | @style_definitions[style_name]
110 | end
111 |
112 | # set a cell to a certain value
113 | # (this will not be saved back to the spreadsheet file!)
114 | def set(row,col,value,sheet=nil) #:nodoc:
115 | sheet = @default_sheet unless sheet
116 | read_cells(sheet) unless @cells_read[sheet]
117 | row,col = normalize(row,col)
118 | set_value(row,col,value,sheet)
119 | if value.class == Fixnum
120 | set_type(row,col,:float,sheet)
121 | elsif value.class == String
122 | set_type(row,col,:string,sheet)
123 | elsif value.class == Float
124 | set_type(row,col,:string,sheet)
125 | else
126 | raise ArgumentError, "Type for "+value.to_s+" not set"
127 | end
128 | end
129 |
130 | # returns the type of a cell:
131 | # * :float
132 | # * :string
133 | # * :date
134 | # * :percentage
135 | # * :formula
136 | # * :time
137 | # * :datetime
138 | def celltype(row,col,sheet=nil)
139 | sheet = @default_sheet unless sheet
140 | read_cells(sheet) unless @cells_read[sheet]
141 | row,col = normalize(row,col)
142 | if @formula[sheet][[row,col]]
143 | return :formula
144 | else
145 | @cell_type[sheet][[row,col]]
146 | end
147 | end
148 |
149 | def sheets
150 | return_sheets = []
151 | @doc.find("//ss:Worksheet").each do |sheet|
152 | return_sheets << sheet.attributes['Name']
153 | end
154 | return_sheets
155 | end
156 |
157 | # version of the openoffice document
158 | # at 2007 this is always "1.0"
159 | def officeversion
160 | oo_version
161 | @officeversion
162 | end
163 |
164 | # shows the internal representation of all cells
165 | # mainly for debugging purposes
166 | def to_s(sheet=nil)
167 | sheet = @default_sheet unless sheet
168 | read_cells(sheet) unless @cells_read[sheet]
169 | @cell[sheet].inspect
170 | end
171 |
172 | # save spreadsheet
173 | def save #:nodoc:
174 | 42
175 | end
176 |
177 | # returns each formula in the selected sheet as an array of elements
178 | # [row, col, formula]
179 | def formulas(sheet=nil)
180 | theformulas = Array.new
181 | sheet = @default_sheet unless sheet
182 | read_cells(sheet) unless @cells_read[sheet]
183 | first_row(sheet).upto(last_row(sheet)) {|row|
184 | first_column(sheet).upto(last_column(sheet)) {|col|
185 | if formula?(row,col,sheet)
186 | f = [row, col, formula(row,col,sheet)]
187 | theformulas << f
188 | end
189 | }
190 | }
191 | theformulas
192 | end
193 |
194 | private
195 |
196 | # read the version of the OO-Version
197 | def oo_version
198 | @doc.find("//*[local-name()='document-content']").each do |office|
199 | @officeversion = office.attributes['version']
200 | end
201 | end
202 |
203 | # helper function to set the internal representation of cells
204 | def set_cell_values(sheet,x,y,i,v,vt,formula,table_cell,str_v,style_name)
205 | key = [y,x+i]
206 | @cell_type[sheet] = {} unless @cell_type[sheet]
207 | @cell_type[sheet][key] = vt
208 | @formula[sheet] = {} unless @formula[sheet]
209 | @formula[sheet][key] = formula if formula
210 | @cell[sheet] = {} unless @cell[sheet]
211 | @style[sheet] = {} unless @style[sheet]
212 | @style[sheet][key] = style_name
213 | case @cell_type[sheet][key]
214 | when :float
215 | @cell[sheet][key] = v.to_f
216 | when :string
217 | @cell[sheet][key] = str_v
218 | when :datetime
219 | @cell[sheet][key] = DateTime.parse(v)
220 | @cell_type[sheet][key] = :datetime
221 | when :percentage
222 | @cell[sheet][key] = v.to_f
223 | # when :time
224 | # hms = v.split(':')
225 | # @cell[sheet][key] = hms[0].to_i*3600 + hms[1].to_i*60 + hms[2].to_i
226 | else
227 | @cell[sheet][key] = v
228 | end
229 | end
230 |
231 | # read all cells in the selected sheet
232 | #--
233 | # the following construct means '4 blanks'
234 | # some content
235 | #++
236 | def read_cells(sheet=nil)
237 | sheet = @default_sheet unless sheet
238 | sheet_found = false
239 | raise ArgumentError, "Error: sheet '#{sheet||'nil'}' not valid" if @default_sheet == nil and sheet==nil
240 | raise RangeError unless self.sheets.include? sheet
241 | @doc.find("ss:Worksheet[@ss:Name='#{sheet}']").each do |ws|
242 | sheet_found = true
243 | row = 1
244 | col = 1
245 | column_attributes = {}
246 | idx = 0
247 | ws.find('.//ss:Column').each do |c|
248 | column_attributes[(idx += 1).to_s] = c.attributes['StyleID']
249 | end
250 | ws.find('.//ss:Row').each do |r|
251 | skip_to_row = r.attributes['Index'].to_i
252 | row = skip_to_row if skip_to_row > 0
253 | style_name = r.attributes['StyleID'] if r.attributes['StyleID']
254 | r.each do |c|
255 | next unless c.name == 'Cell'
256 | skip_to_col = c.attributes['Index'].to_i
257 | col = skip_to_col if skip_to_col > 0
258 | if c.attributes['StyleID']
259 | style_name = c.attributes['StyleID']
260 | elsif
261 | style_name ||= column_attributes[c.attributes['Index']]
262 | end
263 | c.each_element do |cell|
264 | formula = nil
265 | if cell.name == 'Data'
266 | formula = cell.attributes['Formula']
267 | vt = cell.attributes['Type'].downcase.to_sym
268 | v = cell.content
269 | str_v = v
270 | case vt
271 | when :number
272 | v = v.to_f
273 | vt = :float
274 | when :datetime
275 | if v =~ /^1899-12-31T(\d{2}:\d{2}:\d{2})/
276 | v = $1
277 | vt = :time
278 | elsif v =~ /([^T]+)T00:00:00.000/
279 | v = $1
280 | vt = :date
281 | end
282 | when :boolean
283 | v = cell.attributes['boolean-value']
284 | end
285 | end
286 | set_cell_values(sheet,col,row,0,v,vt,formula,cell,str_v,style_name)
287 | end
288 | col += 1
289 | end
290 | row += 1
291 | col = 1
292 | end
293 | end
294 | if !sheet_found
295 | raise RangeError
296 | end
297 | @cells_read[sheet] = true
298 | end
299 |
300 | def read_styles
301 | @doc.find("ss:Styles").each do |styles|
302 | styles.find('.//ss:Style').each do |style|
303 | style_id = style.attributes['ID']
304 | @style_definitions[style_id] = Excel2003XML::Font.new
305 | font = style.find_first('.//ss:Font')
306 | if font
307 | @style_definitions[style_id].bold = font.attributes['Bold']
308 | @style_definitions[style_id].italic = font.attributes['Italic']
309 | @style_definitions[style_id].underline = font.attributes['Underline']
310 | end
311 | end
312 | end
313 | end
314 |
315 | # Checks if the default_sheet exists. If not an RangeError exception is
316 | # raised
317 | def check_default_sheet
318 | sheet_found = false
319 | raise ArgumentError, "Error: default_sheet not set" if @default_sheet == nil
320 | sheet_found = true if sheets.include?(@default_sheet)
321 | if ! sheet_found
322 | raise RangeError, "sheet '#{@default_sheet}' not found"
323 | end
324 | end
325 |
326 | def process_zipfile(zip, path='')
327 | if zip.file.file? path
328 | if path == "content.xml"
329 | open(File.join(@tmpdir, @file_nr.to_s+'_roo_content.xml'),'wb') {|f|
330 | f << zip.read(path)
331 | }
332 | end
333 | else
334 | unless path.empty?
335 | path += '/'
336 | end
337 | zip.dir.foreach(path) do |filename|
338 | process_zipfile(zip, path+filename)
339 | end
340 | end
341 | end
342 |
343 | def extract_content
344 | Zip::ZipFile.open(@filename) do |zip|
345 | process_zipfile(zip)
346 | end
347 | end
348 |
349 | def set_value(row,col,value,sheet=nil)
350 | sheet = @default_value unless sheet
351 | @cell[sheet][[row,col]] = value
352 | end
353 |
354 | def set_type(row,col,type,sheet=nil)
355 | sheet = @default_value unless sheet
356 | @cell_type[sheet][[row,col]] = type
357 | end
358 |
359 | A_ROO_TYPE = {
360 | "float" => :float,
361 | "string" => :string,
362 | "date" => :date,
363 | "percentage" => :percentage,
364 | "time" => :time,
365 | }
366 |
367 | def Openoffice.oo_type_2_roo_type(ootype)
368 | return A_ROO_TYPE[ootype]
369 | end
370 |
371 | # helper method to convert compressed spaces and other elements within
372 | # an text into a string
373 | def children_to_string(children)
374 | result = ''
375 | children.each {|child|
376 | if child.text?
377 | result = result + child.content
378 | else
379 | if child.name == 's'
380 | compressed_spaces = child.attributes['c'].to_i
381 | # no explicit number means a count of 1:
382 | if compressed_spaces == 0
383 | compressed_spaces = 1
384 | end
385 | result = result + " "*compressed_spaces
386 | else
387 | result = result + child.content
388 | end
389 | end
390 | }
391 | result
392 | end
393 |
394 | end # class
395 |
--------------------------------------------------------------------------------
/lib/roo/excel.rb:
--------------------------------------------------------------------------------
1 | require 'spreadsheet'
2 | CHARGUESS = begin
3 | require 'charguess'
4 | true
5 | rescue LoadError => e
6 | false
7 | end
8 |
9 | # The Spreadsheet library has a bug in handling Excel
10 | # base dates so if the file is a 1904 base date then
11 | # dates are off by a day. 1900 base dates work fine
12 | module Spreadsheet
13 | module Excel
14 | class Row < Spreadsheet::Row
15 | def _datetime data # :nodoc:
16 | return data if data.is_a?(DateTime)
17 | base = @worksheet.date_base
18 | date = base + data.to_f
19 | hour = (data % 1) * 24
20 | min = (hour % 1) * 60
21 | sec = ((min % 1) * 60).round
22 | min = min.floor
23 | hour = hour.floor
24 | if sec > 59
25 | sec = 0
26 | min += 1
27 | end
28 | if min > 59
29 | min = 0
30 | hour += 1
31 | end
32 | if hour > 23
33 | hour = 0
34 | date += 1
35 | end
36 | if LEAP_ERROR > base
37 | date -= 1
38 | end
39 | DateTime.new(date.year, date.month, date.day, hour, min, sec)
40 | end
41 | public :_date
42 | public :_datetime
43 | end
44 | # patch for ruby-spreadsheet parsing formulas
45 | class Reader
46 | def read_formula worksheet, addr, work
47 | row, column, xf, rtype, rval, rcheck, opts = work.unpack 'v3CxCx3v2'
48 | formula = Formula.new
49 | formula.shared = (opts & 0x08) > 0
50 | formula.data = work[20..-1]
51 | if rcheck != 0xffff || rtype > 3
52 | value, = work.unpack 'x6E'
53 | unless value
54 | # on architectures where sizeof(double) > 8
55 | value, = work.unpack 'x6e'
56 | end
57 | formula.value = value
58 | elsif rtype == 0
59 | pos, op, len, work = get_next_chunk
60 | if op == :string
61 | formula.value = client read_string(work, 2), @workbook.encoding
62 | else
63 | # This seems to work but I don't know why :). It at least
64 | # seems to correct the case we saw but doubtful it's the right fix
65 | formula.value = client read_string(work[10..-1], 2), @workbook.encoding
66 | end
67 | elsif rtype == 1
68 | formula.value = rval > 0
69 | elsif rtype == 2
70 | formula.value = Error.new rval
71 | else
72 | # leave the Formula value blank
73 | end
74 | set_cell worksheet, row, column, xf, formula
75 | end
76 | end
77 | end
78 | end
79 |
80 |
81 | # ruby-spreadsheet has a font object so we're extending it
82 | # with our own functionality but still providing full access
83 | # to the user for other font information
84 | module ExcelFontExtensions
85 | def bold?(*args)
86 | #From ruby-spreadsheet doc: 100 <= weight <= 1000, bold => 700, normal => 400
87 | case weight
88 | when 700
89 | true
90 | else
91 | false
92 | end
93 | end
94 |
95 | def italic?
96 | italic
97 | end
98 |
99 | def underline?
100 | underline != :none
101 | end
102 |
103 | end
104 |
105 | # Class for handling Excel-Spreadsheets
106 | class Excel < GenericSpreadsheet
107 |
108 | EXCEL_NO_FORMULAS = 'formulas are not supported for excel spreadsheets'
109 |
110 | # Creates a new Excel spreadsheet object.
111 | # Parameter packed: :zip - File is a zip-file
112 | def initialize(filename, packed = nil, file_warning = :error)
113 | super()
114 | @file_warning = file_warning
115 | @tmpdir = "oo_"+$$.to_s
116 | @tmpdir = File.join(ENV['ROO_TMP'], @tmpdir) if ENV['ROO_TMP']
117 | unless File.exists?(@tmpdir)
118 | FileUtils::mkdir(@tmpdir)
119 | end
120 | filename = open_from_uri(filename) if filename[0,7] == "http://"
121 | filename = open_from_stream(filename[7..-1]) if filename[0,7] == "stream:"
122 | filename = unzip(filename) if packed and packed == :zip
123 | begin
124 | file_type_check(filename,'.xls','an Excel')
125 | @filename = filename
126 | unless File.file?(@filename)
127 | raise IOError, "file #{@filename} does not exist"
128 | end
129 | @workbook = Spreadsheet.open(filename)
130 | @default_sheet = self.sheets.first
131 | ensure
132 | #if ENV["roo_local"] != "thomas-p"
133 | FileUtils::rm_r(@tmpdir)
134 | #end
135 | end
136 | @cell = Hash.new
137 | @cell_type = Hash.new
138 | @formula = Hash.new
139 | @first_row = Hash.new
140 | @last_row = Hash.new
141 | @first_column = Hash.new
142 | @last_column = Hash.new
143 | @header_line = 1
144 | @cells_read = Hash.new
145 | @fonts = Hash.new
146 | end
147 |
148 | # returns an array of sheet names in the spreadsheet
149 | def sheets
150 | result = []
151 | @workbook.worksheets.each do |worksheet|
152 | result << normalize_string(worksheet.name)
153 | end
154 | return result
155 | end
156 |
157 | # returns the content of a cell. The upper left corner is (1,1) or ('A',1)
158 | def cell(row,col,sheet=nil)
159 | sheet = @default_sheet unless sheet
160 | raise ArgumentError unless sheet
161 | read_cells(sheet) unless @cells_read[sheet]
162 | raise "should be read" unless @cells_read[sheet]
163 | row,col = normalize(row,col)
164 | if celltype(row,col,sheet) == :date
165 | yyyy,mm,dd = @cell[sheet][[row,col]].split('-')
166 | return Date.new(yyyy.to_i,mm.to_i,dd.to_i)
167 | end
168 | if celltype(row,col,sheet) == :string
169 | return platform_specific_iconv(@cell[sheet][[row,col]])
170 | else
171 | return @cell[sheet][[row,col]]
172 | end
173 | end
174 |
175 | # returns the type of a cell:
176 | # * :float
177 | # * :string,
178 | # * :date
179 | # * :percentage
180 | # * :formula
181 | # * :time
182 | # * :datetime
183 | def celltype(row,col,sheet=nil)
184 | sheet = @default_sheet unless sheet
185 | read_cells(sheet) unless @cells_read[sheet]
186 | row,col = normalize(row,col)
187 | begin
188 | if @formula[sheet][[row,col]]
189 | return :formula
190 | else
191 | @cell_type[sheet][[row,col]]
192 | end
193 | rescue
194 | puts "Error in sheet #{sheet}, row #{row}, col #{col}"
195 | raise
196 | end
197 | end
198 |
199 | # returns NO formula in excel spreadsheets
200 | def formula(row,col,sheet=nil)
201 | raise EXCEL_NO_FORMULAS
202 | end
203 |
204 | # raises an exception because formulas are not supported for excel files
205 | def formula?(row,col,sheet=nil)
206 | raise EXCEL_NO_FORMULAS
207 | end
208 |
209 | # returns NO formulas in excel spreadsheets
210 | def formulas(sheet=nil)
211 | raise EXCEL_NO_FORMULAS
212 | end
213 |
214 | # Given a cell, return the cell's font
215 | def font(row, col, sheet=nil)
216 | sheet = @default_sheet unless sheet
217 | read_cells(sheet) unless @cells_read[sheet]
218 | row,col = normalize(row,col)
219 | @fonts[sheet][[row,col]]
220 | end
221 |
222 | # shows the internal representation of all cells
223 | # mainly for debugging purposes
224 | def to_s(sheet=nil)
225 | sheet = @default_sheet unless sheet
226 | read_cells(sheet) unless @cells_read[sheet]
227 | @cell[sheet].inspect
228 | end
229 |
230 | private
231 |
232 | # converts name of a sheet to index (0,1,2,..)
233 | def sheet_no(name)
234 | return name-1 if name.kind_of?(Fixnum)
235 | i = 0
236 | @workbook.worksheets.each do |worksheet|
237 | return i if name == normalize_string(worksheet.name)
238 | i += 1
239 | end
240 | raise StandardError, "sheet '#{name}' not found"
241 | end
242 |
243 | def empty_row?(row)
244 | content = false
245 | row.compact.each {|elem|
246 | if elem != ''
247 | content = true
248 | end
249 | }
250 | ! content
251 | end
252 |
253 | def empty_column?(col)
254 | content = false
255 | col.compact.each {|elem|
256 | if elem != ''
257 | content = true
258 | end
259 | }
260 | ! content
261 | end
262 |
263 | def normalize_string(value)
264 | value = every_second_null?(value) ? remove_every_second_null(value) : value
265 | if CHARGUESS && encoding = CharGuess::guess(value)
266 | Iconv.new('utf-8', encoding)
267 | else
268 | platform_specific_iconv(value)
269 | end
270 | end
271 |
272 | def platform_specific_iconv(value)
273 | case RUBY_PLATFORM.downcase
274 | when /darwin/
275 | result = Iconv.new('utf-8','utf-8').iconv(value)
276 | when /solaris/
277 | result = Iconv.new('utf-8','utf-8').iconv(value)
278 | when /mswin32/
279 | result = Iconv.new('utf-8','iso-8859-1').iconv(value)
280 | else
281 | result = value
282 | end # case
283 | if every_second_null?(result)
284 | result = remove_every_second_null(result)
285 | end
286 | result
287 | end
288 |
289 | def every_second_null?(str)
290 | result = true
291 | return false if str.length < 2
292 | 0.upto(str.length/2-1) do |i|
293 | c = str[i*2,1]
294 | n = str[i*2+1,1]
295 | if n != "\000"
296 | result = false
297 | break
298 | end
299 | end
300 | result
301 | end
302 |
303 | def remove_every_second_null(str)
304 | result = ''
305 | 0.upto(str.length/2-1) do |i|
306 | c = str[i*2,1]
307 | result += c
308 | end
309 | result
310 | end
311 |
312 | # helper function to set the internal representation of cells
313 | def set_cell_values(sheet,row,col,i,v,vt,formula,tr,font)
314 | #key = "#{y},#{x+i}"
315 | key = [row,col+i]
316 | @cell_type[sheet] = {} unless @cell_type[sheet]
317 | @cell_type[sheet][key] = vt
318 | @formula[sheet] = {} unless @formula[sheet]
319 | @formula[sheet][key] = formula if formula
320 | @cell[sheet] = {} unless @cell[sheet]
321 | @fonts[sheet] = {} unless @fonts[sheet]
322 | @fonts[sheet][key] = font
323 |
324 | case vt # @cell_type[sheet][key]
325 | when :float
326 | @cell[sheet][key] = v.to_f
327 | when :string
328 | @cell[sheet][key] = v
329 | when :date
330 | @cell[sheet][key] = v
331 | when :datetime
332 | @cell[sheet][key] = DateTime.new(v.year,v.month,v.day,v.hour,v.min,v.sec)
333 | when :percentage
334 | @cell[sheet][key] = v.to_f
335 | when :time
336 | @cell[sheet][key] = v
337 | else
338 | @cell[sheet][key] = v
339 | end
340 | end
341 |
342 | # read all cells in the selected sheet
343 | def read_cells(sheet=nil)
344 | sheet = @default_sheet unless sheet
345 | raise ArgumentError, "Error: sheet '#{sheet||'nil'}' not valid" if @default_sheet == nil and sheet==nil
346 | raise RangeError unless self.sheets.include? sheet
347 |
348 | if @cells_read[sheet]
349 | raise "sheet #{sheet} already read"
350 | end
351 |
352 | worksheet = @workbook.worksheet(sheet_no(sheet))
353 | row_index=1
354 | worksheet.each(0) do |row|
355 | (0..row.size).each do |cell_index|
356 | cell = row.at(cell_index)
357 | next if cell.nil? #skip empty cells
358 | next if cell.class == Spreadsheet::Formula && cell.value.nil? # skip empty formla cells
359 | if date_or_time?(row, cell_index)
360 | vt, v = read_cell_date_or_time(row, cell_index)
361 | else
362 | vt, v = read_cell(row, cell_index)
363 | end
364 | formula = tr = nil #TODO:???
365 | col_index = cell_index + 1
366 | font = row.format(cell_index).font
367 | font.extend(ExcelFontExtensions)
368 | set_cell_values(sheet,row_index,col_index,0,v,vt,formula,tr,font)
369 | end #row
370 | row_index += 1
371 | end # worksheet
372 | @cells_read[sheet] = true
373 | end
374 |
375 | # Get the contents of a cell, accounting for the
376 | # way formula stores the value
377 | def read_cell_content(row, idx)
378 | cell = row.at(idx)
379 | cell = cell.value if cell.class == Spreadsheet::Formula
380 | cell
381 | end
382 |
383 | # Test the cell to see if it's a valid date/time.
384 | def date_or_time?(row, idx)
385 | format = row.format(idx)
386 | if format.date_or_time?
387 | cell = read_cell_content(row, idx)
388 | true if Float(cell) > 0 rescue false
389 | else
390 | false
391 | end
392 | end
393 | private :date_or_time?
394 |
395 | # Read the date-time cell and convert to,
396 | # the date-time values for Roo
397 | def read_cell_date_or_time(row, idx)
398 | cell = read_cell_content(row, idx)
399 | cell = cell.to_s.to_f
400 | if cell < 1.0
401 | value_type = :time
402 | f = cell*24.0*60.0*60.0
403 | secs = f.round
404 | h = (secs / 3600.0).floor
405 | secs = secs - 3600*h
406 | m = (secs / 60.0).floor
407 | secs = secs - 60*m
408 | s = secs
409 | value = h*3600+m*60+s
410 | else
411 | if row.at(idx).class == Spreadsheet::Formula
412 | datetime = row._datetime(cell)
413 | else
414 | datetime = row.datetime(idx)
415 | end
416 | if datetime.hour != 0 or
417 | datetime.min != 0 or
418 | datetime.sec != 0
419 | value_type = :datetime
420 | value = datetime
421 | else
422 | value_type = :date
423 | if row.at(idx).class == Spreadsheet::Formula
424 | value = row._date(cell)
425 | else
426 | value = row.date(idx)
427 | end
428 | value = sprintf("%04d-%02d-%02d",value.year,value.month,value.day)
429 | end
430 | end
431 | return value_type, value
432 | end
433 | private :read_cell_date_or_time
434 |
435 | # Read the cell and based on the class,
436 | # return the values for Roo
437 | def read_cell(row, idx)
438 | cell = read_cell_content(row, idx)
439 | case cell
440 | when Float, Integer, Fixnum, Bignum
441 | value_type = :float
442 | value = cell.to_f
443 | when String, TrueClass, FalseClass
444 | value_type = :string
445 | value = cell.to_s
446 | else
447 | value_type = cell.class.to_s.downcase.to_sym
448 | value = nil
449 | end # case
450 | return value_type, value
451 | end
452 | private :read_cell
453 |
454 | #TODO: testing only
455 | # def inject_null_characters(str)
456 | # if str.class != String
457 | # return str
458 | # end
459 | # new_str=''
460 | # 0.upto(str.size-1) do |i|
461 | # new_str += str[i,1]
462 | # new_str += "\000"
463 | # end
464 | # new_str
465 | # end
466 | #
467 |
468 | end
469 |
--------------------------------------------------------------------------------
/lib/roo/openoffice.rb:
--------------------------------------------------------------------------------
1 | require 'xml'
2 | require 'fileutils'
3 | require 'zip/zipfilesystem'
4 | require 'date'
5 | require 'base64'
6 | require 'cgi'
7 |
8 | class Openoffice < GenericSpreadsheet
9 |
10 | @@nr = 0
11 |
12 | # initialization and opening of a spreadsheet file
13 | # values for packed: :zip
14 | def initialize(filename, packed=nil, file_warning=:error) #, create = false)
15 | @file_warning = file_warning
16 | super()
17 | @tmpdir = "oo_"+$$.to_s
18 | @tmpdir = File.join(ENV['ROO_TMP'], @tmpdir) if ENV['ROO_TMP']
19 | unless File.exists?(@tmpdir)
20 | FileUtils::mkdir(@tmpdir)
21 | end
22 | filename = open_from_uri(filename) if filename[0,7] == "http://"
23 | filename = unzip(filename) if packed and packed == :zip
24 | begin
25 | file_type_check(filename,'.ods','an openoffice')
26 | #if create and ! File.exists?(filename)
27 | # self.create_openoffice(filename)
28 | #end
29 | @cells_read = Hash.new
30 | #TODO: @cells_read[:default] = false
31 | @filename = filename
32 | unless File.file?(@filename)
33 | raise IOError, "file #{@filename} does not exist"
34 | end
35 | @@nr += 1
36 | @file_nr = @@nr
37 | extract_content
38 | file = File.new(File.join(@tmpdir, @file_nr.to_s+"_roo_content.xml"))
39 | @doc = XML::Parser.io(file).parse
40 | file.close
41 | ensure
42 | #if ENV["roo_local"] != "thomas-p"
43 | FileUtils::rm_r(@tmpdir)
44 | #end
45 | end
46 | @default_sheet = self.sheets.first
47 | @cell = Hash.new
48 | @cell_type = Hash.new
49 | @formula = Hash.new
50 | @first_row = Hash.new
51 | @last_row = Hash.new
52 | @first_column = Hash.new
53 | @last_column = Hash.new
54 | @style = Hash.new
55 | @style_defaults = Hash.new { |h,k| h[k] = [] }
56 | @style_definitions = Hash.new
57 | @header_line = 1
58 | end
59 |
60 | # creates a new empty openoffice-spreadsheet file
61 | def create_openoffice(filename) #:nodoc:
62 | #TODO: a better way for creating the file contents
63 | # now you have to call mkbase64...rb to create an include file with all
64 | # the empty files in an openoffice zip-file
65 | load 'base64include.rb'
66 | # puts @@empty_spreadsheet
67 | f = File.open(filename,'wb')
68 | f.print(Base64.decode64(@@empty_spreadsheet))
69 | f.close
70 | end
71 |
72 | # Returns the content of a spreadsheet-cell.
73 | # (1,1) is the upper left corner.
74 | # (1,1), (1,'A'), ('A',1), ('a',1) all refers to the
75 | # cell at the first line and first row.
76 | def cell(row, col, sheet=nil)
77 | sheet = @default_sheet unless sheet
78 | read_cells(sheet) unless @cells_read[sheet]
79 | row,col = normalize(row,col)
80 | if celltype(row,col,sheet) == :date
81 | yyyy,mm,dd = @cell[sheet][[row,col]].split('-')
82 | return Date.new(yyyy.to_i,mm.to_i,dd.to_i)
83 | end
84 | @cell[sheet][[row,col]]
85 | end
86 |
87 | # Returns the formula at (row,col).
88 | # Returns nil if there is no formula.
89 | # The method #formula? checks if there is a formula.
90 | def formula(row,col,sheet=nil)
91 | sheet = @default_sheet unless sheet
92 | read_cells(sheet) unless @cells_read[sheet]
93 | row,col = normalize(row,col)
94 | if @formula[sheet][[row,col]] == nil
95 | return nil
96 | else
97 | return @formula[sheet][[row,col]]["oooc:".length..-1]
98 | end
99 | end
100 |
101 | # true, if there is a formula
102 | def formula?(row,col,sheet=nil)
103 | sheet = @default_sheet unless sheet
104 | read_cells(sheet) unless @cells_read[sheet]
105 | row,col = normalize(row,col)
106 | formula(row,col) != nil
107 | end
108 |
109 | class Font
110 | attr_accessor :bold, :italic, :underline
111 |
112 | def bold?
113 | @bold == 'bold'
114 | end
115 |
116 | def italic?
117 | @italic == 'italic'
118 | end
119 |
120 | def underline?
121 | @underline != nil
122 | end
123 | end
124 |
125 | # Given a cell, return the cell's style
126 | def font(row, col, sheet=nil)
127 | sheet = @default_sheet unless sheet
128 | read_cells(sheet) unless @cells_read[sheet]
129 | row,col = normalize(row,col)
130 | style_name = @style[sheet][[row,col]] || @style_defaults[sheet][col - 1] || 'Default'
131 | @style_definitions[style_name]
132 | end
133 |
134 | # set a cell to a certain value
135 | # (this will not be saved back to the spreadsheet file!)
136 | def set(row,col,value,sheet=nil) #:nodoc:
137 | sheet = @default_sheet unless sheet
138 | read_cells(sheet) unless @cells_read[sheet]
139 | row,col = normalize(row,col)
140 | set_value(row,col,value,sheet)
141 | if value.class == Fixnum
142 | set_type(row,col,:float,sheet)
143 | elsif value.class == String
144 | set_type(row,col,:string,sheet)
145 | elsif value.class == Float
146 | set_type(row,col,:string,sheet)
147 | else
148 | raise ArgumentError, "Type for "+value.to_s+" not set"
149 | end
150 | end
151 |
152 | # returns the type of a cell:
153 | # * :float
154 | # * :string
155 | # * :date
156 | # * :percentage
157 | # * :formula
158 | # * :time
159 | # * :datetime
160 | def celltype(row,col,sheet=nil)
161 | sheet = @default_sheet unless sheet
162 | read_cells(sheet) unless @cells_read[sheet]
163 | row,col = normalize(row,col)
164 | if @formula[sheet][[row,col]]
165 | return :formula
166 | else
167 | @cell_type[sheet][[row,col]]
168 | end
169 | end
170 |
171 | def sheets
172 | return_sheets = []
173 | @doc.find("//*[local-name()='table']").each do |sheet|
174 | return_sheets << sheet.attributes['name']
175 | end
176 | return_sheets
177 | end
178 |
179 | # version of the openoffice document
180 | # at 2007 this is always "1.0"
181 | def officeversion
182 | oo_version
183 | @officeversion
184 | end
185 |
186 | # shows the internal representation of all cells
187 | # mainly for debugging purposes
188 | def to_s(sheet=nil)
189 | sheet = @default_sheet unless sheet
190 | read_cells(sheet) unless @cells_read[sheet]
191 | @cell[sheet].inspect
192 | end
193 |
194 | # save spreadsheet
195 | def save #:nodoc:
196 | 42
197 | end
198 |
199 | # returns each formula in the selected sheet as an array of elements
200 | # [row, col, formula]
201 | def formulas(sheet=nil)
202 | theformulas = Array.new
203 | sheet = @default_sheet unless sheet
204 | read_cells(sheet) unless @cells_read[sheet]
205 | first_row(sheet).upto(last_row(sheet)) {|row|
206 | first_column(sheet).upto(last_column(sheet)) {|col|
207 | if formula?(row,col,sheet)
208 | f = [row, col, formula(row,col,sheet)]
209 | theformulas << f
210 | end
211 | }
212 | }
213 | theformulas
214 | end
215 |
216 | private
217 |
218 | # read the version of the OO-Version
219 | def oo_version
220 | @doc.find("//*[local-name()='document-content']").each do |office|
221 | @officeversion = office.attributes['version']
222 | end
223 | end
224 |
225 | # helper function to set the internal representation of cells
226 | def set_cell_values(sheet,x,y,i,v,vt,formula,table_cell,str_v,style_name)
227 | key = [y,x+i]
228 | @cell_type[sheet] = {} unless @cell_type[sheet]
229 | @cell_type[sheet][key] = Openoffice.oo_type_2_roo_type(vt)
230 | @formula[sheet] = {} unless @formula[sheet]
231 | @formula[sheet][key] = formula if formula
232 | @cell[sheet] = {} unless @cell[sheet]
233 | @style[sheet] = {} unless @style[sheet]
234 | @style[sheet][key] = style_name
235 | case @cell_type[sheet][key]
236 | when :float
237 | @cell[sheet][key] = v.to_f
238 | when :string
239 | @cell[sheet][key] = str_v
240 | when :date
241 | if table_cell.attributes['date-value'].size != "XXXX-XX-XX".size
242 | #-- dann ist noch eine Uhrzeit vorhanden
243 | #-- "1961-11-21T12:17:18"
244 | @cell[sheet][key] = DateTime.parse(table_cell.attributes['date-value'])
245 | @cell_type[sheet][key] = :datetime
246 | else
247 | @cell[sheet][key] = table_cell.attributes['date-value']
248 | end
249 | when :percentage
250 | @cell[sheet][key] = v.to_f
251 | when :time
252 | hms = v.split(':')
253 | @cell[sheet][key] = hms[0].to_i*3600 + hms[1].to_i*60 + hms[2].to_i
254 | else
255 | @cell[sheet][key] = v
256 | end
257 | end
258 |
259 | # read all cells in the selected sheet
260 | #--
261 | # the following construct means '4 blanks'
262 | # some content
263 | #++
264 | def read_cells(sheet=nil)
265 | sheet = @default_sheet unless sheet
266 | sheet_found = false
267 | raise ArgumentError, "Error: sheet '#{sheet||'nil'}' not valid" if @default_sheet == nil and sheet==nil
268 | raise RangeError unless self.sheets.include? sheet
269 |
270 | @doc.find("//*[local-name()='table']").each do |ws|
271 | if sheet == ws.attributes['name']
272 | sheet_found = true
273 | col = 1
274 | row = 1
275 | ws.each_element do |table_element|
276 | case table_element.name
277 | when 'table-column'
278 | @style_defaults[sheet] << table_element.attributes['default-cell-style-name']
279 | when 'table-row'
280 | if table_element.attributes['number-rows-repeated']
281 | skip_row = table_element.attributes['number-rows-repeated'].to_i
282 | row = row + skip_row - 1
283 | end
284 | table_element.each_element do |cell|
285 | skip_col = cell.attributes['number-columns-repeated']
286 | formula = cell.attributes['formula']
287 | vt = cell.attributes['value-type']
288 | v = cell.attributes['value']
289 | style_name = cell.attributes['style-name']
290 | if vt == 'string'
291 | str_v = ''
292 | # insert \n if there is more than one paragraph
293 | para_count = 0
294 | cell.each_element do |str|
295 | if str.name == 'p'
296 | v = str.content
297 | str_v += "\n" if para_count > 0
298 | para_count += 1
299 | if str.children.size > 1
300 | str_v += children_to_string(str.children)
301 | else
302 | str.children.each do |child|
303 | str_v += child.content #.text
304 | end
305 | end
306 | str_v.gsub!(/'/,"'") # special case not supported by unescapeHTML
307 | str_v = CGI.unescapeHTML(str_v)
308 | end # == 'p'
309 | end
310 | elsif vt == 'time'
311 | cell.each_element do |str|
312 | if str.name == 'p'
313 | v = str.content
314 | end
315 | end
316 | elsif vt == '' or vt == nil
317 | #
318 | elsif vt == 'date'
319 | #
320 | elsif vt == 'percentage'
321 | #
322 | elsif vt == 'float'
323 | #
324 | elsif vt == 'boolean'
325 | v = cell.attributes['boolean-value']
326 | #
327 | else
328 | # raise "unknown type #{vt}"
329 | end
330 | if skip_col
331 | if v != nil or cell.attributes['date-value']
332 | 0.upto(skip_col.to_i-1) do |i|
333 | set_cell_values(sheet,col,row,i,v,vt,formula,cell,str_v,style_name)
334 | end
335 | end
336 | col += (skip_col.to_i - 1)
337 | end # if skip
338 | set_cell_values(sheet,col,row,0,v,vt,formula,cell,str_v,style_name)
339 | col += 1
340 | end
341 | row += 1
342 | col = 1
343 | end
344 | end
345 | end
346 | end
347 |
348 | @doc.find("//*[local-name()='automatic-styles']").each do |style|
349 | read_styles(style)
350 | end
351 | if !sheet_found
352 | raise RangeError
353 | end
354 | @cells_read[sheet] = true
355 | end
356 |
357 | def read_styles(style_elements)
358 | @style_definitions['Default'] = Openoffice::Font.new
359 | style_elements.each do |style|
360 | next unless style.name == 'style'
361 | style_name = style.attributes['name']
362 | style.each do |properties|
363 | font = Openoffice::Font.new
364 | font.bold = properties.attributes['font-weight']
365 | font.italic = properties.attributes['font-style']
366 | font.underline = properties.attributes['text-underline-style']
367 | @style_definitions[style_name] = font
368 | end
369 | end
370 | end
371 |
372 | # Checks if the default_sheet exists. If not an RangeError exception is
373 | # raised
374 | def check_default_sheet
375 | sheet_found = false
376 | raise ArgumentError, "Error: default_sheet not set" if @default_sheet == nil
377 | sheet_found = true if sheets.include?(@default_sheet)
378 | if ! sheet_found
379 | raise RangeError, "sheet '#{@default_sheet}' not found"
380 | end
381 | end
382 |
383 | def process_zipfile(zip, path='')
384 | if zip.file.file? path
385 | if path == "content.xml"
386 | open(File.join(@tmpdir, @file_nr.to_s+'_roo_content.xml'),'wb') {|f|
387 | f << zip.read(path)
388 | }
389 | end
390 | else
391 | unless path.empty?
392 | path += '/'
393 | end
394 | zip.dir.foreach(path) do |filename|
395 | process_zipfile(zip, path+filename)
396 | end
397 | end
398 | end
399 |
400 | def extract_content
401 | Zip::ZipFile.open(@filename) do |zip|
402 | process_zipfile(zip)
403 | end
404 | end
405 |
406 | def set_value(row,col,value,sheet=nil)
407 | sheet = @default_value unless sheet
408 | @cell[sheet][[row,col]] = value
409 | end
410 |
411 | def set_type(row,col,type,sheet=nil)
412 | sheet = @default_value unless sheet
413 | @cell_type[sheet][[row,col]] = type
414 | end
415 |
416 | A_ROO_TYPE = {
417 | "float" => :float,
418 | "string" => :string,
419 | "date" => :date,
420 | "percentage" => :percentage,
421 | "time" => :time,
422 | }
423 |
424 | def Openoffice.oo_type_2_roo_type(ootype)
425 | return A_ROO_TYPE[ootype]
426 | end
427 |
428 | # helper method to convert compressed spaces and other elements within
429 | # an text into a string
430 | def children_to_string(children)
431 | result = ''
432 | children.each {|child|
433 | if child.text?
434 | result = result + child.content
435 | else
436 | if child.name == 's'
437 | compressed_spaces = child.attributes['c'].to_i
438 | # no explicit number means a count of 1:
439 | if compressed_spaces == 0
440 | compressed_spaces = 1
441 | end
442 | result = result + " "*compressed_spaces
443 | else
444 | result = result + child.content
445 | end
446 | end
447 | }
448 | result
449 | end
450 |
451 | end # class
452 |
--------------------------------------------------------------------------------