roo 1.13.2 → 2.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.codeclimate.yml +17 -0
- data/.github/issue_template.md +16 -0
- data/.github/pull_request_template.md +14 -0
- data/.github/workflows/pull-request.yml +15 -0
- data/.github/workflows/ruby.yml +34 -0
- data/.gitignore +11 -0
- data/.rubocop.yml +186 -0
- data/.simplecov +4 -0
- data/CHANGELOG.md +702 -0
- data/Gemfile +18 -12
- data/Guardfile +23 -0
- data/LICENSE +5 -1
- data/README.md +328 -0
- data/Rakefile +23 -23
- data/examples/roo_soap_client.rb +28 -31
- data/examples/roo_soap_server.rb +4 -6
- data/examples/write_me.rb +9 -10
- data/lib/roo/base.rb +317 -504
- data/lib/roo/constants.rb +7 -0
- data/lib/roo/csv.rb +141 -113
- data/lib/roo/errors.rb +11 -0
- data/lib/roo/excelx/cell/base.rb +108 -0
- data/lib/roo/excelx/cell/boolean.rb +30 -0
- data/lib/roo/excelx/cell/date.rb +28 -0
- data/lib/roo/excelx/cell/datetime.rb +107 -0
- data/lib/roo/excelx/cell/empty.rb +20 -0
- data/lib/roo/excelx/cell/number.rb +99 -0
- data/lib/roo/excelx/cell/string.rb +19 -0
- data/lib/roo/excelx/cell/time.rb +44 -0
- data/lib/roo/excelx/cell.rb +110 -0
- data/lib/roo/excelx/comments.rb +55 -0
- data/lib/roo/excelx/coordinate.rb +19 -0
- data/lib/roo/excelx/extractor.rb +39 -0
- data/lib/roo/excelx/format.rb +71 -0
- data/lib/roo/excelx/images.rb +26 -0
- data/lib/roo/excelx/relationships.rb +33 -0
- data/lib/roo/excelx/shared.rb +39 -0
- data/lib/roo/excelx/shared_strings.rb +151 -0
- data/lib/roo/excelx/sheet.rb +151 -0
- data/lib/roo/excelx/sheet_doc.rb +257 -0
- data/lib/roo/excelx/styles.rb +64 -0
- data/lib/roo/excelx/workbook.rb +64 -0
- data/lib/roo/excelx.rb +407 -601
- data/lib/roo/font.rb +17 -0
- data/lib/roo/formatters/base.rb +15 -0
- data/lib/roo/formatters/csv.rb +84 -0
- data/lib/roo/formatters/matrix.rb +23 -0
- data/lib/roo/formatters/xml.rb +31 -0
- data/lib/roo/formatters/yaml.rb +40 -0
- data/lib/roo/helpers/default_attr_reader.rb +20 -0
- data/lib/roo/helpers/weak_instance_cache.rb +41 -0
- data/lib/roo/libre_office.rb +4 -0
- data/lib/roo/link.rb +34 -0
- data/lib/roo/open_office.rb +631 -0
- data/lib/roo/spreadsheet.rb +28 -23
- data/lib/roo/tempdir.rb +24 -0
- data/lib/roo/utils.rb +128 -0
- data/lib/roo/version.rb +3 -0
- data/lib/roo.rb +26 -24
- data/roo.gemspec +29 -203
- data/spec/helpers.rb +5 -0
- data/spec/lib/roo/base_spec.rb +291 -3
- data/spec/lib/roo/csv_spec.rb +38 -11
- data/spec/lib/roo/excelx/cell/time_spec.rb +15 -0
- data/spec/lib/roo/excelx/format_spec.rb +7 -6
- data/spec/lib/roo/excelx/relationships_spec.rb +43 -0
- data/spec/lib/roo/excelx/sheet_doc_spec.rb +11 -0
- data/spec/lib/roo/excelx_spec.rb +672 -11
- data/spec/lib/roo/libreoffice_spec.rb +16 -6
- data/spec/lib/roo/openoffice_spec.rb +30 -8
- data/spec/lib/roo/spreadsheet_spec.rb +60 -12
- data/spec/lib/roo/strict_spec.rb +43 -0
- data/spec/lib/roo/utils_spec.rb +119 -0
- data/spec/lib/roo/weak_instance_cache_spec.rb +92 -0
- data/spec/lib/roo_spec.rb +0 -0
- data/spec/spec_helper.rb +7 -6
- data/test/all_ss.rb +12 -11
- data/test/excelx/cell/test_attr_reader_default.rb +72 -0
- data/test/excelx/cell/test_base.rb +68 -0
- data/test/excelx/cell/test_boolean.rb +36 -0
- data/test/excelx/cell/test_date.rb +38 -0
- data/test/excelx/cell/test_datetime.rb +45 -0
- data/test/excelx/cell/test_empty.rb +18 -0
- data/test/excelx/cell/test_number.rb +90 -0
- data/test/excelx/cell/test_string.rb +48 -0
- data/test/excelx/cell/test_time.rb +30 -0
- data/test/excelx/test_coordinate.rb +51 -0
- data/test/formatters/test_csv.rb +136 -0
- data/test/formatters/test_matrix.rb +76 -0
- data/test/formatters/test_xml.rb +78 -0
- data/test/formatters/test_yaml.rb +20 -0
- data/test/helpers/test_accessing_files.rb +81 -0
- data/test/helpers/test_comments.rb +43 -0
- data/test/helpers/test_formulas.rb +9 -0
- data/test/helpers/test_labels.rb +103 -0
- data/test/helpers/test_sheets.rb +55 -0
- data/test/helpers/test_styles.rb +62 -0
- data/test/roo/test_base.rb +182 -0
- data/test/roo/test_csv.rb +88 -0
- data/test/roo/test_excelx.rb +360 -0
- data/test/roo/test_libre_office.rb +9 -0
- data/test/roo/test_open_office.rb +289 -0
- data/test/test_helper.rb +123 -59
- data/test/test_roo.rb +392 -2292
- metadata +153 -298
- data/CHANGELOG +0 -417
- data/Gemfile.lock +0 -78
- data/README.markdown +0 -126
- data/VERSION +0 -1
- data/lib/roo/excel.rb +0 -355
- data/lib/roo/excel2003xml.rb +0 -300
- data/lib/roo/google.rb +0 -292
- data/lib/roo/openoffice.rb +0 -496
- data/lib/roo/roo_rails_helper.rb +0 -83
- data/lib/roo/worksheet.rb +0 -18
- data/scripts/txt2html +0 -67
- data/spec/lib/roo/excel2003xml_spec.rb +0 -15
- data/spec/lib/roo/excel_spec.rb +0 -17
- data/spec/lib/roo/google_spec.rb +0 -64
- data/test/files/1900_base.xls +0 -0
- data/test/files/1900_base.xlsx +0 -0
- data/test/files/1904_base.xls +0 -0
- data/test/files/1904_base.xlsx +0 -0
- data/test/files/Bibelbund.csv +0 -3741
- data/test/files/Bibelbund.ods +0 -0
- data/test/files/Bibelbund.xls +0 -0
- data/test/files/Bibelbund.xlsx +0 -0
- data/test/files/Bibelbund.xml +0 -62518
- data/test/files/Bibelbund1.ods +0 -0
- data/test/files/Pfand_from_windows_phone.xlsx +0 -0
- data/test/files/bad_excel_date.xls +0 -0
- data/test/files/bbu.ods +0 -0
- data/test/files/bbu.xls +0 -0
- data/test/files/bbu.xlsx +0 -0
- data/test/files/bbu.xml +0 -152
- data/test/files/bode-v1.ods.zip +0 -0
- data/test/files/bode-v1.xls.zip +0 -0
- data/test/files/boolean.csv +0 -2
- data/test/files/boolean.ods +0 -0
- data/test/files/boolean.xls +0 -0
- data/test/files/boolean.xlsx +0 -0
- data/test/files/boolean.xml +0 -112
- data/test/files/borders.ods +0 -0
- data/test/files/borders.xls +0 -0
- data/test/files/borders.xlsx +0 -0
- data/test/files/borders.xml +0 -144
- data/test/files/bug-numbered-sheet-names.xlsx +0 -0
- data/test/files/bug-row-column-fixnum-float.xls +0 -0
- data/test/files/bug-row-column-fixnum-float.xml +0 -127
- data/test/files/comments.ods +0 -0
- data/test/files/comments.xls +0 -0
- data/test/files/comments.xlsx +0 -0
- data/test/files/csvtypes.csv +0 -1
- data/test/files/datetime.ods +0 -0
- data/test/files/datetime.xls +0 -0
- data/test/files/datetime.xlsx +0 -0
- data/test/files/datetime.xml +0 -142
- data/test/files/datetime_floatconv.xls +0 -0
- data/test/files/datetime_floatconv.xml +0 -148
- data/test/files/dreimalvier.ods +0 -0
- data/test/files/emptysheets.ods +0 -0
- data/test/files/emptysheets.xls +0 -0
- data/test/files/emptysheets.xlsx +0 -0
- data/test/files/emptysheets.xml +0 -105
- data/test/files/excel2003.xml +0 -21140
- data/test/files/false_encoding.xls +0 -0
- data/test/files/false_encoding.xml +0 -132
- data/test/files/file_item_error.xlsx +0 -0
- data/test/files/formula.ods +0 -0
- data/test/files/formula.xls +0 -0
- data/test/files/formula.xlsx +0 -0
- data/test/files/formula.xml +0 -134
- data/test/files/formula_parse_error.xls +0 -0
- data/test/files/formula_parse_error.xml +0 -1833
- data/test/files/formula_string_error.xlsx +0 -0
- data/test/files/html-escape.ods +0 -0
- data/test/files/link.xls +0 -0
- data/test/files/link.xlsx +0 -0
- data/test/files/matrix.ods +0 -0
- data/test/files/matrix.xls +0 -0
- data/test/files/named_cells.ods +0 -0
- data/test/files/named_cells.xls +0 -0
- data/test/files/named_cells.xlsx +0 -0
- data/test/files/no_spreadsheet_file.txt +0 -1
- data/test/files/numbers1.csv +0 -18
- data/test/files/numbers1.ods +0 -0
- data/test/files/numbers1.xls +0 -0
- data/test/files/numbers1.xlsx +0 -0
- data/test/files/numbers1.xml +0 -312
- data/test/files/numeric-link.xlsx +0 -0
- data/test/files/only_one_sheet.ods +0 -0
- data/test/files/only_one_sheet.xls +0 -0
- data/test/files/only_one_sheet.xlsx +0 -0
- data/test/files/only_one_sheet.xml +0 -67
- data/test/files/paragraph.ods +0 -0
- data/test/files/paragraph.xls +0 -0
- data/test/files/paragraph.xlsx +0 -0
- data/test/files/paragraph.xml +0 -127
- data/test/files/prova.xls +0 -0
- data/test/files/ric.ods +0 -0
- data/test/files/simple_spreadsheet.ods +0 -0
- data/test/files/simple_spreadsheet.xls +0 -0
- data/test/files/simple_spreadsheet.xlsx +0 -0
- data/test/files/simple_spreadsheet.xml +0 -225
- data/test/files/simple_spreadsheet_from_italo.ods +0 -0
- data/test/files/simple_spreadsheet_from_italo.xls +0 -0
- data/test/files/simple_spreadsheet_from_italo.xml +0 -242
- data/test/files/so_datetime.csv +0 -7
- data/test/files/style.ods +0 -0
- data/test/files/style.xls +0 -0
- data/test/files/style.xlsx +0 -0
- data/test/files/style.xml +0 -154
- data/test/files/time-test.csv +0 -2
- data/test/files/time-test.ods +0 -0
- data/test/files/time-test.xls +0 -0
- data/test/files/time-test.xlsx +0 -0
- data/test/files/time-test.xml +0 -131
- data/test/files/type_excel.ods +0 -0
- data/test/files/type_excel.xlsx +0 -0
- data/test/files/type_excelx.ods +0 -0
- data/test/files/type_excelx.xls +0 -0
- data/test/files/type_openoffice.xls +0 -0
- data/test/files/type_openoffice.xlsx +0 -0
- data/test/files/whitespace.ods +0 -0
- data/test/files/whitespace.xls +0 -0
- data/test/files/whitespace.xlsx +0 -0
- data/test/files/whitespace.xml +0 -184
- data/test/rm_sub_test.rb +0 -12
- data/test/rm_test.rb +0 -7
- data/test/test_generic_spreadsheet.rb +0 -259
- data/website/index.html +0 -385
- data/website/index.txt +0 -423
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/screen.css +0 -130
- data/website/template.rhtml +0 -48
data/lib/roo/excel.rb
DELETED
@@ -1,355 +0,0 @@
|
|
1
|
-
require 'spreadsheet'
|
2
|
-
|
3
|
-
# Class for handling Excel-Spreadsheets
|
4
|
-
class Roo::Excel < Roo::Base
|
5
|
-
FORMULAS_MESSAGE = 'the spreadsheet gem does not support forumulas, so roo can not.'
|
6
|
-
CHARGUESS =
|
7
|
-
begin
|
8
|
-
require 'charguess'
|
9
|
-
true
|
10
|
-
rescue LoadError
|
11
|
-
false
|
12
|
-
end
|
13
|
-
|
14
|
-
attr_reader :workbook
|
15
|
-
|
16
|
-
# Creates a new Excel spreadsheet object.
|
17
|
-
# Parameter packed: :zip - File is a zip-file
|
18
|
-
def initialize(filename, options = {}, deprecated_file_warning = :error)
|
19
|
-
if Hash === options
|
20
|
-
packed = options[:packed]
|
21
|
-
file_warning = options[:file_warning] || :error
|
22
|
-
mode = options[:mode] || "rb+"
|
23
|
-
else
|
24
|
-
warn 'Supplying `packed` or `file_warning` as separate arguments to `Roo::Excel.new` is deprecated. Use an options hash instead.'
|
25
|
-
packed = options
|
26
|
-
mode = "rb+"
|
27
|
-
file_warning = deprecated_file_warning
|
28
|
-
end
|
29
|
-
|
30
|
-
file_type_check(filename,'.xls','an Excel', file_warning, packed)
|
31
|
-
make_tmpdir do |tmpdir|
|
32
|
-
filename = download_uri(filename, tmpdir) if uri?(filename)
|
33
|
-
filename = open_from_stream(filename[7..-1], tmpdir) if filename[0,7] == "stream:"
|
34
|
-
filename = unzip(filename, tmpdir) if packed == :zip
|
35
|
-
|
36
|
-
@filename = filename
|
37
|
-
unless File.file?(@filename)
|
38
|
-
raise IOError, "file #{@filename} does not exist"
|
39
|
-
end
|
40
|
-
@workbook = Spreadsheet.open(filename, mode)
|
41
|
-
end
|
42
|
-
super(filename, options)
|
43
|
-
@formula = Hash.new
|
44
|
-
@fonts = Hash.new
|
45
|
-
end
|
46
|
-
|
47
|
-
def encoding=(codepage)
|
48
|
-
@workbook.encoding = codepage
|
49
|
-
end
|
50
|
-
|
51
|
-
# returns an array of sheet names in the spreadsheet
|
52
|
-
def sheets
|
53
|
-
@workbook.worksheets.collect {|worksheet| normalize_string(worksheet.name)}
|
54
|
-
end
|
55
|
-
|
56
|
-
# this method lets you find the worksheet with the most data
|
57
|
-
def longest_sheet
|
58
|
-
sheet(@workbook.worksheets.inject {|m,o|
|
59
|
-
o.row_count > m.row_count ? o : m
|
60
|
-
}.name)
|
61
|
-
end
|
62
|
-
|
63
|
-
# returns the content of a cell. The upper left corner is (1,1) or ('A',1)
|
64
|
-
def cell(row,col,sheet=nil)
|
65
|
-
sheet ||= @default_sheet
|
66
|
-
validate_sheet!(sheet)
|
67
|
-
|
68
|
-
read_cells(sheet)
|
69
|
-
raise "should be read" unless @cells_read[sheet]
|
70
|
-
row,col = normalize(row,col)
|
71
|
-
if celltype(row,col,sheet) == :date
|
72
|
-
yyyy,mm,dd = @cell[sheet][[row,col]].split('-')
|
73
|
-
return Date.new(yyyy.to_i,mm.to_i,dd.to_i)
|
74
|
-
end
|
75
|
-
if celltype(row,col,sheet) == :string
|
76
|
-
return platform_specific_encoding(@cell[sheet][[row,col]])
|
77
|
-
else
|
78
|
-
if @cell[sheet] and @cell[sheet][[row,col]]
|
79
|
-
return @cell[sheet][[row,col]]
|
80
|
-
else
|
81
|
-
return nil
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
# returns the type of a cell:
|
87
|
-
# * :float
|
88
|
-
# * :string,
|
89
|
-
# * :date
|
90
|
-
# * :percentage
|
91
|
-
# * :formula
|
92
|
-
# * :time
|
93
|
-
# * :datetime
|
94
|
-
def celltype(row,col,sheet=nil)
|
95
|
-
sheet ||= @default_sheet
|
96
|
-
read_cells(sheet)
|
97
|
-
row,col = normalize(row,col)
|
98
|
-
begin
|
99
|
-
if @formula[sheet] and @formula[sheet][[row,col]]
|
100
|
-
:formula
|
101
|
-
elsif @cell_type[sheet]
|
102
|
-
@cell_type[sheet][[row,col]]
|
103
|
-
end
|
104
|
-
rescue
|
105
|
-
puts "Error in sheet #{sheet}, row #{row}, col #{col}"
|
106
|
-
raise
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
# returns NO formula in excel spreadsheets
|
111
|
-
def formula(row,col,sheet=nil)
|
112
|
-
raise NotImplementedError, FORMULAS_MESSAGE
|
113
|
-
end
|
114
|
-
alias_method :formula?, :formula
|
115
|
-
|
116
|
-
# returns NO formulas in excel spreadsheets
|
117
|
-
def formulas(sheet=nil)
|
118
|
-
raise NotImplementedError, FORMULAS_MESSAGE
|
119
|
-
end
|
120
|
-
|
121
|
-
# Given a cell, return the cell's font
|
122
|
-
def font(row, col, sheet=nil)
|
123
|
-
sheet ||= @default_sheet
|
124
|
-
read_cells(sheet)
|
125
|
-
row,col = normalize(row,col)
|
126
|
-
@fonts[sheet][[row,col]]
|
127
|
-
end
|
128
|
-
|
129
|
-
# shows the internal representation of all cells
|
130
|
-
# mainly for debugging purposes
|
131
|
-
def to_s(sheet=nil)
|
132
|
-
sheet ||= @default_sheet
|
133
|
-
read_cells(sheet)
|
134
|
-
@cell[sheet].inspect
|
135
|
-
end
|
136
|
-
|
137
|
-
private
|
138
|
-
|
139
|
-
# converts name of a sheet to index (0,1,2,..)
|
140
|
-
def sheet_no(name)
|
141
|
-
return name-1 if name.kind_of?(Fixnum)
|
142
|
-
i = 0
|
143
|
-
@workbook.worksheets.each do |worksheet|
|
144
|
-
return i if name == normalize_string(worksheet.name)
|
145
|
-
i += 1
|
146
|
-
end
|
147
|
-
raise StandardError, "sheet '#{name}' not found"
|
148
|
-
end
|
149
|
-
|
150
|
-
def normalize_string(value)
|
151
|
-
value = every_second_null?(value) ? remove_every_second_null(value) : value
|
152
|
-
if CHARGUESS && encoding = CharGuess::guess(value)
|
153
|
-
encoding.encode Encoding::UTF_8
|
154
|
-
else
|
155
|
-
platform_specific_encoding(value)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def platform_specific_encoding(value)
|
160
|
-
result =
|
161
|
-
case RUBY_PLATFORM.downcase
|
162
|
-
when /darwin|solaris/
|
163
|
-
value.encode Encoding::UTF_8
|
164
|
-
when /mswin32/
|
165
|
-
value.encode Encoding::ISO_8859_1
|
166
|
-
else
|
167
|
-
value
|
168
|
-
end
|
169
|
-
if every_second_null?(result)
|
170
|
-
result = remove_every_second_null(result)
|
171
|
-
end
|
172
|
-
result
|
173
|
-
end
|
174
|
-
|
175
|
-
def every_second_null?(str)
|
176
|
-
result = true
|
177
|
-
return false if str.length < 2
|
178
|
-
0.upto(str.length/2-1) do |i|
|
179
|
-
if str[i*2+1,1] != "\000"
|
180
|
-
result = false
|
181
|
-
break
|
182
|
-
end
|
183
|
-
end
|
184
|
-
result
|
185
|
-
end
|
186
|
-
|
187
|
-
def remove_every_second_null(str)
|
188
|
-
result = ''
|
189
|
-
0.upto(str.length/2-1) do |i|
|
190
|
-
c = str[i*2,1]
|
191
|
-
result += c
|
192
|
-
end
|
193
|
-
result
|
194
|
-
end
|
195
|
-
|
196
|
-
# helper function to set the internal representation of cells
|
197
|
-
def set_cell_values(sheet,row,col,i,v,value_type,formula,tr,font)
|
198
|
-
#key = "#{y},#{x+i}"
|
199
|
-
key = [row,col+i]
|
200
|
-
@cell_type[sheet] = {} unless @cell_type[sheet]
|
201
|
-
@cell_type[sheet][key] = value_type
|
202
|
-
@formula[sheet] = {} unless @formula[sheet]
|
203
|
-
@formula[sheet][key] = formula if formula
|
204
|
-
@cell[sheet] = {} unless @cell[sheet]
|
205
|
-
@fonts[sheet] = {} unless @fonts[sheet]
|
206
|
-
@fonts[sheet][key] = font
|
207
|
-
|
208
|
-
@cell[sheet][key] =
|
209
|
-
case value_type
|
210
|
-
when :float
|
211
|
-
v.to_f
|
212
|
-
when :string
|
213
|
-
v
|
214
|
-
when :date
|
215
|
-
v
|
216
|
-
when :datetime
|
217
|
-
@cell[sheet][key] = DateTime.new(v.year,v.month,v.day,v.hour,v.min,v.sec)
|
218
|
-
when :percentage
|
219
|
-
v.to_f
|
220
|
-
when :time
|
221
|
-
v
|
222
|
-
else
|
223
|
-
v
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
# ruby-spreadsheet has a font object so we're extending it
|
228
|
-
# with our own functionality but still providing full access
|
229
|
-
# to the user for other font information
|
230
|
-
module ExcelFontExtensions
|
231
|
-
def bold?(*args)
|
232
|
-
#From ruby-spreadsheet doc: 100 <= weight <= 1000, bold => 700, normal => 400
|
233
|
-
weight == 700
|
234
|
-
end
|
235
|
-
|
236
|
-
def italic?
|
237
|
-
italic
|
238
|
-
end
|
239
|
-
|
240
|
-
def underline?
|
241
|
-
underline != :none
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
# read all cells in the selected sheet
|
246
|
-
def read_cells(sheet=nil)
|
247
|
-
sheet ||= @default_sheet
|
248
|
-
validate_sheet!(sheet)
|
249
|
-
return if @cells_read[sheet]
|
250
|
-
|
251
|
-
worksheet = @workbook.worksheet(sheet_no(sheet))
|
252
|
-
row_index=1
|
253
|
-
worksheet.each(0) do |row|
|
254
|
-
(0..row.size).each do |cell_index|
|
255
|
-
cell = row.at(cell_index)
|
256
|
-
next if cell.nil? #skip empty cells
|
257
|
-
next if cell.class == Spreadsheet::Formula && cell.value.nil? # skip empty formula cells
|
258
|
-
value_type, v =
|
259
|
-
if date_or_time?(row, cell_index)
|
260
|
-
read_cell_date_or_time(row, cell_index)
|
261
|
-
else
|
262
|
-
read_cell(row, cell_index)
|
263
|
-
end
|
264
|
-
formula = tr = nil #TODO:???
|
265
|
-
col_index = cell_index + 1
|
266
|
-
font = row.format(cell_index).font
|
267
|
-
font.extend(ExcelFontExtensions)
|
268
|
-
set_cell_values(sheet,row_index,col_index,0,v,value_type,formula,tr,font)
|
269
|
-
end #row
|
270
|
-
row_index += 1
|
271
|
-
end # worksheet
|
272
|
-
@cells_read[sheet] = true
|
273
|
-
end
|
274
|
-
|
275
|
-
# Get the contents of a cell, accounting for the
|
276
|
-
# way formula stores the value
|
277
|
-
def read_cell_content(row, idx)
|
278
|
-
cell = row.at(idx)
|
279
|
-
cell = row[idx] if row[idx].class == Spreadsheet::Link
|
280
|
-
cell = cell.value if cell.class == Spreadsheet::Formula
|
281
|
-
cell
|
282
|
-
end
|
283
|
-
|
284
|
-
# Test the cell to see if it's a valid date/time.
|
285
|
-
def date_or_time?(row, idx)
|
286
|
-
format = row.format(idx)
|
287
|
-
if format.date_or_time?
|
288
|
-
cell = read_cell_content(row, idx)
|
289
|
-
true if Float(cell) > 0 rescue false
|
290
|
-
else
|
291
|
-
false
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
# Read the date-time cell and convert to,
|
296
|
-
# the date-time values for Roo
|
297
|
-
def read_cell_date_or_time(row, idx)
|
298
|
-
cell = read_cell_content(row, idx)
|
299
|
-
cell = cell.to_s.to_f
|
300
|
-
if cell < 1.0
|
301
|
-
value_type = :time
|
302
|
-
f = cell*24.0*60.0*60.0
|
303
|
-
secs = f.round
|
304
|
-
h = (secs / 3600.0).floor
|
305
|
-
secs = secs - 3600*h
|
306
|
-
m = (secs / 60.0).floor
|
307
|
-
secs = secs - 60*m
|
308
|
-
s = secs
|
309
|
-
value = h*3600+m*60+s
|
310
|
-
else
|
311
|
-
if row.at(idx).class == Spreadsheet::Formula
|
312
|
-
datetime = row.send(:_datetime, cell)
|
313
|
-
else
|
314
|
-
datetime = row.datetime(idx)
|
315
|
-
end
|
316
|
-
if datetime.hour != 0 or
|
317
|
-
datetime.min != 0 or
|
318
|
-
datetime.sec != 0
|
319
|
-
value_type = :datetime
|
320
|
-
value = datetime
|
321
|
-
else
|
322
|
-
value_type = :date
|
323
|
-
if row.at(idx).class == Spreadsheet::Formula
|
324
|
-
value = row.send(:_date, cell)
|
325
|
-
else
|
326
|
-
value = row.date(idx)
|
327
|
-
end
|
328
|
-
value = sprintf("%04d-%02d-%02d",value.year,value.month,value.day)
|
329
|
-
end
|
330
|
-
end
|
331
|
-
return value_type, value
|
332
|
-
end
|
333
|
-
|
334
|
-
# Read the cell and based on the class,
|
335
|
-
# return the values for Roo
|
336
|
-
def read_cell(row, idx)
|
337
|
-
cell = read_cell_content(row, idx)
|
338
|
-
case cell
|
339
|
-
when Float, Integer, Fixnum, Bignum
|
340
|
-
value_type = :float
|
341
|
-
value = cell.to_f
|
342
|
-
when Spreadsheet::Link
|
343
|
-
value_type = :link
|
344
|
-
value = cell
|
345
|
-
when String, TrueClass, FalseClass
|
346
|
-
value_type = :string
|
347
|
-
value = cell.to_s
|
348
|
-
else
|
349
|
-
value_type = cell.class.to_s.downcase.to_sym
|
350
|
-
value = nil
|
351
|
-
end # case
|
352
|
-
return value_type, value
|
353
|
-
end
|
354
|
-
|
355
|
-
end
|
data/lib/roo/excel2003xml.rb
DELETED
@@ -1,300 +0,0 @@
|
|
1
|
-
require 'date'
|
2
|
-
require 'base64'
|
3
|
-
require 'nokogiri'
|
4
|
-
|
5
|
-
class Roo::Excel2003XML < Roo::Base
|
6
|
-
|
7
|
-
# initialization and opening of a spreadsheet file
|
8
|
-
# values for packed: :zip
|
9
|
-
def initialize(filename, options={}, deprecated_file_warning=:error)
|
10
|
-
if Hash === options
|
11
|
-
packed = options[:packed]
|
12
|
-
file_warning = options[:file_warning] || :error
|
13
|
-
else
|
14
|
-
warn 'Supplying `packed` or `file_warning` as separate arguments to `Roo::Excel2003XML.new` is deprecated. Use an options hash instead.'
|
15
|
-
packed = options
|
16
|
-
file_warning = deprecated_file_warning
|
17
|
-
end
|
18
|
-
|
19
|
-
make_tmpdir do |tmpdir|
|
20
|
-
filename = download_uri(filename, tmpdir) if uri?(filename)
|
21
|
-
filename = unzip(filename, tmpdir) if packed == :zip
|
22
|
-
|
23
|
-
file_type_check(filename,'.xml','an Excel 2003 XML', file_warning)
|
24
|
-
@filename = filename
|
25
|
-
unless File.file?(@filename)
|
26
|
-
raise IOError, "file #{@filename} does not exist"
|
27
|
-
end
|
28
|
-
@doc = load_xml(@filename)
|
29
|
-
end
|
30
|
-
super(filename, options)
|
31
|
-
@formula = Hash.new
|
32
|
-
@style = Hash.new
|
33
|
-
@style_defaults = Hash.new { |h,k| h[k] = [] }
|
34
|
-
@style_definitions = Hash.new
|
35
|
-
read_styles
|
36
|
-
end
|
37
|
-
|
38
|
-
# Returns the content of a spreadsheet-cell.
|
39
|
-
# (1,1) is the upper left corner.
|
40
|
-
# (1,1), (1,'A'), ('A',1), ('a',1) all refers to the
|
41
|
-
# cell at the first line and first row.
|
42
|
-
def cell(row, col, sheet=nil)
|
43
|
-
sheet ||= @default_sheet
|
44
|
-
read_cells(sheet)
|
45
|
-
row,col = normalize(row,col)
|
46
|
-
if celltype(row,col,sheet) == :date
|
47
|
-
yyyy,mm,dd = @cell[sheet][[row,col]].split('-')
|
48
|
-
return Date.new(yyyy.to_i,mm.to_i,dd.to_i)
|
49
|
-
end
|
50
|
-
@cell[sheet][[row,col]]
|
51
|
-
end
|
52
|
-
|
53
|
-
# Returns the formula at (row,col).
|
54
|
-
# Returns nil if there is no formula.
|
55
|
-
# The method #formula? checks if there is a formula.
|
56
|
-
def formula(row,col,sheet=nil)
|
57
|
-
sheet ||= @default_sheet
|
58
|
-
read_cells(sheet)
|
59
|
-
row,col = normalize(row,col)
|
60
|
-
@formula[sheet][[row,col]] && @formula[sheet][[row,col]]["oooc:".length..-1]
|
61
|
-
end
|
62
|
-
alias_method :formula?, :formula
|
63
|
-
|
64
|
-
class Font
|
65
|
-
attr_accessor :bold, :italic, :underline
|
66
|
-
|
67
|
-
def bold?
|
68
|
-
@bold == '1'
|
69
|
-
end
|
70
|
-
|
71
|
-
def italic?
|
72
|
-
@italic == '1'
|
73
|
-
end
|
74
|
-
|
75
|
-
def underline?
|
76
|
-
@underline != nil
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
# Given a cell, return the cell's style
|
81
|
-
def font(row, col, sheet=nil)
|
82
|
-
sheet ||= @default_sheet
|
83
|
-
read_cells(sheet)
|
84
|
-
row,col = normalize(row,col)
|
85
|
-
style_name = @style[sheet][[row,col]] || @style_defaults[sheet][col - 1] || 'Default'
|
86
|
-
@style_definitions[style_name]
|
87
|
-
end
|
88
|
-
|
89
|
-
# returns the type of a cell:
|
90
|
-
# * :float
|
91
|
-
# * :string
|
92
|
-
# * :date
|
93
|
-
# * :percentage
|
94
|
-
# * :formula
|
95
|
-
# * :time
|
96
|
-
# * :datetime
|
97
|
-
def celltype(row,col,sheet=nil)
|
98
|
-
sheet ||= @default_sheet
|
99
|
-
read_cells(sheet)
|
100
|
-
row,col = normalize(row,col)
|
101
|
-
if @formula[sheet][[row,col]]
|
102
|
-
return :formula
|
103
|
-
else
|
104
|
-
@cell_type[sheet][[row,col]]
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def sheets
|
109
|
-
@doc.xpath("/ss:Workbook/ss:Worksheet").map do |sheet|
|
110
|
-
sheet['ss:Name']
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
# version of the openoffice document
|
115
|
-
# at 2007 this is always "1.0"
|
116
|
-
def officeversion
|
117
|
-
oo_version
|
118
|
-
@officeversion
|
119
|
-
end
|
120
|
-
|
121
|
-
# shows the internal representation of all cells
|
122
|
-
# mainly for debugging purposes
|
123
|
-
def to_s(sheet=nil)
|
124
|
-
sheet ||= @default_sheet
|
125
|
-
read_cells(sheet)
|
126
|
-
@cell[sheet].inspect
|
127
|
-
end
|
128
|
-
|
129
|
-
# save spreadsheet
|
130
|
-
def save #:nodoc:
|
131
|
-
42
|
132
|
-
end
|
133
|
-
|
134
|
-
# returns each formula in the selected sheet as an array of elements
|
135
|
-
# [row, col, formula]
|
136
|
-
def formulas(sheet=nil)
|
137
|
-
theformulas = Array.new
|
138
|
-
sheet ||= @default_sheet
|
139
|
-
read_cells(sheet)
|
140
|
-
first_row(sheet).upto(last_row(sheet)) {|row|
|
141
|
-
first_column(sheet).upto(last_column(sheet)) {|col|
|
142
|
-
if formula?(row,col,sheet)
|
143
|
-
f = [row, col, formula(row,col,sheet)]
|
144
|
-
theformulas << f
|
145
|
-
end
|
146
|
-
}
|
147
|
-
}
|
148
|
-
theformulas
|
149
|
-
end
|
150
|
-
|
151
|
-
private
|
152
|
-
|
153
|
-
# read the version of the OO-Version
|
154
|
-
def oo_version
|
155
|
-
@doc.find("//*[local-name()='document-content']").each do |office|
|
156
|
-
@officeversion = office['version']
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
# helper function to set the internal representation of cells
|
161
|
-
def set_cell_values(sheet,x,y,i,v,value_type,formula,table_cell,str_v,style_name)
|
162
|
-
key = [y,x+i]
|
163
|
-
@cell_type[sheet] = {} unless @cell_type[sheet]
|
164
|
-
@cell_type[sheet][key] = value_type
|
165
|
-
@formula[sheet] = {} unless @formula[sheet]
|
166
|
-
@formula[sheet][key] = formula if formula
|
167
|
-
@cell[sheet] = {} unless @cell[sheet]
|
168
|
-
@style[sheet] = {} unless @style[sheet]
|
169
|
-
@style[sheet][key] = style_name
|
170
|
-
@cell[sheet][key] =
|
171
|
-
case @cell_type[sheet][key]
|
172
|
-
when :float
|
173
|
-
v.to_f
|
174
|
-
when :string
|
175
|
-
str_v
|
176
|
-
when :datetime
|
177
|
-
DateTime.parse(v)
|
178
|
-
when :percentage
|
179
|
-
v.to_f
|
180
|
-
# when :time
|
181
|
-
# hms = v.split(':')
|
182
|
-
# hms[0].to_i*3600 + hms[1].to_i*60 + hms[2].to_i
|
183
|
-
else
|
184
|
-
v
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
# read all cells in the selected sheet
|
189
|
-
#--
|
190
|
-
# the following construct means '4 blanks'
|
191
|
-
# some content <text:s text:c="3"/>
|
192
|
-
#++
|
193
|
-
def read_cells(sheet=nil)
|
194
|
-
sheet ||= @default_sheet
|
195
|
-
validate_sheet!(sheet)
|
196
|
-
return if @cells_read[sheet]
|
197
|
-
sheet_found = false
|
198
|
-
@doc.xpath("/ss:Workbook/ss:Worksheet[@ss:Name='#{sheet}']").each do |ws|
|
199
|
-
sheet_found = true
|
200
|
-
row = 1
|
201
|
-
col = 1
|
202
|
-
column_attributes = {}
|
203
|
-
idx = 0
|
204
|
-
ws.xpath('./ss:Table/ss:Column').each do |c|
|
205
|
-
column_attributes[(idx += 1).to_s] = c['StyleID']
|
206
|
-
end
|
207
|
-
ws.xpath('./ss:Table/ss:Row').each do |r|
|
208
|
-
skip_to_row = r['Index'].to_i
|
209
|
-
row = skip_to_row if skip_to_row > 0
|
210
|
-
style_name = r['StyleID'] if r['StyleID']
|
211
|
-
r.xpath('./ss:Cell').each do |c|
|
212
|
-
skip_to_col = c['Index'].to_i
|
213
|
-
col = skip_to_col if skip_to_col > 0
|
214
|
-
if c['StyleID']
|
215
|
-
style_name = c['StyleID']
|
216
|
-
elsif
|
217
|
-
style_name ||= column_attributes[c['Index']]
|
218
|
-
end
|
219
|
-
c.xpath('./ss:Data').each do |cell|
|
220
|
-
formula = cell['Formula']
|
221
|
-
value_type = cell['ss:Type'].downcase.to_sym
|
222
|
-
v = cell.content
|
223
|
-
str_v = v
|
224
|
-
case value_type
|
225
|
-
when :number
|
226
|
-
v = v.to_f
|
227
|
-
value_type = :float
|
228
|
-
when :datetime
|
229
|
-
if v =~ /^1899-12-31T(\d{2}:\d{2}:\d{2})/
|
230
|
-
v = $1
|
231
|
-
value_type = :time
|
232
|
-
elsif v =~ /([^T]+)T00:00:00.000/
|
233
|
-
v = $1
|
234
|
-
value_type = :date
|
235
|
-
end
|
236
|
-
when :boolean
|
237
|
-
v = cell['boolean-value']
|
238
|
-
end
|
239
|
-
set_cell_values(sheet,col,row,0,v,value_type,formula,cell,str_v,style_name)
|
240
|
-
end
|
241
|
-
col += 1
|
242
|
-
end
|
243
|
-
row += 1
|
244
|
-
col = 1
|
245
|
-
end
|
246
|
-
end
|
247
|
-
if !sheet_found
|
248
|
-
raise RangeError, "Unable to find sheet #{sheet} for reading"
|
249
|
-
end
|
250
|
-
@cells_read[sheet] = true
|
251
|
-
end
|
252
|
-
|
253
|
-
def read_styles
|
254
|
-
@doc.xpath("/ss:Workbook/ss:Styles/ss:Style").each do |style|
|
255
|
-
style_id = style['ID']
|
256
|
-
@style_definitions[style_id] = Roo::Excel2003XML::Font.new
|
257
|
-
if font = style.at_xpath('./ss:Font')
|
258
|
-
@style_definitions[style_id].bold = font['Bold']
|
259
|
-
@style_definitions[style_id].italic = font['Italic']
|
260
|
-
@style_definitions[style_id].underline = font['Underline']
|
261
|
-
end
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
A_ROO_TYPE = {
|
266
|
-
"float" => :float,
|
267
|
-
"string" => :string,
|
268
|
-
"date" => :date,
|
269
|
-
"percentage" => :percentage,
|
270
|
-
"time" => :time,
|
271
|
-
}
|
272
|
-
|
273
|
-
def self.oo_type_2_roo_type(ootype)
|
274
|
-
return A_ROO_TYPE[ootype]
|
275
|
-
end
|
276
|
-
|
277
|
-
# helper method to convert compressed spaces and other elements within
|
278
|
-
# an text into a string
|
279
|
-
def children_to_string(children)
|
280
|
-
result = ''
|
281
|
-
children.each {|child|
|
282
|
-
if child.text?
|
283
|
-
result = result + child.content
|
284
|
-
else
|
285
|
-
if child.name == 's'
|
286
|
-
compressed_spaces = child['c'].to_i
|
287
|
-
# no explicit number means a count of 1:
|
288
|
-
if compressed_spaces == 0
|
289
|
-
compressed_spaces = 1
|
290
|
-
end
|
291
|
-
result = result + " "*compressed_spaces
|
292
|
-
else
|
293
|
-
result = result + child.content
|
294
|
-
end
|
295
|
-
end
|
296
|
-
}
|
297
|
-
result
|
298
|
-
end
|
299
|
-
|
300
|
-
end # class
|