oxcelix 0.3.3 → 0.4.0
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 +7 -0
- data/.yardopts +3 -2
- data/CHANGES +10 -0
- data/LICENSE +20 -20
- data/README.md +79 -70
- data/README.rdoc +70 -62
- data/lib/oxcelix.rb +48 -47
- data/lib/oxcelix/cell.rb +22 -22
- data/lib/oxcelix/cellhelper.rb +48 -48
- data/lib/oxcelix/nf.rb +172 -172
- data/lib/oxcelix/numformats.rb +115 -114
- data/lib/oxcelix/sax/comments.rb +28 -28
- data/lib/oxcelix/sax/sharedstrings.rb +17 -17
- data/lib/oxcelix/sax/styles.rb +48 -48
- data/lib/oxcelix/sax/xlsheet.rb +136 -78
- data/lib/oxcelix/sheet.rb +96 -75
- data/lib/oxcelix/workbook.rb +393 -256
- data/oxcelix.gemspec +26 -26
- data/spec/cell_spec.rb +14 -13
- data/spec/fixnum_spec.rb +12 -11
- data/spec/helper.rb +10 -0
- data/spec/matrix_spec.rb +12 -11
- data/spec/oxcelix_spec.rb +37 -36
- data/spec/spec_helper.rb +3 -3
- data/spec/string_spec.rb +19 -19
- metadata +21 -25
data/lib/oxcelix/sheet.rb
CHANGED
@@ -1,75 +1,96 @@
|
|
1
|
-
|
2
|
-
module Oxcelix
|
3
|
-
# The Sheet class represents an excel sheet.
|
4
|
-
class Sheet < Matrix
|
5
|
-
include Cellhelper
|
6
|
-
include Numberhelper
|
7
|
-
# @!attribute [rw] name
|
8
|
-
# @return [String] Sheet name
|
9
|
-
# @!attribute [rw] sheetId
|
10
|
-
# @return [String] returns the sheetId SheetML internal attribute
|
11
|
-
# @!attribute [rw] relationId
|
12
|
-
# @return [String] Internal reference key. relationID is used internally by Excel 2010 to e.g. build up the relationship between worksheets and comments
|
13
|
-
attr_accessor :name, :sheetId, :relationId
|
14
|
-
|
15
|
-
# The [] method overrides the standard Matrix::[]. It will now accept Excel-style cell coordinates.
|
16
|
-
# @param [String] i
|
17
|
-
# @return [Cell] the object denoted with the Excel column-row name.
|
18
|
-
# @example Select a cell in a sheet
|
19
|
-
# w = Oxcelix::Workbook.new('Example.xlsx')
|
20
|
-
# w.sheets[0][3,1] #=> #<Oxcelix::Cell:0x00000001e00fa0 @xlcoords="B4", @style="0", @type="n", @value="3">
|
21
|
-
# w.sheets[0]['B4'] #=> #<Oxcelix::Cell:0x00000001e00fa0 @xlcoords="B4", @style="0", @type="n", @value="3">
|
22
|
-
def [](i, *j)
|
23
|
-
if i.is_a? String
|
24
|
-
super(y(i),x(i))
|
25
|
-
else
|
26
|
-
super(i,j[0])
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
#The to_m method returns a simple Matrix object filled with the raw values of the original Sheet object.
|
31
|
-
# @return [Matrix] a collection of string values (the former #Cell::value)
|
32
|
-
def to_m(*attrs)
|
33
|
-
m=Matrix.build(self.row_size, self.column_size){nil}
|
34
|
-
self.each_with_index do |x, row, col|
|
35
|
-
if attrs.size == 0 || attrs.nil?
|
36
|
-
m[row, col] = x.value
|
37
|
-
end
|
38
|
-
end
|
39
|
-
return m
|
40
|
-
end
|
41
|
-
|
42
|
-
# The to_ru method returns a Matrix of "rubified" values. It basically builds a new Matrix
|
43
|
-
# and puts the result of the #Cell::to_ru method of every cell of the original sheet in
|
44
|
-
# the corresponding Matrix cell.
|
45
|
-
# @return [Matrix] a collection of ruby objects (#Integers, #Floats, #DateTimes, #Rationals, #Strings)
|
46
|
-
def to_ru
|
47
|
-
m=Matrix.build(self.row_size, self.column_size){nil}
|
48
|
-
self.each_with_index do |x, row, col|
|
49
|
-
if x.nil? || x.value.nil?
|
50
|
-
m[row, col] = nil
|
51
|
-
else
|
52
|
-
m[row, col] = x.to_ru
|
53
|
-
end
|
54
|
-
end
|
55
|
-
return m
|
56
|
-
end
|
57
|
-
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
1
|
+
|
2
|
+
module Oxcelix
|
3
|
+
# The Sheet class represents an excel sheet.
|
4
|
+
class Sheet < Matrix
|
5
|
+
include Cellhelper
|
6
|
+
include Numberhelper
|
7
|
+
# @!attribute [rw] name
|
8
|
+
# @return [String] Sheet name
|
9
|
+
# @!attribute [rw] sheetId
|
10
|
+
# @return [String] returns the sheetId SheetML internal attribute
|
11
|
+
# @!attribute [rw] relationId
|
12
|
+
# @return [String] Internal reference key. relationID is used internally by Excel 2010 to e.g. build up the relationship between worksheets and comments
|
13
|
+
attr_accessor :name, :sheetId, :relationId
|
14
|
+
|
15
|
+
# The [] method overrides the standard Matrix::[]. It will now accept Excel-style cell coordinates.
|
16
|
+
# @param [String] i
|
17
|
+
# @return [Cell] the object denoted with the Excel column-row name.
|
18
|
+
# @example Select a cell in a sheet
|
19
|
+
# w = Oxcelix::Workbook.new('Example.xlsx')
|
20
|
+
# w.sheets[0][3,1] #=> #<Oxcelix::Cell:0x00000001e00fa0 @xlcoords="B4", @style="0", @type="n", @value="3">
|
21
|
+
# w.sheets[0]['B4'] #=> #<Oxcelix::Cell:0x00000001e00fa0 @xlcoords="B4", @style="0", @type="n", @value="3">
|
22
|
+
def [](i, *j)
|
23
|
+
if i.is_a? String
|
24
|
+
super(y(i),x(i))
|
25
|
+
else
|
26
|
+
super(i,j[0])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
#The to_m method returns a simple Matrix object filled with the raw values of the original Sheet object.
|
31
|
+
# @return [Matrix] a collection of string values (the former #Cell::value)
|
32
|
+
def to_m(*attrs)
|
33
|
+
m=Matrix.build(self.row_size, self.column_size){nil}
|
34
|
+
self.each_with_index do |x, row, col|
|
35
|
+
if attrs.size == 0 || attrs.nil?
|
36
|
+
m[row, col] = x.value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
return m
|
40
|
+
end
|
41
|
+
|
42
|
+
# The to_ru method returns a Matrix of "rubified" values. It basically builds a new Matrix
|
43
|
+
# and puts the result of the #Cell::to_ru method of every cell of the original sheet in
|
44
|
+
# the corresponding Matrix cell.
|
45
|
+
# @return [Matrix] a collection of ruby objects (#Integers, #Floats, #DateTimes, #Rationals, #Strings)
|
46
|
+
def to_ru
|
47
|
+
m=Matrix.build(self.row_size, self.column_size){nil}
|
48
|
+
self.each_with_index do |x, row, col|
|
49
|
+
if x.nil? || x.value.nil?
|
50
|
+
m[row, col] = nil
|
51
|
+
else
|
52
|
+
m[row, col] = x.to_ru
|
53
|
+
end
|
54
|
+
end
|
55
|
+
return m
|
56
|
+
end
|
57
|
+
|
58
|
+
# Invokes the #Cell::to_ru method on each element of self, replacing each element of the Sheet with the value returned.
|
59
|
+
def to_ru!
|
60
|
+
self.each_with_index do |x, row, col|
|
61
|
+
if x.nil? || x.value.nil?
|
62
|
+
self[row, col] = nil
|
63
|
+
else
|
64
|
+
self[row, col] = x.to_ru
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# The to_fmt method returns a Matrix of "formatted" values. It basically builds a new Matrix
|
70
|
+
# and puts the result of the #Cell::to_fmt method of every cell of the original sheet in
|
71
|
+
# the corresponding Matrix cell. The #Cell::to_fmt will pass the original values to to_ru, and then
|
72
|
+
# depending on the value, will run strftime on DateTime objects and sprintf on numeric types.
|
73
|
+
# @return [Matrix] a collection of Strings
|
74
|
+
def to_fmt
|
75
|
+
m=Matrix.build(self.row_size, self.column_size){nil}
|
76
|
+
self.each_with_index do |x, row, col|
|
77
|
+
if x.nil? || x.value.nil?
|
78
|
+
m[row, col] = nil
|
79
|
+
else
|
80
|
+
m[row, col] = x.to_fmt
|
81
|
+
end
|
82
|
+
end
|
83
|
+
return m
|
84
|
+
end
|
85
|
+
# Invokes the #Cell::to_fmt method on each element of self, replacing each element of the Sheet with the value returned.
|
86
|
+
def to_fmt!
|
87
|
+
self.each_with_index do |x, row, col|
|
88
|
+
if x.nil? || x.value.nil?
|
89
|
+
self[row, col] = nil
|
90
|
+
else
|
91
|
+
self[row, col] = x.to_fmt
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/oxcelix/workbook.rb
CHANGED
@@ -1,256 +1,393 @@
|
|
1
|
-
# The namespace for all classes and modules included on Oxcelix.
|
2
|
-
module Oxcelix
|
3
|
-
# Helper methods for the Workbook class
|
4
|
-
module Workbookhelper
|
5
|
-
# returns a sheet based on its name
|
6
|
-
|
7
|
-
# @example Select a sheet
|
8
|
-
# w = Workbook.new('Example.xlsx')
|
9
|
-
# sheet = w["Examplesheet"]
|
10
|
-
def [] (sheetname=String)
|
11
|
-
@sheets.select{|s| s.name==sheetname}[0]
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
#
|
16
|
-
# Matrix objects
|
17
|
-
# @!attribute [rw] sheets
|
18
|
-
# @return [Array] a collection of {Sheet} objects
|
19
|
-
class Workbook
|
20
|
-
include Cellhelper
|
21
|
-
include Workbookhelper
|
22
|
-
include Numformats
|
23
|
-
|
24
|
-
attr_accessor :sheets
|
25
|
-
|
26
|
-
##
|
27
|
-
# Create a new {Workbook} object.
|
28
|
-
#
|
29
|
-
# filename is the name of the Excel 2007/2010 file to be opened (
|
30
|
-
#
|
31
|
-
# options is a collection of options that can be passed to Workbook.
|
32
|
-
# Options may include:
|
33
|
-
# * :copymerge (=> true/false) - Copy and repeat the content of the merged cells into the whole group, e.g.
|
34
|
-
# the group of three merged cells <tt>| a |</tt>
|
35
|
-
#
|
36
|
-
# * :
|
37
|
-
# * :
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
# *
|
49
|
-
# *
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
if
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
end
|
177
|
-
|
178
|
-
#
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
#
|
217
|
-
|
218
|
-
|
219
|
-
@
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
1
|
+
# The namespace for all classes and modules included on Oxcelix.
|
2
|
+
module Oxcelix
|
3
|
+
# Helper methods for the Workbook class
|
4
|
+
module Workbookhelper
|
5
|
+
# returns a sheet based on its name
|
6
|
+
|
7
|
+
# @example Select a sheet
|
8
|
+
# w = Workbook.new('Example.xlsx')
|
9
|
+
# sheet = w["Examplesheet"]
|
10
|
+
def [] (sheetname=String)
|
11
|
+
@sheets.select{|s| s.name==sheetname}[0]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# A class that represents an Excel workbook. By default, it will open the excel file, and convert it to a collection of
|
16
|
+
# Matrix objects
|
17
|
+
# @!attribute [rw] sheets
|
18
|
+
# @return [Array] a collection of {Sheet} objects
|
19
|
+
class Workbook
|
20
|
+
include Cellhelper
|
21
|
+
include Workbookhelper
|
22
|
+
include Numformats
|
23
|
+
|
24
|
+
attr_accessor :sheets
|
25
|
+
|
26
|
+
##
|
27
|
+
# Create a new {Workbook} object.
|
28
|
+
#
|
29
|
+
# filename is the name of the Excel 2007/2010 file (xlsx) to be opened (Optional)
|
30
|
+
#
|
31
|
+
# options is a collection of options that can be passed to Workbook.
|
32
|
+
# Options may include:
|
33
|
+
# * :copymerge (=> true/false) - Copy and repeat the content of the merged cells into the whole group, e.g.
|
34
|
+
# the group of three merged cells <tt>| a |</tt>
|
35
|
+
# will become: <tt>|a|a|a|</tt>
|
36
|
+
# * :include (Array) - an array of sheet names to be included
|
37
|
+
# * :exclude (Array) - an array of sheet names not to be processed
|
38
|
+
# * :paginate (Array) - an array that defines the number of lines to be included in the pagination and the page to be parsed
|
39
|
+
# * :cellrange (Range) - the range of cells to be included in parsing
|
40
|
+
#
|
41
|
+
# If a filename gets passed, the excel file is first getting unzipped, then
|
42
|
+
# the workbook.xml file gets processed.
|
43
|
+
# This file stores sheet metadata, which will be filtered (by including
|
44
|
+
# and excluding sheets from further processing)
|
45
|
+
#
|
46
|
+
# The next stage is building sheets.
|
47
|
+
# This includes:
|
48
|
+
# * Parsing the XML files representing the sheets
|
49
|
+
# * Interpolation of the shared strings
|
50
|
+
# * adding comments to the cells
|
51
|
+
# * Converting each sheet to a Matrix object
|
52
|
+
# * Deleting the temporary directory that stores the XML files.
|
53
|
+
def initialize(filename=nil, options={})
|
54
|
+
@sheets=[]
|
55
|
+
@sheetbase={}
|
56
|
+
@sharedstrings=[]
|
57
|
+
unless filename.nil?
|
58
|
+
unpack filename
|
59
|
+
open options
|
60
|
+
parse options
|
61
|
+
FileUtils.remove_dir(@destination, true)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
at_exit do
|
66
|
+
FileUtils.remove_dir(@destination, true)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Unzips the excel file to a temporary directory. The directory will be removed at the end of the parsing stage when invoked
|
70
|
+
# by initialize, otherwise at exit.
|
71
|
+
# @param [String] filename the name of the Excel file to be unpacked
|
72
|
+
def unpack(filename)
|
73
|
+
@destination = Dir.mktmpdir
|
74
|
+
Zip::File.open(filename){ |zip_file|
|
75
|
+
zip_file.each{ |f|
|
76
|
+
f_path=File.join(@destination, f.name)
|
77
|
+
FileUtils.mkdir_p(File.dirname(f_path))
|
78
|
+
zip_file.extract(f, f_path) unless File.exists?(f_path)
|
79
|
+
}
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
# Parses workbook metadata (sheet data, comments, shared strings)
|
84
|
+
# @param [Hash] options Options affecting file opening, metadata collection and processing.
|
85
|
+
def open(options={})
|
86
|
+
f=IO.read(@destination + '/xl/workbook.xml')
|
87
|
+
a=Ox::load(f)
|
88
|
+
|
89
|
+
sheetdata(a, options); commentsrel; shstrings;
|
90
|
+
|
91
|
+
@styles = Styles.new()
|
92
|
+
File.open(@destination + '/xl/styles.xml', 'r') do |f|
|
93
|
+
Ox.sax_parse(@styles, f)
|
94
|
+
end
|
95
|
+
|
96
|
+
@styles.temparray.sort_by!{|st| st[:numFmtId].to_i}
|
97
|
+
add_custom_formats @styles.temparray
|
98
|
+
@styles.styleary.map!{|s| Numformats::Formatarray[s.to_i][:id].to_i}
|
99
|
+
end
|
100
|
+
|
101
|
+
# Parses sheet data by feeding the output of the Xlsheet SAX parser into the arrays representing the sheets.
|
102
|
+
# @param [Hash] options Options that affect the parser.
|
103
|
+
def parse(options={})
|
104
|
+
@sheets.each do |x|
|
105
|
+
if !options[:paginate].nil?
|
106
|
+
lines = options[:paginate][0]; page = options[:paginate][1]
|
107
|
+
sheet = PagSheet.new(lines, page)
|
108
|
+
elsif !options[:cellrange].nil?
|
109
|
+
range = options[:cellrange]
|
110
|
+
sheet = Cellrange.new(range)
|
111
|
+
else
|
112
|
+
sheet = Xlsheet.new()
|
113
|
+
end
|
114
|
+
|
115
|
+
File.open(@destination+"/xl/#{x[:filename]}", 'r') do |f|
|
116
|
+
Ox.sax_parse(sheet, f)
|
117
|
+
end
|
118
|
+
comments = mkcomments(x[:comments])
|
119
|
+
sheet.cellarray.each do |sh|
|
120
|
+
sh.numformat = @styles.styleary[sh.style.to_i]
|
121
|
+
if sh.type=="s"
|
122
|
+
sh.value = @sharedstrings[sh.value.to_i]
|
123
|
+
end
|
124
|
+
if !comments.nil?
|
125
|
+
comm=comments.select {|c| c[:ref]==(sh.xlcoords)}
|
126
|
+
if comm.size > 0
|
127
|
+
sh.comment=comm[0][:comment]
|
128
|
+
end
|
129
|
+
comments.delete_if{|c| c[:ref]==(sh.xlcoords)}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
x[:cells] = sheet.cellarray
|
133
|
+
x[:mergedcells] = sheet.mergedcells
|
134
|
+
end
|
135
|
+
matrixto options
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
# @private
|
140
|
+
# Given the data found in workbook.xml, create a hash and push it to the sheets
|
141
|
+
# array.
|
142
|
+
#
|
143
|
+
# The hash will not be pushed into the array if the sheet name is blacklisted
|
144
|
+
# (it appears in the *excluded_sheets* array) or does not appear in the list of
|
145
|
+
# included sheets.
|
146
|
+
#
|
147
|
+
# If *included_sheets* (the array of whitelisted sheets) is *nil*, the hash is added.
|
148
|
+
def sheetdata(wb_file, options)
|
149
|
+
wb_file.locate("workbook/sheets/*").each do |x|
|
150
|
+
@sheetbase[:name] = x[:name]
|
151
|
+
@sheetbase[:sheetId] = x[:sheetId]
|
152
|
+
@sheetbase[:relationId] = x[:"r:id"]
|
153
|
+
|
154
|
+
relationshipfile=nil
|
155
|
+
fname=nil
|
156
|
+
unless Dir[@destination + '/xl/_rels'].empty?
|
157
|
+
Find.find(@destination + '/xl/_rels') do |path|
|
158
|
+
if File.basename(path).split(".").last=='rels'
|
159
|
+
g=IO.read(path)
|
160
|
+
relationshipfile=Ox::load(g)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
relationshipfile.locate("Relationships/*").each do |rship|
|
165
|
+
if rship[:Id] == x[:"r:id"]
|
166
|
+
@sheetbase[:filename]=rship[:Target]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
@sheets << @sheetbase
|
172
|
+
@sheetbase=Hash.new
|
173
|
+
end
|
174
|
+
sheetarr=@sheets.map{|i| i[:name]}
|
175
|
+
sheet_collection(sheetarr, options)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Build the array of working sheets based on the :include and :exclude parameters.
|
179
|
+
# @param[sheetarr, options]
|
180
|
+
def sheet_collection(sheetarr, options)
|
181
|
+
if options[:include].nil?; options[:include]=[]; end
|
182
|
+
if options[:include].to_a.size>0
|
183
|
+
sheetarr.keep_if{|item| options[:include].to_a.detect{|d| d==item}}
|
184
|
+
end
|
185
|
+
sheetarr=sheetarr-options[:exclude].to_a
|
186
|
+
@sheets.keep_if{|item| sheetarr.detect{|d| d==item[:name]}}
|
187
|
+
@sheets.uniq!
|
188
|
+
end
|
189
|
+
|
190
|
+
# Build the relationship between sheets and the XML files storing the comments
|
191
|
+
# to the actual sheet.
|
192
|
+
def commentsrel
|
193
|
+
unless Dir[@destination + '/xl/worksheets/_rels'].empty?
|
194
|
+
Find.find(@destination + '/xl/worksheets/_rels') do |path|
|
195
|
+
if File.basename(path).split(".").last=='rels'
|
196
|
+
a=IO.read(path)
|
197
|
+
f=Ox::load(a)
|
198
|
+
f.locate("Relationships/*").each do |x|
|
199
|
+
if x[:Target].include?"comments"
|
200
|
+
@sheets.each do |s|
|
201
|
+
if "worksheets/" + File.basename(path,".rels")==s[:filename]
|
202
|
+
s[:comments]=x[:Target]
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
else
|
210
|
+
@sheets.each do |s|
|
211
|
+
s[:comments]=nil
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Invokes the Sharedstrings helper class
|
217
|
+
def shstrings
|
218
|
+
strings = Sharedstrings.new()
|
219
|
+
File.open(@destination + '/xl/sharedStrings.xml', 'r') do |f|
|
220
|
+
Ox.sax_parse(strings, f)
|
221
|
+
end
|
222
|
+
@sharedstrings=strings.stringarray
|
223
|
+
end
|
224
|
+
|
225
|
+
# Parses the comments related to the actual sheet.
|
226
|
+
# @param [String] commentfile
|
227
|
+
# @return [Array] a collection of comments relative to the Excel sheet currently processed
|
228
|
+
def mkcomments(commentfile)
|
229
|
+
unless commentfile.nil?
|
230
|
+
comms = Comments.new()
|
231
|
+
File.open(@destination + '/xl/'+commentfile.gsub('../', ''), 'r') do |f|
|
232
|
+
Ox.sax_parse(comms, f)
|
233
|
+
end
|
234
|
+
return comms.commarray
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Returns an array of Matrix objects.
|
239
|
+
# For each sheet, matrixto first checks the address (xlcoords) of the
|
240
|
+
# last cell in the cellarray, then builds a *nil*-filled Matrix object of
|
241
|
+
# size *xlcoords.x, xlcoords.y*.
|
242
|
+
#
|
243
|
+
# The matrix will then be filled with Cell objects according to their coordinates.
|
244
|
+
#
|
245
|
+
# If the *copymerge* parameter is *true*, it creates a submatrix (minor)
|
246
|
+
# of every mergegroup (based on the mergedcells array relative to the actual
|
247
|
+
# sheet), and after the only meaningful cell of the minor is found, it is
|
248
|
+
# copied back to the remaining cells of the group. The coordinates (xlcoords)
|
249
|
+
# of each copied cell is changed to reflect the actual Excel coordinate.
|
250
|
+
#
|
251
|
+
# The matrix will replace the array of cells in the actual sheet.
|
252
|
+
# @param [Hash] options
|
253
|
+
# @return [Matrix] a Matrix object that stores the cell values, and, depending on the copymerge parameter, will copy the merged value
|
254
|
+
# into every merged cell
|
255
|
+
def matrixto(options)
|
256
|
+
@sheets.each_with_index do |sheet, i|
|
257
|
+
if sheet[:cells].empty?
|
258
|
+
m=Sheet.build(0,0)
|
259
|
+
else
|
260
|
+
m=buildsheet(sheet, options)
|
261
|
+
if options[:copymerge]==true
|
262
|
+
sheet[:mergedcells].each do |mc|
|
263
|
+
a = mc.split(':')
|
264
|
+
x1=x(a[0])
|
265
|
+
y1=y(a[0])
|
266
|
+
x2=x(a[1])
|
267
|
+
y2=y(a[1])
|
268
|
+
mrange=m.minor(y1..y2, x1..x2)
|
269
|
+
valuecell=mrange.to_a.flatten.compact[0]
|
270
|
+
(x1..x2).each do |col|
|
271
|
+
(y1..y2).each do |row|
|
272
|
+
m, valuecell = mergevalues(m, col, row, valuecell)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
m.name=@sheets[i][:name]; m.sheetId=@sheets[i][:sheetId]; m.relationId=@sheets[i][:relationId]
|
278
|
+
@sheets[i]=m
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# buildsheet creates a matrix of the needed size and fills it with the cells. Mainly for internal use only.
|
284
|
+
# When paginating or parsing only a range of cells, the size of the matrix will be adjusted (no nil values
|
285
|
+
# will be left at the beginning of the sheet), to preserve memory.
|
286
|
+
# @param [Sheet] sheet the actual sheetarray.
|
287
|
+
# @param [Hash] options :paginate or :cellrange will affect the size of the matrix
|
288
|
+
# @return [Sheet] a Sheet object that stores the cell values.
|
289
|
+
def buildsheet(sheet, options)
|
290
|
+
ydiff, xdiff = 0,0
|
291
|
+
if !options[:paginate].nil?
|
292
|
+
ydiff = options[:paginate][0] * (options[:paginate][1]-1)
|
293
|
+
elsif !options[:cellrange].nil?
|
294
|
+
xdiff = x(options[:cellrange].begin)
|
295
|
+
ydiff = y(options[:cellrange].begin)
|
296
|
+
end
|
297
|
+
|
298
|
+
m=Sheet.build(sheet[:cells].last.y+1-ydiff, sheet[:cells].last.x+1-xdiff) {nil}
|
299
|
+
sheet[:cells].each do |c|
|
300
|
+
m[c.y-ydiff, c.x-xdiff] = c
|
301
|
+
end
|
302
|
+
return m
|
303
|
+
end
|
304
|
+
|
305
|
+
# Replace the empty values of the mergegroup with cell values or nil.
|
306
|
+
# @param [Matrix] m the Sheet object
|
307
|
+
# @param [Integer] col Column of the actual cell
|
308
|
+
# @param [Integer] row Row of the actual cell
|
309
|
+
# @param [Cell] valuecell A Cell containing the value to be copied over the mergegroup
|
310
|
+
# @return [Matrix, Cell] the sheet and the new (empty) cell or nil.
|
311
|
+
def mergevalues(m, col, row, valuecell)
|
312
|
+
if valuecell != nil
|
313
|
+
valuecell.xlcoords=(col.col_name)+(row+1).to_s
|
314
|
+
m[row, col]=valuecell
|
315
|
+
return m, valuecell
|
316
|
+
else
|
317
|
+
valuecell=Cell.new
|
318
|
+
valuecell.xlcoords=(col.col_name)+(row+1).to_s
|
319
|
+
m[row, col]=valuecell
|
320
|
+
return m, valuecell
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# RawWorkbook is a Workbook that contains the raw values of the original Excel cells instead of Cell objects.
|
326
|
+
# The values are taken from the Sheet arrays by running the #Cell::value method.
|
327
|
+
class RawWorkbook < Workbook
|
328
|
+
private
|
329
|
+
|
330
|
+
# {include:Workbook}
|
331
|
+
def buildsheet(sheet, options)
|
332
|
+
ydiff, xdiff = 0,0
|
333
|
+
if !options[:paginate].nil?
|
334
|
+
ydiff = options[:paginate][0] * (options[:paginate][1]-1)
|
335
|
+
elsif !options[:cellrange].nil?
|
336
|
+
xdiff = x(options[:cellrange].begin)
|
337
|
+
ydiff = y(options[:cellrange].begin)
|
338
|
+
end
|
339
|
+
m=Sheet.build(sheet[:cells].last.y+1-ydiff, sheet[:cells].last.x+1-xdiff) {nil}
|
340
|
+
sheet[:cells].each do |c|
|
341
|
+
m[c.y-ydiff, c.x-xdiff] = c.value
|
342
|
+
end
|
343
|
+
return m
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# RuValueWorkbook is a Workbook that contains the "rubyfied" values of the original Excel cells instead of Cell objects
|
348
|
+
# (e.g. DateTime objects).
|
349
|
+
# The values are taken from the Sheet arrays by running the #Cell::to_ru method. The result will be exactly the same as if
|
350
|
+
# you ran the #Sheet::to_ru method, but it will be snappier as the merged cellgroups will not need to be processed.
|
351
|
+
class RuValueWorkbook < Workbook
|
352
|
+
private
|
353
|
+
|
354
|
+
# {include:Workbook}
|
355
|
+
def buildsheet(sheet, options)
|
356
|
+
ydiff, xdiff = 0,0
|
357
|
+
if !options[:paginate].nil?
|
358
|
+
ydiff = options[:paginate][0] * (options[:paginate][1]-1)
|
359
|
+
elsif !options[:cellrange].nil?
|
360
|
+
xdiff = x(options[:cellrange].begin)
|
361
|
+
ydiff = y(options[:cellrange].begin)
|
362
|
+
end
|
363
|
+
m=Sheet.build(sheet[:cells].last.y+1-ydiff, sheet[:cells].last.x+1-xdiff) {nil}
|
364
|
+
sheet[:cells].each do |c|
|
365
|
+
m[c.y-ydiff, c.x-xdiff] = c.to_ru
|
366
|
+
end
|
367
|
+
return m
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
# FormattedWorkbook is a Workbook that contains the formatted values (strings) of the original Excel cells instead of Cell objects.
|
372
|
+
# The values are taken from the Sheet arrays by running the #Cell::to_fmt method. The result will be exactly the same as if
|
373
|
+
# you ran the #Sheet::to_fmt method, but it will be snappier as the merged cellgroups will not need to be processed.
|
374
|
+
class FormattedWorkbook < Workbook
|
375
|
+
private
|
376
|
+
|
377
|
+
# {include:Workbook}
|
378
|
+
def buildsheet(sheet, options)
|
379
|
+
ydiff, xdiff = 0,0
|
380
|
+
if !options[:paginate].nil?
|
381
|
+
ydiff = options[:paginate][0] * (options[:paginate][1]-1)
|
382
|
+
elsif !options[:cellrange].nil?
|
383
|
+
xdiff = x(options[:cellrange].begin)
|
384
|
+
ydiff = y(options[:cellrange].begin)
|
385
|
+
end
|
386
|
+
m=Sheet.build(sheet[:cells].last.y+1-ydiff, sheet[:cells].last.x+1-xdiff) {nil}
|
387
|
+
sheet[:cells].each do |c|
|
388
|
+
m[c.y-ydiff, c.x-xdiff] = c.to_fmt
|
389
|
+
end
|
390
|
+
return m
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|