roo 1.10.1 → 1.10.2
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.
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +38 -0
- data/History.txt +4 -4
- data/License.txt +20 -0
- data/Manifest.txt +68 -0
- data/README.markdown +109 -0
- data/Rakefile +5 -4
- data/bin/roo +0 -0
- data/examples/roo_soap_client.rb +53 -0
- data/examples/roo_soap_server.rb +29 -0
- data/examples/write_me.rb +33 -0
- data/lib/roo.rb +20 -61
- data/lib/roo/csv.rb +13 -11
- data/lib/roo/excel.rb +108 -219
- data/lib/roo/excel2003xml.rb +312 -0
- data/lib/roo/excelx.rb +205 -341
- data/lib/roo/generic_spreadsheet.rb +371 -268
- data/lib/roo/google.rb +64 -54
- data/lib/roo/openoffice.rb +101 -156
- data/lib/roo/roo_rails_helper.rb +5 -5
- data/lib/roo/worksheet.rb +18 -0
- data/roo.gemspec +43 -0
- data/scripts/txt2html +67 -0
- data/test/all_ss.rb +8 -10
- data/test/{1900_base.xls → files/1900_base.xls} +0 -0
- data/test/{1904_base.xls → files/1904_base.xls} +0 -0
- data/test/{Bibelbund.csv → files/Bibelbund.csv} +0 -0
- data/test/{Bibelbund.ods → files/Bibelbund.ods} +0 -0
- data/test/{Bibelbund.xls → files/Bibelbund.xls} +0 -0
- data/test/{Bibelbund.xlsx → files/Bibelbund.xlsx} +0 -0
- data/test/files/Bibelbund.xml +62518 -0
- data/test/{Bibelbund1.ods → files/Bibelbund1.ods} +0 -0
- data/test/{Pfand_from_windows_phone.xlsx → files/Pfand_from_windows_phone.xlsx} +0 -0
- data/test/files/bad_excel_date.xls +0 -0
- data/test/{bbu.ods → files/bbu.ods} +0 -0
- data/test/{bbu.xls → files/bbu.xls} +0 -0
- data/test/{bbu.xlsx → files/bbu.xlsx} +0 -0
- data/test/files/bbu.xml +152 -0
- data/test/{bode-v1.ods.zip → files/bode-v1.ods.zip} +0 -0
- data/test/{bode-v1.xls.zip → files/bode-v1.xls.zip} +0 -0
- data/test/{boolean.ods → files/boolean.ods} +0 -0
- data/test/{boolean.xls → files/boolean.xls} +0 -0
- data/test/{boolean.xlsx → files/boolean.xlsx} +0 -0
- data/test/files/boolean.xml +112 -0
- data/test/{borders.ods → files/borders.ods} +0 -0
- data/test/{borders.xls → files/borders.xls} +0 -0
- data/test/{borders.xlsx → files/borders.xlsx} +0 -0
- data/test/files/borders.xml +144 -0
- data/test/{bug-row-column-fixnum-float.xls → files/bug-row-column-fixnum-float.xls} +0 -0
- data/test/files/bug-row-column-fixnum-float.xml +127 -0
- data/test/{comments.ods → files/comments.ods} +0 -0
- data/test/{comments.xls → files/comments.xls} +0 -0
- data/test/{comments.xlsx → files/comments.xlsx} +0 -0
- data/test/{csvtypes.csv → files/csvtypes.csv} +0 -0
- data/test/{datetime.ods → files/datetime.ods} +0 -0
- data/test/{datetime.xls → files/datetime.xls} +0 -0
- data/test/{datetime.xlsx → files/datetime.xlsx} +0 -0
- data/test/files/datetime.xml +142 -0
- data/test/{datetime_floatconv.xls → files/datetime_floatconv.xls} +0 -0
- data/test/files/datetime_floatconv.xml +148 -0
- data/test/{dreimalvier.ods → files/dreimalvier.ods} +0 -0
- data/test/{emptysheets.ods → files/emptysheets.ods} +0 -0
- data/test/{emptysheets.xls → files/emptysheets.xls} +0 -0
- data/test/{emptysheets.xlsx → files/emptysheets.xlsx} +0 -0
- data/test/files/emptysheets.xml +105 -0
- data/test/files/excel2003.xml +21140 -0
- data/test/{false_encoding.xls → files/false_encoding.xls} +0 -0
- data/test/files/false_encoding.xml +132 -0
- data/test/{formula.ods → files/formula.ods} +0 -0
- data/test/{formula.xls → files/formula.xls} +0 -0
- data/test/{formula.xlsx → files/formula.xlsx} +0 -0
- data/test/files/formula.xml +134 -0
- data/test/files/formula_parse_error.xls +0 -0
- data/test/files/formula_parse_error.xml +1833 -0
- data/test/{formula_string_error.xlsx → files/formula_string_error.xlsx} +0 -0
- data/test/{html-escape.ods → files/html-escape.ods} +0 -0
- data/test/{matrix.ods → files/matrix.ods} +0 -0
- data/test/{matrix.xls → files/matrix.xls} +0 -0
- data/test/{named_cells.ods → files/named_cells.ods} +0 -0
- data/test/{named_cells.xls → files/named_cells.xls} +0 -0
- data/test/{named_cells.xlsx → files/named_cells.xlsx} +0 -0
- data/test/{no_spreadsheet_file.txt → files/no_spreadsheet_file.txt} +0 -0
- data/test/{numbers1.csv → files/numbers1.csv} +0 -0
- data/test/{numbers1.ods → files/numbers1.ods} +0 -0
- data/test/{numbers1.xls → files/numbers1.xls} +0 -0
- data/test/{numbers1.xlsx → files/numbers1.xlsx} +0 -0
- data/test/files/numbers1.xml +312 -0
- data/test/{only_one_sheet.ods → files/only_one_sheet.ods} +0 -0
- data/test/{only_one_sheet.xls → files/only_one_sheet.xls} +0 -0
- data/test/{only_one_sheet.xlsx → files/only_one_sheet.xlsx} +0 -0
- data/test/files/only_one_sheet.xml +67 -0
- data/test/{paragraph.ods → files/paragraph.ods} +0 -0
- data/test/{paragraph.xls → files/paragraph.xls} +0 -0
- data/test/{paragraph.xlsx → files/paragraph.xlsx} +0 -0
- data/test/files/paragraph.xml +127 -0
- data/test/{prova.xls → files/prova.xls} +0 -0
- data/test/{ric.ods → files/ric.ods} +0 -0
- data/test/{simple_spreadsheet.ods → files/simple_spreadsheet.ods} +0 -0
- data/test/{simple_spreadsheet.xls → files/simple_spreadsheet.xls} +0 -0
- data/test/{simple_spreadsheet.xlsx → files/simple_spreadsheet.xlsx} +0 -0
- data/test/files/simple_spreadsheet.xml +225 -0
- data/test/{simple_spreadsheet_from_italo.ods → files/simple_spreadsheet_from_italo.ods} +0 -0
- data/test/{simple_spreadsheet_from_italo.xls → files/simple_spreadsheet_from_italo.xls} +0 -0
- data/test/files/simple_spreadsheet_from_italo.xml +242 -0
- data/test/{so_datetime.csv → files/so_datetime.csv} +0 -0
- data/test/{style.ods → files/style.ods} +0 -0
- data/test/{style.xls → files/style.xls} +0 -0
- data/test/{style.xlsx → files/style.xlsx} +0 -0
- data/test/files/style.xml +154 -0
- data/test/{time-test.csv → files/time-test.csv} +0 -0
- data/test/{time-test.ods → files/time-test.ods} +0 -0
- data/test/{time-test.xls → files/time-test.xls} +0 -0
- data/test/{time-test.xlsx → files/time-test.xlsx} +0 -0
- data/test/files/time-test.xml +131 -0
- data/test/{type_excel.ods → files/type_excel.ods} +0 -0
- data/test/{type_excel.xlsx → files/type_excel.xlsx} +0 -0
- data/test/{type_excelx.ods → files/type_excelx.ods} +0 -0
- data/test/{type_excelx.xls → files/type_excelx.xls} +0 -0
- data/test/{type_openoffice.xls → files/type_openoffice.xls} +0 -0
- data/test/{type_openoffice.xlsx → files/type_openoffice.xlsx} +0 -0
- data/test/{whitespace.ods → files/whitespace.ods} +0 -0
- data/test/{whitespace.xls → files/whitespace.xls} +0 -0
- data/test/{whitespace.xlsx → files/whitespace.xlsx} +0 -0
- data/test/files/whitespace.xml +184 -0
- data/test/test_generic_spreadsheet.rb +257 -0
- data/test/test_helper.rb +167 -27
- data/test/test_roo.rb +1178 -930
- data/website/index.html +385 -0
- data/website/index.txt +423 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +130 -0
- data/website/template.rhtml +48 -0
- metadata +151 -121
- data/README.txt +0 -110
- data/lib/roo/.csv.rb.swp +0 -0
@@ -0,0 +1,312 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'zip/zipfilesystem'
|
3
|
+
require 'date'
|
4
|
+
require 'base64'
|
5
|
+
require 'cgi'
|
6
|
+
|
7
|
+
class Roo::Excel2003XML < Roo::GenericSpreadsheet
|
8
|
+
|
9
|
+
# initialization and opening of a spreadsheet file
|
10
|
+
# values for packed: :zip
|
11
|
+
def initialize(filename, packed=nil, file_warning=:error)
|
12
|
+
make_tmpdir do |tmpdir|
|
13
|
+
filename = open_from_uri(filename, tmpdir) if uri?(filename)
|
14
|
+
filename = unzip(filename, tmpdir) if packed == :zip
|
15
|
+
|
16
|
+
file_type_check(filename,'.xml','an Excel 2003 XML', file_warning)
|
17
|
+
@cells_read = Hash.new
|
18
|
+
@filename = filename
|
19
|
+
unless File.file?(@filename)
|
20
|
+
raise IOError, "file #{@filename} does not exist"
|
21
|
+
end
|
22
|
+
@doc = Nokogiri::XML(open(@filename))
|
23
|
+
end
|
24
|
+
@default_sheet = self.sheets.first
|
25
|
+
@cell = Hash.new
|
26
|
+
@cell_type = Hash.new
|
27
|
+
@formula = Hash.new
|
28
|
+
@first_row = Hash.new
|
29
|
+
@last_row = Hash.new
|
30
|
+
@first_column = Hash.new
|
31
|
+
@last_column = Hash.new
|
32
|
+
@header_line = 1
|
33
|
+
@style = Hash.new
|
34
|
+
@style_defaults = Hash.new { |h,k| h[k] = [] }
|
35
|
+
@style_definitions = Hash.new
|
36
|
+
read_styles
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the content of a spreadsheet-cell.
|
40
|
+
# (1,1) is the upper left corner.
|
41
|
+
# (1,1), (1,'A'), ('A',1), ('a',1) all refers to the
|
42
|
+
# cell at the first line and first row.
|
43
|
+
def cell(row, col, sheet=nil)
|
44
|
+
sheet ||= @default_sheet
|
45
|
+
read_cells(sheet) unless @cells_read[sheet]
|
46
|
+
row,col = normalize(row,col)
|
47
|
+
if celltype(row,col,sheet) == :date
|
48
|
+
yyyy,mm,dd = @cell[sheet][[row,col]].split('-')
|
49
|
+
return Date.new(yyyy.to_i,mm.to_i,dd.to_i)
|
50
|
+
end
|
51
|
+
@cell[sheet][[row,col]]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the formula at (row,col).
|
55
|
+
# Returns nil if there is no formula.
|
56
|
+
# The method #formula? checks if there is a formula.
|
57
|
+
def formula(row,col,sheet=nil)
|
58
|
+
sheet ||= @default_sheet
|
59
|
+
read_cells(sheet) unless @cells_read[sheet]
|
60
|
+
row,col = normalize(row,col)
|
61
|
+
if @formula[sheet][[row,col]] == nil
|
62
|
+
return nil
|
63
|
+
else
|
64
|
+
return @formula[sheet][[row,col]]["oooc:".length..-1]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# true, if there is a formula
|
69
|
+
def formula?(row,col,sheet=nil)
|
70
|
+
sheet ||= @default_sheet
|
71
|
+
read_cells(sheet) unless @cells_read[sheet]
|
72
|
+
row,col = normalize(row,col)
|
73
|
+
formula(row,col) != nil
|
74
|
+
end
|
75
|
+
|
76
|
+
class Font
|
77
|
+
attr_accessor :bold, :italic, :underline
|
78
|
+
|
79
|
+
def bold?
|
80
|
+
@bold == '1'
|
81
|
+
end
|
82
|
+
|
83
|
+
def italic?
|
84
|
+
@italic == '1'
|
85
|
+
end
|
86
|
+
|
87
|
+
def underline?
|
88
|
+
@underline != nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Given a cell, return the cell's style
|
93
|
+
def font(row, col, sheet=nil)
|
94
|
+
sheet ||= @default_sheet
|
95
|
+
read_cells(sheet) unless @cells_read[sheet]
|
96
|
+
row,col = normalize(row,col)
|
97
|
+
style_name = @style[sheet][[row,col]] || @style_defaults[sheet][col - 1] || 'Default'
|
98
|
+
@style_definitions[style_name]
|
99
|
+
end
|
100
|
+
|
101
|
+
# returns the type of a cell:
|
102
|
+
# * :float
|
103
|
+
# * :string
|
104
|
+
# * :date
|
105
|
+
# * :percentage
|
106
|
+
# * :formula
|
107
|
+
# * :time
|
108
|
+
# * :datetime
|
109
|
+
def celltype(row,col,sheet=nil)
|
110
|
+
sheet ||= @default_sheet
|
111
|
+
read_cells(sheet) unless @cells_read[sheet]
|
112
|
+
row,col = normalize(row,col)
|
113
|
+
if @formula[sheet][[row,col]]
|
114
|
+
return :formula
|
115
|
+
else
|
116
|
+
@cell_type[sheet][[row,col]]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def sheets
|
121
|
+
@doc.xpath("/ss:Workbook/ss:Worksheet").map do |sheet|
|
122
|
+
sheet['Name']
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# version of the openoffice document
|
127
|
+
# at 2007 this is always "1.0"
|
128
|
+
def officeversion
|
129
|
+
oo_version
|
130
|
+
@officeversion
|
131
|
+
end
|
132
|
+
|
133
|
+
# shows the internal representation of all cells
|
134
|
+
# mainly for debugging purposes
|
135
|
+
def to_s(sheet=nil)
|
136
|
+
sheet ||= @default_sheet
|
137
|
+
read_cells(sheet) unless @cells_read[sheet]
|
138
|
+
@cell[sheet].inspect
|
139
|
+
end
|
140
|
+
|
141
|
+
# save spreadsheet
|
142
|
+
def save #:nodoc:
|
143
|
+
42
|
144
|
+
end
|
145
|
+
|
146
|
+
# returns each formula in the selected sheet as an array of elements
|
147
|
+
# [row, col, formula]
|
148
|
+
def formulas(sheet=nil)
|
149
|
+
theformulas = Array.new
|
150
|
+
sheet ||= @default_sheet
|
151
|
+
read_cells(sheet) unless @cells_read[sheet]
|
152
|
+
first_row(sheet).upto(last_row(sheet)) {|row|
|
153
|
+
first_column(sheet).upto(last_column(sheet)) {|col|
|
154
|
+
if formula?(row,col,sheet)
|
155
|
+
f = [row, col, formula(row,col,sheet)]
|
156
|
+
theformulas << f
|
157
|
+
end
|
158
|
+
}
|
159
|
+
}
|
160
|
+
theformulas
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
# read the version of the OO-Version
|
166
|
+
def oo_version
|
167
|
+
@doc.find("//*[local-name()='document-content']").each do |office|
|
168
|
+
@officeversion = office['version']
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# helper function to set the internal representation of cells
|
173
|
+
def set_cell_values(sheet,x,y,i,v,value_type,formula,table_cell,str_v,style_name)
|
174
|
+
key = [y,x+i]
|
175
|
+
@cell_type[sheet] = {} unless @cell_type[sheet]
|
176
|
+
@cell_type[sheet][key] = value_type
|
177
|
+
@formula[sheet] = {} unless @formula[sheet]
|
178
|
+
@formula[sheet][key] = formula if formula
|
179
|
+
@cell[sheet] = {} unless @cell[sheet]
|
180
|
+
@style[sheet] = {} unless @style[sheet]
|
181
|
+
@style[sheet][key] = style_name
|
182
|
+
@cell[sheet][key] =
|
183
|
+
case @cell_type[sheet][key]
|
184
|
+
when :float
|
185
|
+
v.to_f
|
186
|
+
when :string
|
187
|
+
str_v
|
188
|
+
when :datetime
|
189
|
+
DateTime.parse(v)
|
190
|
+
when :percentage
|
191
|
+
v.to_f
|
192
|
+
# when :time
|
193
|
+
# hms = v.split(':')
|
194
|
+
# hms[0].to_i*3600 + hms[1].to_i*60 + hms[2].to_i
|
195
|
+
else
|
196
|
+
v
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# read all cells in the selected sheet
|
201
|
+
#--
|
202
|
+
# the following construct means '4 blanks'
|
203
|
+
# some content <text:s text:c="3"/>
|
204
|
+
#++
|
205
|
+
def read_cells(sheet=nil)
|
206
|
+
sheet ||= @default_sheet
|
207
|
+
sheet_found = false
|
208
|
+
raise ArgumentError, "Error: sheet '#{sheet||'nil'}' not valid" if @default_sheet == nil and sheet==nil
|
209
|
+
raise RangeError unless self.sheets.include? sheet
|
210
|
+
@doc.xpath("/ss:Workbook/ss:Worksheet[@ss:Name='#{sheet}']").each do |ws|
|
211
|
+
sheet_found = true
|
212
|
+
row = 1
|
213
|
+
col = 1
|
214
|
+
column_attributes = {}
|
215
|
+
idx = 0
|
216
|
+
ws.xpath('./ss:Table/ss:Column').each do |c|
|
217
|
+
column_attributes[(idx += 1).to_s] = c['StyleID']
|
218
|
+
end
|
219
|
+
ws.xpath('./ss:Table/ss:Row').each do |r|
|
220
|
+
skip_to_row = r['Index'].to_i
|
221
|
+
row = skip_to_row if skip_to_row > 0
|
222
|
+
style_name = r['StyleID'] if r['StyleID']
|
223
|
+
r.xpath('./ss:Cell').each do |c|
|
224
|
+
skip_to_col = c['Index'].to_i
|
225
|
+
col = skip_to_col if skip_to_col > 0
|
226
|
+
if c['StyleID']
|
227
|
+
style_name = c['StyleID']
|
228
|
+
elsif
|
229
|
+
style_name ||= column_attributes[c['Index']]
|
230
|
+
end
|
231
|
+
c.xpath('./ss:Data').each do |cell|
|
232
|
+
formula = cell['Formula']
|
233
|
+
value_type = cell['Type'].downcase.to_sym
|
234
|
+
v = cell.content
|
235
|
+
str_v = v
|
236
|
+
case value_type
|
237
|
+
when :number
|
238
|
+
v = v.to_f
|
239
|
+
value_type = :float
|
240
|
+
when :datetime
|
241
|
+
if v =~ /^1899-12-31T(\d{2}:\d{2}:\d{2})/
|
242
|
+
v = $1
|
243
|
+
value_type = :time
|
244
|
+
elsif v =~ /([^T]+)T00:00:00.000/
|
245
|
+
v = $1
|
246
|
+
value_type = :date
|
247
|
+
end
|
248
|
+
when :boolean
|
249
|
+
v = cell['boolean-value']
|
250
|
+
end
|
251
|
+
set_cell_values(sheet,col,row,0,v,value_type,formula,cell,str_v,style_name)
|
252
|
+
end
|
253
|
+
col += 1
|
254
|
+
end
|
255
|
+
row += 1
|
256
|
+
col = 1
|
257
|
+
end
|
258
|
+
end
|
259
|
+
if !sheet_found
|
260
|
+
raise RangeError, "Unable to find sheet #{sheet} for reading"
|
261
|
+
end
|
262
|
+
@cells_read[sheet] = true
|
263
|
+
end
|
264
|
+
|
265
|
+
def read_styles
|
266
|
+
@doc.xpath("/ss:Workbook/ss:Styles/ss:Style").each do |style|
|
267
|
+
style_id = style['ID']
|
268
|
+
@style_definitions[style_id] = Roo::Excel2003XML::Font.new
|
269
|
+
if font = style.at_xpath('./ss:Font')
|
270
|
+
@style_definitions[style_id].bold = font['Bold']
|
271
|
+
@style_definitions[style_id].italic = font['Italic']
|
272
|
+
@style_definitions[style_id].underline = font['Underline']
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
A_ROO_TYPE = {
|
278
|
+
"float" => :float,
|
279
|
+
"string" => :string,
|
280
|
+
"date" => :date,
|
281
|
+
"percentage" => :percentage,
|
282
|
+
"time" => :time,
|
283
|
+
}
|
284
|
+
|
285
|
+
def self.oo_type_2_roo_type(ootype)
|
286
|
+
return A_ROO_TYPE[ootype]
|
287
|
+
end
|
288
|
+
|
289
|
+
# helper method to convert compressed spaces and other elements within
|
290
|
+
# an text into a string
|
291
|
+
def children_to_string(children)
|
292
|
+
result = ''
|
293
|
+
children.each {|child|
|
294
|
+
if child.text?
|
295
|
+
result = result + child.content
|
296
|
+
else
|
297
|
+
if child.name == 's'
|
298
|
+
compressed_spaces = child['c'].to_i
|
299
|
+
# no explicit number means a count of 1:
|
300
|
+
if compressed_spaces == 0
|
301
|
+
compressed_spaces = 1
|
302
|
+
end
|
303
|
+
result = result + " "*compressed_spaces
|
304
|
+
else
|
305
|
+
result = result + child.content
|
306
|
+
end
|
307
|
+
end
|
308
|
+
}
|
309
|
+
result
|
310
|
+
end
|
311
|
+
|
312
|
+
end # class
|
data/lib/roo/excelx.rb
CHANGED
@@ -12,7 +12,7 @@ if RUBY_VERSION < '1.9.0'
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
class Excelx < GenericSpreadsheet
|
15
|
+
class Roo::Excelx < Roo::GenericSpreadsheet
|
16
16
|
FORMATS = {
|
17
17
|
'General' => :float,
|
18
18
|
'0' => :float,
|
@@ -51,7 +51,7 @@ class Excelx < GenericSpreadsheet
|
|
51
51
|
'yyyy-mm-dd' => :date, # 2011-09-16
|
52
52
|
# was used in a spreadsheet file from a windows phone
|
53
53
|
}
|
54
|
-
STANDARD_FORMATS = {
|
54
|
+
STANDARD_FORMATS = {
|
55
55
|
0 => 'General',
|
56
56
|
1 => '0',
|
57
57
|
2 => '0.00',
|
@@ -81,62 +81,50 @@ class Excelx < GenericSpreadsheet
|
|
81
81
|
48 => '##0.0E+0',
|
82
82
|
49 => '@',
|
83
83
|
}
|
84
|
-
@@nr = 0
|
85
84
|
|
86
85
|
# initialization and opening of a spreadsheet file
|
87
86
|
# values for packed: :zip
|
88
87
|
def initialize(filename, packed=nil, file_warning = :error) #, create = false)
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
@
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
@sheet_files.each_with_index do |item, i|
|
129
|
-
file = File.new(item)
|
130
|
-
@sheet_doc[i] = Nokogiri::XML(file)
|
131
|
-
file.close
|
132
|
-
end
|
133
|
-
@comments_doc = []
|
134
|
-
@comments_files.each_with_index do |item, i|
|
135
|
-
file = File.new(item)
|
136
|
-
@comments_doc[i] = Nokogiri::XML(file)
|
137
|
-
file.close
|
88
|
+
file_type_check(filename,'.xlsx','an Excel-xlsx', file_warning, packed)
|
89
|
+
make_tmpdir do |tmpdir|
|
90
|
+
filename = open_from_uri(filename, tmpdir) if uri?(filename)
|
91
|
+
filename = unzip(filename, tmpdir) if packed == :zip
|
92
|
+
@cells_read = Hash.new
|
93
|
+
@filename = filename
|
94
|
+
unless File.file?(@filename)
|
95
|
+
raise IOError, "file #{@filename} does not exist"
|
96
|
+
end
|
97
|
+
@comments_files = Array.new
|
98
|
+
extract_content(tmpdir, @filename)
|
99
|
+
@workbook_doc = File.open(File.join(tmpdir, "roo_workbook.xml")) do |file|
|
100
|
+
Nokogiri::XML(file)
|
101
|
+
end
|
102
|
+
@shared_table = []
|
103
|
+
if File.exist?(File.join(tmpdir, 'roo_sharedStrings.xml'))
|
104
|
+
@sharedstring_doc = File.open(File.join(tmpdir, 'roo_sharedStrings.xml')) do |file|
|
105
|
+
Nokogiri::XML(file)
|
106
|
+
end
|
107
|
+
read_shared_strings(@sharedstring_doc)
|
108
|
+
end
|
109
|
+
@styles_table = []
|
110
|
+
@style_definitions = Array.new # TODO: ??? { |h,k| h[k] = {} }
|
111
|
+
if File.exist?(File.join(tmpdir, 'roo_styles.xml'))
|
112
|
+
@styles_doc = File.open(File.join(tmpdir, 'roo_styles.xml')) do |file|
|
113
|
+
Nokogiri::XML(file)
|
114
|
+
end
|
115
|
+
read_styles(@styles_doc)
|
116
|
+
end
|
117
|
+
@sheet_doc = @sheet_files.map do |item|
|
118
|
+
File.open(item) do |file|
|
119
|
+
Nokogiri::XML(file)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
@comments_doc = @comments_files.map do |item|
|
123
|
+
File.open(item) do |file|
|
124
|
+
Nokogiri::XML(file)
|
125
|
+
end
|
126
|
+
end
|
138
127
|
end
|
139
|
-
FileUtils::rm_r(@tmpdir)
|
140
128
|
@default_sheet = self.sheets.first
|
141
129
|
@cell = Hash.new
|
142
130
|
@cell_type = Hash.new
|
@@ -149,17 +137,15 @@ class Excelx < GenericSpreadsheet
|
|
149
137
|
@excelx_type = Hash.new
|
150
138
|
@excelx_value = Hash.new
|
151
139
|
@s_attribute = Hash.new # TODO: ggf. wieder entfernen nur lokal benoetigt
|
152
|
-
@label = Hash.new
|
153
|
-
@labels_read = false
|
154
140
|
@comment = Hash.new
|
155
141
|
@comments_read = Hash.new
|
156
142
|
end
|
157
143
|
|
158
144
|
def method_missing(m,*args)
|
159
145
|
# is method name a label name
|
160
|
-
read_labels
|
146
|
+
read_labels
|
161
147
|
if @label.has_key?(m.to_s)
|
162
|
-
sheet
|
148
|
+
sheet ||= @default_sheet
|
163
149
|
read_cells(sheet) unless @cells_read[sheet]
|
164
150
|
row,col = label(m.to_s)
|
165
151
|
cell(row,col)
|
@@ -168,13 +154,13 @@ class Excelx < GenericSpreadsheet
|
|
168
154
|
super
|
169
155
|
end
|
170
156
|
end
|
171
|
-
|
157
|
+
|
172
158
|
# Returns the content of a spreadsheet-cell.
|
173
159
|
# (1,1) is the upper left corner.
|
174
160
|
# (1,1), (1,'A'), ('A',1), ('a',1) all refers to the
|
175
161
|
# cell at the first line and first row.
|
176
162
|
def cell(row, col, sheet=nil)
|
177
|
-
sheet
|
163
|
+
sheet ||= @default_sheet
|
178
164
|
read_cells(sheet) unless @cells_read[sheet]
|
179
165
|
row,col = normalize(row,col)
|
180
166
|
if celltype(row,col,sheet) == :date
|
@@ -193,7 +179,7 @@ class Excelx < GenericSpreadsheet
|
|
193
179
|
# Returns nil if there is no formula.
|
194
180
|
# The method #formula? checks if there is a formula.
|
195
181
|
def formula(row,col,sheet=nil)
|
196
|
-
sheet
|
182
|
+
sheet ||= @default_sheet
|
197
183
|
read_cells(sheet) unless @cells_read[sheet]
|
198
184
|
row,col = normalize(row,col)
|
199
185
|
if @formula[sheet][[row,col]] == nil
|
@@ -205,7 +191,7 @@ class Excelx < GenericSpreadsheet
|
|
205
191
|
|
206
192
|
# true, if there is a formula
|
207
193
|
def formula?(row,col,sheet=nil)
|
208
|
-
sheet
|
194
|
+
sheet ||= @default_sheet
|
209
195
|
read_cells(sheet) unless @cells_read[sheet]
|
210
196
|
row,col = normalize(row,col)
|
211
197
|
formula(row,col) != nil
|
@@ -214,7 +200,7 @@ class Excelx < GenericSpreadsheet
|
|
214
200
|
# returns each formula in the selected sheet as an array of elements
|
215
201
|
# [row, col, formula]
|
216
202
|
def formulas(sheet=nil)
|
217
|
-
sheet
|
203
|
+
sheet ||= @default_sheet
|
218
204
|
read_cells(sheet) unless @cells_read[sheet]
|
219
205
|
if @formula[sheet]
|
220
206
|
@formula[sheet].each.collect do |elem|
|
@@ -227,47 +213,29 @@ class Excelx < GenericSpreadsheet
|
|
227
213
|
|
228
214
|
class Font
|
229
215
|
attr_accessor :bold, :italic, :underline
|
230
|
-
|
216
|
+
|
231
217
|
def bold?
|
232
218
|
@bold == true
|
233
219
|
end
|
234
|
-
|
235
|
-
def italic?
|
220
|
+
|
221
|
+
def italic?
|
236
222
|
@italic == true
|
237
223
|
end
|
238
|
-
|
224
|
+
|
239
225
|
def underline?
|
240
226
|
@underline == true
|
241
|
-
end
|
227
|
+
end
|
242
228
|
end
|
243
|
-
|
229
|
+
|
244
230
|
# Given a cell, return the cell's style
|
245
231
|
def font(row, col, sheet=nil)
|
246
|
-
sheet
|
232
|
+
sheet ||= @default_sheet
|
247
233
|
read_cells(sheet) unless @cells_read[sheet]
|
248
234
|
row,col = normalize(row,col)
|
249
235
|
s_attribute = @s_attribute[sheet][[row,col]]
|
250
236
|
s_attribute ||= 0
|
251
237
|
s_attribute = s_attribute.to_i
|
252
238
|
@style_definitions[s_attribute]
|
253
|
-
end
|
254
|
-
|
255
|
-
# set a cell to a certain value
|
256
|
-
# (this will not be saved back to the spreadsheet file!)
|
257
|
-
def set(row,col,value,sheet=nil) #:nodoc:
|
258
|
-
sheet = @default_sheet unless sheet
|
259
|
-
read_cells(sheet) unless @cells_read[sheet]
|
260
|
-
row,col = normalize(row,col)
|
261
|
-
set_value(row,col,value,sheet)
|
262
|
-
if value.class == Fixnum
|
263
|
-
set_type(row,col,:float,sheet)
|
264
|
-
elsif value.class == String
|
265
|
-
set_type(row,col,:string,sheet)
|
266
|
-
elsif value.class == Float
|
267
|
-
set_type(row,col,:string,sheet)
|
268
|
-
else
|
269
|
-
raise ArgumentError, "Type for "+value.to_s+" not set"
|
270
|
-
end
|
271
239
|
end
|
272
240
|
|
273
241
|
# returns the type of a cell:
|
@@ -279,7 +247,7 @@ class Excelx < GenericSpreadsheet
|
|
279
247
|
# * :time
|
280
248
|
# * :datetime
|
281
249
|
def celltype(row,col,sheet=nil)
|
282
|
-
sheet
|
250
|
+
sheet ||= @default_sheet
|
283
251
|
read_cells(sheet) unless @cells_read[sheet]
|
284
252
|
row,col = normalize(row,col)
|
285
253
|
if @formula[sheet][[row,col]]
|
@@ -291,47 +259,44 @@ class Excelx < GenericSpreadsheet
|
|
291
259
|
|
292
260
|
# returns the internal type of an excel cell
|
293
261
|
# * :numeric_or_formula
|
294
|
-
# * :string
|
295
|
-
# Note: this is only available within the Excelx class
|
262
|
+
# * :string
|
263
|
+
# Note: this is only available within the Excelx class
|
296
264
|
def excelx_type(row,col,sheet=nil)
|
297
|
-
sheet
|
265
|
+
sheet ||= @default_sheet
|
298
266
|
read_cells(sheet) unless @cells_read[sheet]
|
299
267
|
row,col = normalize(row,col)
|
300
268
|
return @excelx_type[sheet][[row,col]]
|
301
269
|
end
|
302
|
-
|
270
|
+
|
303
271
|
# returns the internal value of an excelx cell
|
304
|
-
# Note: this is only available within the Excelx class
|
272
|
+
# Note: this is only available within the Excelx class
|
305
273
|
def excelx_value(row,col,sheet=nil)
|
306
|
-
sheet
|
274
|
+
sheet ||= @default_sheet
|
307
275
|
read_cells(sheet) unless @cells_read[sheet]
|
308
276
|
row,col = normalize(row,col)
|
309
277
|
return @excelx_value[sheet][[row,col]]
|
310
278
|
end
|
311
|
-
|
279
|
+
|
312
280
|
# returns the internal format of an excel cell
|
313
281
|
def excelx_format(row,col,sheet=nil)
|
314
|
-
sheet
|
282
|
+
sheet ||= @default_sheet
|
315
283
|
read_cells(sheet) unless @cells_read[sheet]
|
316
284
|
row,col = normalize(row,col)
|
317
285
|
s = @s_attribute[sheet][[row,col]]
|
318
|
-
|
319
|
-
result
|
286
|
+
attribute2format(s).to_s
|
320
287
|
end
|
321
|
-
|
288
|
+
|
322
289
|
# returns an array of sheet names in the spreadsheet
|
323
290
|
def sheets
|
324
|
-
|
325
|
-
|
326
|
-
return_sheets << sheet['name']
|
291
|
+
@workbook_doc.xpath("//xmlns:sheet").map do |sheet|
|
292
|
+
sheet['name']
|
327
293
|
end
|
328
|
-
return_sheets
|
329
294
|
end
|
330
295
|
|
331
296
|
# shows the internal representation of all cells
|
332
297
|
# for debugging purposes
|
333
298
|
def to_s(sheet=nil)
|
334
|
-
sheet
|
299
|
+
sheet ||= @default_sheet
|
335
300
|
read_cells(sheet) unless @cells_read[sheet]
|
336
301
|
@cell[sheet].inspect
|
337
302
|
end
|
@@ -339,40 +304,35 @@ class Excelx < GenericSpreadsheet
|
|
339
304
|
# returns the row,col values of the labelled cell
|
340
305
|
# (nil,nil) if label is not defined
|
341
306
|
def label(labelname)
|
342
|
-
read_labels
|
343
|
-
|
307
|
+
read_labels
|
308
|
+
if @label.empty? || !@label.has_key?(labelname)
|
344
309
|
return nil,nil,nil
|
345
|
-
|
346
|
-
if @label.has_key? labelname
|
310
|
+
else
|
347
311
|
return @label[labelname][1].to_i,
|
348
|
-
GenericSpreadsheet.letter_to_number(@label[labelname][2]),
|
312
|
+
Roo::GenericSpreadsheet.letter_to_number(@label[labelname][2]),
|
349
313
|
@label[labelname][0]
|
350
|
-
else
|
351
|
-
return nil,nil,nil
|
352
314
|
end
|
353
315
|
end
|
354
316
|
|
355
317
|
# Returns an array which all labels. Each element is an array with
|
356
|
-
# [labelname, [
|
318
|
+
# [labelname, [row,col,sheetname]]
|
357
319
|
def labels
|
358
|
-
# sheet
|
320
|
+
# sheet ||= @default_sheet
|
359
321
|
# read_cells(sheet) unless @cells_read[sheet]
|
360
|
-
read_labels
|
361
|
-
|
362
|
-
|
363
|
-
result << [ label[0], # name
|
322
|
+
read_labels
|
323
|
+
@label.map do |label|
|
324
|
+
[ label[0], # name
|
364
325
|
[ label[1][1].to_i, # row
|
365
|
-
GenericSpreadsheet.letter_to_number(label[1][2]), # column
|
326
|
+
Roo::GenericSpreadsheet.letter_to_number(label[1][2]), # column
|
366
327
|
label[1][0], # sheet
|
367
328
|
] ]
|
368
329
|
end
|
369
|
-
result
|
370
330
|
end
|
371
331
|
|
372
332
|
# returns the comment at (row/col)
|
373
333
|
# nil if there is no comment
|
374
334
|
def comment(row,col,sheet=nil)
|
375
|
-
sheet
|
335
|
+
sheet ||= @default_sheet
|
376
336
|
#read_cells(sheet) unless @cells_read[sheet]
|
377
337
|
read_comments(sheet) unless @comments_read[sheet]
|
378
338
|
row,col = normalize(row,col)
|
@@ -382,7 +342,7 @@ class Excelx < GenericSpreadsheet
|
|
382
342
|
|
383
343
|
# true, if there is a comment
|
384
344
|
def comment?(row,col,sheet=nil)
|
385
|
-
sheet
|
345
|
+
sheet ||= @default_sheet
|
386
346
|
# read_cells(sheet) unless @cells_read[sheet]
|
387
347
|
read_comments(sheet) unless @comments_read[sheet]
|
388
348
|
row,col = normalize(row,col)
|
@@ -392,7 +352,7 @@ class Excelx < GenericSpreadsheet
|
|
392
352
|
# returns each comment in the selected sheet as an array of elements
|
393
353
|
# [row, col, comment]
|
394
354
|
def comments(sheet=nil)
|
395
|
-
sheet
|
355
|
+
sheet ||= @default_sheet
|
396
356
|
read_comments(sheet) unless @comments_read[sheet]
|
397
357
|
if @comment[sheet]
|
398
358
|
@comment[sheet].each.collect do |elem|
|
@@ -406,37 +366,38 @@ class Excelx < GenericSpreadsheet
|
|
406
366
|
private
|
407
367
|
|
408
368
|
# helper function to set the internal representation of cells
|
409
|
-
def set_cell_values(sheet,x,y,i,v,
|
369
|
+
def set_cell_values(sheet,x,y,i,v,value_type,formula,
|
410
370
|
excelx_type=nil,
|
411
371
|
excelx_value=nil,
|
412
372
|
s_attribute=nil)
|
413
373
|
key = [y,x+i]
|
414
|
-
@cell_type[sheet]
|
415
|
-
@cell_type[sheet][key] =
|
416
|
-
@formula[sheet]
|
374
|
+
@cell_type[sheet] ||= {}
|
375
|
+
@cell_type[sheet][key] = value_type
|
376
|
+
@formula[sheet] ||= {}
|
417
377
|
@formula[sheet][key] = formula if formula
|
418
|
-
@cell[sheet]
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
378
|
+
@cell[sheet] ||= {}
|
379
|
+
@cell[sheet][key] =
|
380
|
+
case @cell_type[sheet][key]
|
381
|
+
when :float
|
382
|
+
v.to_f
|
383
|
+
when :string
|
384
|
+
v
|
385
|
+
when :date
|
386
|
+
(Date.new(1899,12,30)+v.to_i).strftime("%Y-%m-%d")
|
387
|
+
when :datetime
|
388
|
+
(DateTime.new(1899,12,30)+v.to_f).strftime("%Y-%m-%d %H:%M:%S")
|
389
|
+
when :percentage
|
390
|
+
v.to_f
|
391
|
+
when :time
|
392
|
+
v.to_f*(24*60*60)
|
393
|
+
else
|
394
|
+
v
|
395
|
+
end
|
396
|
+
@excelx_type[sheet] ||= {}
|
436
397
|
@excelx_type[sheet][key] = excelx_type
|
437
|
-
@excelx_value[sheet]
|
398
|
+
@excelx_value[sheet] ||= {}
|
438
399
|
@excelx_value[sheet][key] = excelx_value
|
439
|
-
@s_attribute[sheet]
|
400
|
+
@s_attribute[sheet] ||= {}
|
440
401
|
@s_attribute[sheet][key] = s_attribute
|
441
402
|
end
|
442
403
|
|
@@ -451,107 +412,87 @@ class Excelx < GenericSpreadsheet
|
|
451
412
|
|
452
413
|
# read all cells in the selected sheet
|
453
414
|
def read_cells(sheet=nil)
|
454
|
-
sheet
|
455
|
-
|
456
|
-
|
457
|
-
raise RangeError unless self.sheets.include? sheet
|
458
|
-
n = self.sheets.index(sheet)
|
459
|
-
@sheet_doc[n].xpath("//*[local-name()='c']").each do |c|
|
415
|
+
sheet ||= @default_sheet
|
416
|
+
validate_sheet!(sheet)
|
417
|
+
@sheet_doc[sheets.index(sheet)].xpath("/xmlns:worksheet/xmlns:sheetData/xmlns:row/xmlns:c").each do |c|
|
460
418
|
s_attribute = c['s'].to_i # should be here
|
461
419
|
# c: <c r="A5" s="2">
|
462
420
|
# <v>22606</v>
|
463
421
|
# </c>, format: , tmp_type: float
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
422
|
+
value_type =
|
423
|
+
case c['t']
|
424
|
+
when 's'
|
425
|
+
:shared
|
426
|
+
when 'b'
|
427
|
+
:boolean
|
428
|
+
# 2011-02-25 BEGIN
|
429
|
+
when 'str'
|
430
|
+
:string
|
431
|
+
# 2011-02-25 END
|
432
|
+
# 2011-09-15 BEGIN
|
433
|
+
when 'inlineStr'
|
434
|
+
:inlinestr
|
435
|
+
# 2011-09-15 END
|
436
|
+
else
|
437
|
+
format = attribute2format(s_attribute)
|
438
|
+
format2type(format)
|
439
|
+
end
|
481
440
|
formula = nil
|
482
441
|
c.children.each do |cell|
|
483
|
-
|
484
|
-
|
442
|
+
case cell.name
|
443
|
+
when 'is'
|
485
444
|
cell.children.each do |is|
|
486
445
|
if is.name == 't'
|
487
446
|
inlinestr_content = is.content
|
488
|
-
|
489
|
-
|
447
|
+
value_type = :string
|
448
|
+
v = inlinestr_content
|
490
449
|
excelx_type = :string
|
491
|
-
y, x = GenericSpreadsheet.split_coordinate(c['r'])
|
492
|
-
v = nil
|
493
|
-
tr=nil #TODO: ???s
|
450
|
+
y, x = Roo::GenericSpreadsheet.split_coordinate(c['r'])
|
494
451
|
excelx_value = inlinestr_content #cell.content
|
495
|
-
set_cell_values(sheet,x,y,0,v,
|
452
|
+
set_cell_values(sheet,x,y,0,v,value_type,formula,excelx_type,excelx_value,s_attribute)
|
496
453
|
end
|
497
454
|
end
|
498
|
-
|
499
|
-
# 2011-09-15 END
|
500
|
-
if cell.name == 'f'
|
455
|
+
when 'f'
|
501
456
|
formula = cell.content
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
tmp_type = :datetime
|
457
|
+
when 'v'
|
458
|
+
if [:time, :datetime].include?(value_type) && cell.content.to_f >= 1.0
|
459
|
+
value_type =
|
460
|
+
if (cell.content.to_f - cell.content.to_f.floor).abs > 0.000001
|
461
|
+
:datetime
|
508
462
|
else
|
509
|
-
|
463
|
+
:date
|
510
464
|
end
|
511
|
-
else
|
512
|
-
end
|
513
465
|
end
|
514
466
|
excelx_type = [:numeric_or_formula,format.to_s]
|
515
467
|
excelx_value = cell.content
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
else
|
542
|
-
vt = :float
|
543
|
-
v = cell.content
|
544
|
-
end
|
545
|
-
y, x = GenericSpreadsheet.split_coordinate(c['r'])
|
546
|
-
tr=nil #TODO: ???s
|
547
|
-
set_cell_values(sheet,x,y,0,v,vt,formula,tr,str_v,excelx_type,excelx_value,s_attribute)
|
468
|
+
v =
|
469
|
+
case value_type
|
470
|
+
when :shared
|
471
|
+
value_type = :string
|
472
|
+
excelx_type = :string
|
473
|
+
@shared_table[cell.content.to_i]
|
474
|
+
when :boolean
|
475
|
+
(cell.content.to_i == 1 ? 'TRUE' : 'FALSE')
|
476
|
+
when :date
|
477
|
+
cell.content
|
478
|
+
when :time
|
479
|
+
cell.content
|
480
|
+
when :datetime
|
481
|
+
cell.content
|
482
|
+
when :formula
|
483
|
+
cell.content.to_f #TODO: !!!!
|
484
|
+
when :string
|
485
|
+
excelx_type = :string
|
486
|
+
cell.content
|
487
|
+
else
|
488
|
+
value_type = :float
|
489
|
+
cell.content
|
490
|
+
end
|
491
|
+
y, x = Roo::GenericSpreadsheet.split_coordinate(c['r'])
|
492
|
+
set_cell_values(sheet,x,y,0,v,value_type,formula,excelx_type,excelx_value,s_attribute)
|
548
493
|
end
|
549
494
|
end
|
550
495
|
end
|
551
|
-
sheet_found = true #TODO:
|
552
|
-
if !sheet_found
|
553
|
-
raise RangeError
|
554
|
-
end
|
555
496
|
@cells_read[sheet] = true
|
556
497
|
# begin comments
|
557
498
|
=begin
|
@@ -599,71 +540,37 @@ Datei xl/comments1.xml
|
|
599
540
|
|
600
541
|
# Reads all comments from a sheet
|
601
542
|
def read_comments(sheet=nil)
|
602
|
-
sheet
|
603
|
-
|
604
|
-
raise ArgumentError, "Error: sheet '#{sheet||'nil'}' not valid" if @default_sheet == nil and sheet==nil
|
605
|
-
raise RangeError unless self.sheets.include? sheet
|
543
|
+
sheet ||= @default_sheet
|
544
|
+
validate_sheet!(sheet)
|
606
545
|
n = self.sheets.index(sheet)
|
607
546
|
return unless @comments_doc[n] #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
608
|
-
@comments_doc[n].xpath("
|
609
|
-
comment.
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
row,col = GenericSpreadsheet.split_coordinate(ref)
|
615
|
-
commentlist.children.each do |clc|
|
616
|
-
if clc.name == 'text'
|
617
|
-
clc.children.each do |text|
|
618
|
-
if text.name == 'r'
|
619
|
-
text.children.each do |r|
|
620
|
-
if r.name == 't'
|
621
|
-
comment = r.text
|
622
|
-
@comment[sheet] = Hash.new unless @comment[sheet]
|
623
|
-
@comment[sheet][[row,col]] = comment
|
624
|
-
end
|
625
|
-
end
|
626
|
-
end
|
627
|
-
end
|
628
|
-
end
|
629
|
-
end
|
630
|
-
end
|
631
|
-
end
|
632
|
-
end
|
547
|
+
@comments_doc[n].xpath("//xmlns:comments/xmlns:commentList/xmlns:comment").each do |comment|
|
548
|
+
ref = comment.attributes['ref'].to_s
|
549
|
+
row,col = Roo::GenericSpreadsheet.split_coordinate(ref)
|
550
|
+
comment.xpath('./xmlns:text/xmlns:r/xmlns:t').each do |text|
|
551
|
+
@comment[sheet] ||= {}
|
552
|
+
@comment[sheet][[row,col]] = text.text
|
633
553
|
end
|
634
554
|
end
|
635
555
|
@comments_read[sheet] = true
|
636
556
|
end
|
637
557
|
|
638
558
|
def read_labels
|
639
|
-
@workbook_doc.xpath("
|
559
|
+
@label ||= Hash[@workbook_doc.xpath("//xmlns:definedName").map do |defined_name|
|
640
560
|
# "Sheet1!$C$5"
|
641
|
-
sheet = defined_name.text.split('
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
end
|
646
|
-
@labels_read = true
|
647
|
-
end
|
648
|
-
|
649
|
-
# Checks if the default_sheet exists. If not an RangeError exception is
|
650
|
-
# raised
|
651
|
-
def check_default_sheet
|
652
|
-
sheet_found = false
|
653
|
-
raise ArgumentError, "Error: default_sheet not set" if @default_sheet == nil
|
654
|
-
sheet_found = true if sheets.include?(@default_sheet)
|
655
|
-
if ! sheet_found
|
656
|
-
raise RangeError, "sheet '#{@default_sheet}' not found"
|
657
|
-
end
|
561
|
+
sheet, coordinates = defined_name.text.split('!$', 2)
|
562
|
+
col,row = coordinates.split('$')
|
563
|
+
[defined_name['name'], [sheet,row,col]]
|
564
|
+
end]
|
658
565
|
end
|
659
566
|
|
660
567
|
# Extracts all needed files from the zip file
|
661
|
-
def process_zipfile(zipfilename, zip, path='')
|
568
|
+
def process_zipfile(tmpdir, zipfilename, zip, path='')
|
662
569
|
@sheet_files = []
|
663
570
|
Zip::ZipFile.open(zipfilename) {|zf|
|
664
571
|
zf.entries.each {|entry|
|
665
572
|
if entry.to_s.end_with?('workbook.xml')
|
666
|
-
open(
|
573
|
+
open(tmpdir+'/'+'roo_workbook.xml','wb') {|f|
|
667
574
|
f << zip.read(entry)
|
668
575
|
}
|
669
576
|
end
|
@@ -673,28 +580,28 @@ Datei xl/comments1.xml
|
|
673
580
|
# won't be both names in the archive.
|
674
581
|
# Changed the casing of all the following filenames.
|
675
582
|
if entry.to_s.downcase.end_with?('sharedstrings.xml')
|
676
|
-
open(
|
583
|
+
open(tmpdir+'/'+'roo_sharedStrings.xml','wb') {|f|
|
677
584
|
f << zip.read(entry)
|
678
585
|
}
|
679
586
|
end
|
680
587
|
if entry.to_s.downcase.end_with?('styles.xml')
|
681
|
-
open(
|
588
|
+
open(tmpdir+'/'+'roo_styles.xml','wb') {|f|
|
682
589
|
f << zip.read(entry)
|
683
590
|
}
|
684
591
|
end
|
685
592
|
if entry.to_s.downcase =~ /sheet([0-9]+).xml$/
|
686
593
|
nr = $1
|
687
|
-
open(
|
594
|
+
open(tmpdir+'/'+"roo_sheet#{nr}",'wb') {|f|
|
688
595
|
f << zip.read(entry)
|
689
596
|
}
|
690
|
-
@sheet_files[nr.to_i-1] =
|
597
|
+
@sheet_files[nr.to_i-1] = tmpdir+'/'+"roo_sheet#{nr}"
|
691
598
|
end
|
692
599
|
if entry.to_s.downcase =~ /comments([0-9]+).xml$/
|
693
600
|
nr = $1
|
694
|
-
open(
|
601
|
+
open(tmpdir+'/'+"roo_comments#{nr}",'wb') {|f|
|
695
602
|
f << zip.read(entry)
|
696
603
|
}
|
697
|
-
@comments_files[nr.to_i-1] =
|
604
|
+
@comments_files[nr.to_i-1] = tmpdir+'/'+"roo_comments#{nr}"
|
698
605
|
end
|
699
606
|
}
|
700
607
|
}
|
@@ -702,27 +609,15 @@ Datei xl/comments1.xml
|
|
702
609
|
end
|
703
610
|
|
704
611
|
# extract files from the zip file
|
705
|
-
def extract_content(zipfilename)
|
612
|
+
def extract_content(tmpdir, zipfilename)
|
706
613
|
Zip::ZipFile.open(@filename) do |zip|
|
707
|
-
process_zipfile(zipfilename,zip)
|
614
|
+
process_zipfile(tmpdir, zipfilename,zip)
|
708
615
|
end
|
709
616
|
end
|
710
617
|
|
711
|
-
# sets the value of a cell
|
712
|
-
def set_value(row,col,value,sheet=nil)
|
713
|
-
sheet = @default_value unless sheet
|
714
|
-
@cell[sheet][[row,col]] = value
|
715
|
-
end
|
716
|
-
|
717
|
-
# sets the type of a cell
|
718
|
-
def set_type(row,col,type,sheet=nil)
|
719
|
-
sheet = @default_value unless sheet
|
720
|
-
@cell_type[sheet][[row,col]] = type
|
721
|
-
end
|
722
|
-
|
723
618
|
# read the shared strings xml document
|
724
619
|
def read_shared_strings(doc)
|
725
|
-
doc.xpath("
|
620
|
+
doc.xpath("/xmlns:sst/xmlns:si").each do |si|
|
726
621
|
shared_table_entry = ''
|
727
622
|
si.children.each do |elem|
|
728
623
|
if elem.name == 'r' and elem.children
|
@@ -742,62 +637,31 @@ Datei xl/comments1.xml
|
|
742
637
|
|
743
638
|
# read the styles elements of an excelx document
|
744
639
|
def read_styles(doc)
|
745
|
-
@numFmts = []
|
746
640
|
@cellXfs = []
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
if font_el == 'font'
|
757
|
-
font = Excelx::Font.new
|
758
|
-
font_el.each_element do |font_sub_el|
|
759
|
-
case font_sub_el.name
|
760
|
-
when 'b'
|
761
|
-
font.bold = true
|
762
|
-
when 'i'
|
763
|
-
font.italic = true
|
764
|
-
when 'u'
|
765
|
-
font.underline = true
|
766
|
-
end
|
767
|
-
end
|
768
|
-
fonts << font
|
769
|
-
end
|
641
|
+
|
642
|
+
@numFmts = Hash[doc.xpath("//xmlns:numFmt").map do |numFmt|
|
643
|
+
[numFmt['numFmtId'], numFmt['formatCode']]
|
644
|
+
end]
|
645
|
+
fonts = doc.xpath("//xmlns:fonts/xmlns:font").map do |font_el|
|
646
|
+
Font.new.tap do |font|
|
647
|
+
font.bold = !font_el.xpath('./xmlns:b').empty?
|
648
|
+
font.italic = !font_el.xpath('./xmlns:i').empty?
|
649
|
+
font.underline = !font_el.xpath('./xmlns:u').empty?
|
770
650
|
end
|
771
651
|
end
|
772
|
-
|
773
|
-
doc.xpath("
|
652
|
+
|
653
|
+
doc.xpath("//xmlns:cellXfs").each do |xfs|
|
774
654
|
xfs.children.each do |xf|
|
775
|
-
|
776
|
-
@
|
777
|
-
fontId = xf['fontId'].to_i
|
778
|
-
@style_definitions << fonts[fontId]
|
655
|
+
@cellXfs << xf['numFmtId']
|
656
|
+
@style_definitions << fonts[xf['fontId'].to_i]
|
779
657
|
end
|
780
658
|
end
|
781
659
|
end
|
782
660
|
|
783
661
|
# convert internal excelx attribute to a format
|
784
662
|
def attribute2format(s)
|
785
|
-
|
786
|
-
@numFmts.
|
787
|
-
# to_s weil das eine Nokogiri::XML::Attr und das
|
788
|
-
# andere ein String ist
|
789
|
-
if nf.first.to_s == @cellXfs[s.to_i].first
|
790
|
-
result = nf[1]
|
791
|
-
break
|
792
|
-
end
|
793
|
-
}
|
794
|
-
unless result
|
795
|
-
id = @cellXfs[s.to_i].first.to_i
|
796
|
-
if STANDARD_FORMATS.has_key? id
|
797
|
-
result = STANDARD_FORMATS[id]
|
798
|
-
end
|
799
|
-
end
|
800
|
-
result
|
663
|
+
id = @cellXfs[s.to_i]
|
664
|
+
@numFmts[id] || STANDARD_FORMATS[id.to_i]
|
801
665
|
end
|
802
666
|
|
803
667
|
end # class
|