oxcelix 0.1.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.
- data/LICENSE +20 -0
- data/README.md +35 -0
- data/README.rdoc +30 -0
- data/lib/oxcelix/cell.rb +17 -0
- data/lib/oxcelix/cellhelper.rb +45 -0
- data/lib/oxcelix/sax/comments.rb +24 -0
- data/lib/oxcelix/sax/sharedstrings.rb +15 -0
- data/lib/oxcelix/sax/xlsheet.rb +77 -0
- data/lib/oxcelix/sheet.rb +17 -0
- data/lib/oxcelix/workbook.rb +219 -0
- data/lib/oxcelix.rb +44 -0
- data/oxcelix.gemspec +21 -0
- data/spec/cell_spec.rb +13 -0
- data/spec/fixnum_spec.rb +11 -0
- data/spec/matrix_spec.rb +11 -0
- data/spec/oxcelix_spec.rb +36 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/string_spec.rb +19 -0
- data/spec/test.xlsx +0 -0
- metadata +97 -0
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2013 gbiczo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Oxcelix
|
|
2
|
+
=======
|
|
3
|
+
|
|
4
|
+
Oxcelix - A fast and simple .xlsx file parser
|
|
5
|
+
|
|
6
|
+
Description
|
|
7
|
+
-----------
|
|
8
|
+
|
|
9
|
+
Oxcelix is an xlsx (Excel 2007/2010) parser. The result of the parsing is a
|
|
10
|
+
Workbook which is an array of Sheet objects, which in turn store the data in
|
|
11
|
+
Matrix objects. Matrices consist of Cell objects to maintain comments and
|
|
12
|
+
formatting/style data
|
|
13
|
+
|
|
14
|
+
Oxcelix uses the great Ox gem (http://rubygems.org/gems/ox) for fast SAX-parsing.
|
|
15
|
+
|
|
16
|
+
Synopsis
|
|
17
|
+
--------
|
|
18
|
+
|
|
19
|
+
To process an xlsx file:
|
|
20
|
+
|
|
21
|
+
`require 'oxcelix'`
|
|
22
|
+
|
|
23
|
+
`w = Oxcelix::Workbook.new('whatever.xlsx')`
|
|
24
|
+
|
|
25
|
+
To omit certain sheets:
|
|
26
|
+
|
|
27
|
+
`w = Oxcelix::Workbook.new('whatever.xlsx', :exclude => ['sheet1', 'sheet2'])`
|
|
28
|
+
|
|
29
|
+
To include only some of the sheets:
|
|
30
|
+
|
|
31
|
+
`w = Oxcelix::Workbook.new('whatever.xlsx', :include => ['sheet1', 'sheet2', 'sheet3'])`
|
|
32
|
+
|
|
33
|
+
To have the values of the merged cells copied over the mergegroup:
|
|
34
|
+
|
|
35
|
+
`w = Oxcelix::Workbook.new('whatever.xlsx', :mergecells => true)`
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
= \Oxcelix - A fast .xlsx file parser
|
|
2
|
+
|
|
3
|
+
== Description
|
|
4
|
+
|
|
5
|
+
Oxcelix is an xlsx (Excel 2007/2010) parser. The result of the parsing is a
|
|
6
|
+
Workbook which is an array of Sheet objects, which in turn store the data in
|
|
7
|
+
Matrix objects. Matrices consist of Cell objects to maintain comments and
|
|
8
|
+
formatting/style data
|
|
9
|
+
.
|
|
10
|
+
Oxcelix uses the great Ox gem (http://rubygems.org/gems/ox) for fast SAX-parsing.
|
|
11
|
+
|
|
12
|
+
== Synopsis
|
|
13
|
+
|
|
14
|
+
To process an xlsx file:
|
|
15
|
+
|
|
16
|
+
require 'oxcelix'
|
|
17
|
+
|
|
18
|
+
w = Oxcelix::Workbook.new('whatever.xlsx')
|
|
19
|
+
|
|
20
|
+
To omit certain sheets to be processed:
|
|
21
|
+
|
|
22
|
+
w = Oxcelix::Workbook.new('whatever.xlsx', :exclude => ['sheet1', 'sheet2'])
|
|
23
|
+
|
|
24
|
+
To include only some of the sheets:
|
|
25
|
+
|
|
26
|
+
w = Oxcelix::Workbook.new('whatever.xlsx', :include => ['sheet1', 'sheet2', 'sheet3'])
|
|
27
|
+
|
|
28
|
+
To have the values of the merged cells copied over the mergegroup:
|
|
29
|
+
|
|
30
|
+
w = Oxcelix::Workbook.new('whatever.xlsx', :mergecells => true)
|
data/lib/oxcelix/cell.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Oxcelix
|
|
2
|
+
# Simple class representing an excel cell.
|
|
3
|
+
# @!attribute [rw] xlcoords
|
|
4
|
+
# @return [String] The Excel-style coordinates of the cell object
|
|
5
|
+
# @!attribute [rw] type
|
|
6
|
+
# @return [String] Cell content type
|
|
7
|
+
# @!attribute [rw] value
|
|
8
|
+
# @return [String] the type of the cell
|
|
9
|
+
# @!attribute [rw] comment
|
|
10
|
+
# @return [String] Comment text
|
|
11
|
+
# @!attribute [rw] style
|
|
12
|
+
# @return [String] Excel style attribute
|
|
13
|
+
class Cell
|
|
14
|
+
attr_accessor :xlcoords, :type, :value, :comment, :style
|
|
15
|
+
include Cellhelper
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
|
|
2
|
+
module Oxcelix
|
|
3
|
+
# The Cellhelper module defines some methods useful to manipulate Cell objects
|
|
4
|
+
module Cellhelper
|
|
5
|
+
# Set the excel cell name (eg: 'A2')
|
|
6
|
+
# @param [String] val Excel cell address name
|
|
7
|
+
def r(val); @xlcoords = val; end;
|
|
8
|
+
# Set cell type
|
|
9
|
+
def t(val); @type = val; end;
|
|
10
|
+
# Set cell value
|
|
11
|
+
def v(val); @value = val; end;
|
|
12
|
+
# Set cell style (number format and style)
|
|
13
|
+
def s(val); @style = val; end;
|
|
14
|
+
|
|
15
|
+
# When called without parameters, returns the x coordinate of the calling cell object based on the value of #@xlcoords
|
|
16
|
+
# If a parameter is given, #x will return the x coordinate corresponding to the parameter
|
|
17
|
+
# @example find x coordinate (column number) of a cell
|
|
18
|
+
# c = Cell.new
|
|
19
|
+
# c.xlcoords = ('B3')
|
|
20
|
+
# c.x #=> 1
|
|
21
|
+
# @param [String] coord Optional parameter used when method is not called from a Cell object
|
|
22
|
+
# @return [Integer] x coordinate
|
|
23
|
+
def x(coord=nil)
|
|
24
|
+
if coord.nil?
|
|
25
|
+
coord = @xlcoords
|
|
26
|
+
end
|
|
27
|
+
('A'..(coord.scan(/\p{Alpha}+|\p{Digit}+/u)[0])).to_a.length-1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# When called without parameters, returns the y coordinate of the calling cell object based on the value of #@xlcoords
|
|
31
|
+
# If a parameter is given, #y will return the y coordinate corresponding to the parameter
|
|
32
|
+
# @example find y coordinate (row number) of a cell
|
|
33
|
+
# c = Cell.new
|
|
34
|
+
# c.xlcoords = ('B3')
|
|
35
|
+
# c.y #=> 2
|
|
36
|
+
# @param [String] coord Optional parameter used when method is not called from a Cell object
|
|
37
|
+
# @return [Integer] x coordinate
|
|
38
|
+
def y(coord=nil)
|
|
39
|
+
if coord.nil?
|
|
40
|
+
coord = @xlcoords
|
|
41
|
+
end
|
|
42
|
+
coord.scan(/\p{Alpha}+|\p{Digit}+/u)[1].to_i-1
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Oxcelix
|
|
2
|
+
# The Comments class is a parser which builds an array of comments
|
|
3
|
+
class Comments < ::Ox::Sax
|
|
4
|
+
attr_accessor :commarray, :comment
|
|
5
|
+
def initialize
|
|
6
|
+
@commarray=[]
|
|
7
|
+
@comment={}
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Push Cell comment hash (comment + reference) to @commarray
|
|
11
|
+
def text(str)
|
|
12
|
+
@comment[:comment]=str.gsub(' ', '')
|
|
13
|
+
@commarray << @comment
|
|
14
|
+
@comment = Hash.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Returns reference
|
|
18
|
+
def attr(name, str)
|
|
19
|
+
if name == :ref
|
|
20
|
+
@comment[:ref]=str
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Oxcelix
|
|
2
|
+
# Ox based SAX parser which pushes shared strings (taken from the sharedString.xml file) to an array
|
|
3
|
+
# These strings will replace the references in the cells (interpolation).
|
|
4
|
+
class Sharedstrings < ::Ox::Sax
|
|
5
|
+
attr_accessor :stringarray
|
|
6
|
+
def initialize
|
|
7
|
+
@stringarray=[]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Push the comment string into @stringarray
|
|
11
|
+
def text(str)
|
|
12
|
+
@stringarray << str
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
|
|
2
|
+
module Oxcelix
|
|
3
|
+
##
|
|
4
|
+
# The Xlsheet class is a SAX parser based on the Ox library. It parses a
|
|
5
|
+
# SpreadsheetML (AKA Office Open XML) formatted XML file and returns an array
|
|
6
|
+
# of Cell objects {#cellarray} and an array of merged cells {#mergedcells}.
|
|
7
|
+
#
|
|
8
|
+
# Xlsheet will omit the following:
|
|
9
|
+
# * empty cells
|
|
10
|
+
# * cells containing formulas
|
|
11
|
+
#
|
|
12
|
+
# Only non-empty cells of merged groups will be added to {#cellarray}. A separate array
|
|
13
|
+
# {#mergedcells} is reserved for merging.
|
|
14
|
+
class Xlsheet < ::Ox::Sax
|
|
15
|
+
# @!attribute [rw] xmlstack
|
|
16
|
+
# @return [Array] Stores the state machine's actual state
|
|
17
|
+
# @!attribute [rw] mergedcells
|
|
18
|
+
# @return [Array] the array of merged cells
|
|
19
|
+
# @!attribute [rw] cellarray
|
|
20
|
+
# @return [Array] the array of non-empty (meaningful) cells of the current sheet
|
|
21
|
+
# @!attribute [rw] cell
|
|
22
|
+
# @return [Cell] the cell currently being processed.
|
|
23
|
+
attr_accessor :xmlstack, :mergedcells, :cellarray, :cell
|
|
24
|
+
def initialize()
|
|
25
|
+
@xmlstack = []
|
|
26
|
+
@mergedcells = []
|
|
27
|
+
@cellarray = []
|
|
28
|
+
@cell = Cell.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Save SAX state-machine state to {#xmlstack} if and only if the processed
|
|
32
|
+
# element is a :c (column) or a :mergeCell (merged cell)
|
|
33
|
+
# @param [String] name Start element
|
|
34
|
+
def start_element(name)
|
|
35
|
+
if name == :c || name == :mergeCell
|
|
36
|
+
@xmlstack << name
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Step back in the stack ({#xmlstack}.pop), clear actual cell information
|
|
41
|
+
# @param [String] name Element ends
|
|
42
|
+
def end_element(name)
|
|
43
|
+
@xmlstack.pop
|
|
44
|
+
if name == :c || name == :mergeCell
|
|
45
|
+
@cell=Cell.new
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Set cell value, style, etc. This will only happen if the cell has an
|
|
50
|
+
# actual value AND the parser's state is :c.
|
|
51
|
+
# If the state is :mergeCell AND the actual attribute name is :ref the
|
|
52
|
+
# attribute will be added to the merged cells array.
|
|
53
|
+
# The attribute name is tested against the Cell object: if the cell
|
|
54
|
+
# has a method named the same way, that method is called with the str parameter.
|
|
55
|
+
# @param [String] name of the attribute.
|
|
56
|
+
# @param [String] str Content of the attribute
|
|
57
|
+
def attr(name, str)
|
|
58
|
+
if @xmlstack.last == :c
|
|
59
|
+
@cell.send name, str if @cell.respond_to?(name)
|
|
60
|
+
elsif xmlstack.last == :mergeCell && name == :ref
|
|
61
|
+
@mergedcells << str
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# String type cell content is parsed here. String interpolation using the
|
|
66
|
+
# sharedStrings.xml file is done in the #Sharedstrings class
|
|
67
|
+
def text(str)
|
|
68
|
+
if @xmlstack.last == :c
|
|
69
|
+
if @cell.type != "shared" && @cell.type != "e" && str.numeric?
|
|
70
|
+
@cell.v str
|
|
71
|
+
@cellarray << @cell
|
|
72
|
+
end
|
|
73
|
+
cell=Cell.new
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
module Oxcelix
|
|
3
|
+
# The Sheet class represents an excel sheet.
|
|
4
|
+
# @!attribute [rw] name
|
|
5
|
+
# @return [String] Sheet name
|
|
6
|
+
# @!attribute [rw] sheetId
|
|
7
|
+
# @return [String] returns the sheetId SheetML internal attribute
|
|
8
|
+
# @!attribute [rw] relationId
|
|
9
|
+
# @return [String] returns the relation key used to reference the comments file related to a certain sheet.
|
|
10
|
+
# @!attribute [rw] data
|
|
11
|
+
# @return [Array] stores the collection of cells of a certain sheet. This is the copy of the sum of cellarray and mergedcells.
|
|
12
|
+
# This is laterconverted to a matrix
|
|
13
|
+
class Sheet
|
|
14
|
+
attr_accessor :name, :sheetId, :relationId, :data
|
|
15
|
+
def initialize; @data=[]; end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,219 @@
|
|
|
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
|
+
# The Workbook class will open the excel file, and convert it to a collection of
|
|
16
|
+
# Matrix objects
|
|
17
|
+
# @!attribute [rw] sheetbase
|
|
18
|
+
# @return [Hash] sheetbase Single sheet metadata - will be obsolete in the future
|
|
19
|
+
# @!attribute [rw] sheets
|
|
20
|
+
# @return [Array] sheets Collection of {Sheet} objects
|
|
21
|
+
# @!attribute [rw] sharedstrings
|
|
22
|
+
# @return [Sharedstrings] sharedstrings returns a {Sharedstrings} object used to interpolate string indexes with their actual value.
|
|
23
|
+
class Workbook
|
|
24
|
+
include Cellhelper
|
|
25
|
+
include Workbookhelper
|
|
26
|
+
attr_accessor :sheetbase, :sheets, :sharedstrings
|
|
27
|
+
##
|
|
28
|
+
# Create a new Workbook object.
|
|
29
|
+
#
|
|
30
|
+
# filename is the name of the Excel 2007/2010 file to be opened (xlsx)
|
|
31
|
+
#
|
|
32
|
+
# options is a collection of options that can be passed to Workbook.
|
|
33
|
+
# Options may include:
|
|
34
|
+
# * :copymerge (=> true/false) - Copy and repeat the content of the merged cells into the whole group, e.g.
|
|
35
|
+
# the group of three merged cells <tt>| a |</tt> will become <tt>|a|a|a|</tt>
|
|
36
|
+
# * :include (Ary) - an array of sheet names to be included
|
|
37
|
+
# * :exclude (Ary) - an array of sheet names not to be processed
|
|
38
|
+
#
|
|
39
|
+
# The excel file is first getting unzipped, then the workbook.xml file gets
|
|
40
|
+
# processed. This file stores sheet metadata, which will be filtered (by including
|
|
41
|
+
# and excluding sheets from further processing)
|
|
42
|
+
#
|
|
43
|
+
# The next stage is building sheets.
|
|
44
|
+
# This includes:
|
|
45
|
+
# * Parsing the XML files representing the sheets
|
|
46
|
+
# * Interpolation of the shared strings
|
|
47
|
+
# * adding comments to the cells
|
|
48
|
+
# * Converting each sheet to a Matrix object
|
|
49
|
+
def initialize(filename, options={})
|
|
50
|
+
@destination = Dir.pwd+'/tmp'
|
|
51
|
+
FileUtils.mkdir(@destination)
|
|
52
|
+
Zip::File.open(filename){ |zip_file|
|
|
53
|
+
zip_file.each{ |f|
|
|
54
|
+
f_path=File.join(@destination, f.name)
|
|
55
|
+
FileUtils.mkdir_p(File.dirname(f_path))
|
|
56
|
+
zip_file.extract(f, f_path) unless File.exists?(f_path)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
@sheets=[]
|
|
60
|
+
@sheetbase={}
|
|
61
|
+
@a=Ox::load_file(@destination+'/xl/workbook.xml')
|
|
62
|
+
|
|
63
|
+
sheetdata(options); commentsrel; shstrings;
|
|
64
|
+
@sheets.each do |x|
|
|
65
|
+
sname="sheet#{x[:sheetId]}"
|
|
66
|
+
@sheet = Xlsheet.new()
|
|
67
|
+
File.open(@destination+"/xl/worksheets/#{sname}.xml", 'r') do |f|
|
|
68
|
+
Ox.sax_parse(@sheet, f)
|
|
69
|
+
end
|
|
70
|
+
comments=
|
|
71
|
+
mkcomments(x[:comments])
|
|
72
|
+
@sheet.cellarray.each do |sh|
|
|
73
|
+
if sh.type=="s"
|
|
74
|
+
sh.value = @sharedstrings[sh.value.to_i]
|
|
75
|
+
end
|
|
76
|
+
if !comments.nil?
|
|
77
|
+
comm=comments.select {|c| c[:ref]==(sh.xlcoords)}
|
|
78
|
+
if comm.size > 0
|
|
79
|
+
sh.comment=comm
|
|
80
|
+
end
|
|
81
|
+
comments.delete_if{|c| c[:ref]==(sh.xlcoords)}
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
x[:cells] = @sheet.cellarray
|
|
85
|
+
x[:mergedcells] = @sheet.mergedcells
|
|
86
|
+
end
|
|
87
|
+
FileUtils.remove_dir(@destination, true)
|
|
88
|
+
matrixto(options[:copymerge])
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
# @private
|
|
93
|
+
# Given the data found in workbook.xml, create a hash and push it to the sheets
|
|
94
|
+
# array.
|
|
95
|
+
#
|
|
96
|
+
# The hash will not be pushed into the array if the sheet name is blacklisted
|
|
97
|
+
# (it appears in the *excluded_sheets* array) or does not appear in the list of
|
|
98
|
+
# included sheets.
|
|
99
|
+
#
|
|
100
|
+
# If *included_sheets* (the array of whitelisted sheets) is *nil*, the hash is added.
|
|
101
|
+
def sheetdata options={}
|
|
102
|
+
@a.locate("workbook/sheets/*").each do |x|
|
|
103
|
+
@sheetbase[:name] = x[:name]
|
|
104
|
+
@sheetbase[:sheetId] = x[:sheetId]
|
|
105
|
+
@sheetbase[:relationId] = x[:"r:id"]
|
|
106
|
+
@sheets << @sheetbase
|
|
107
|
+
@sheetbase=Hash.new
|
|
108
|
+
end
|
|
109
|
+
sheetarr=@sheets.map{|i| i[:name]}
|
|
110
|
+
if options[:include].nil?; options[:include]=[]; end
|
|
111
|
+
if options[:include].to_a.size>0
|
|
112
|
+
sheetarr.keep_if{|item| options[:include].to_a.detect{|d| d==item}}
|
|
113
|
+
end
|
|
114
|
+
sheetarr=sheetarr-options[:exclude].to_a
|
|
115
|
+
@sheets.keep_if{|item| sheetarr.detect{|d| d==item[:name]}}
|
|
116
|
+
@sheets.uniq!
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Build the relationship between sheets and the XML files storing the comments
|
|
120
|
+
# to the actual sheet.
|
|
121
|
+
def commentsrel #!!!MI VAN HA NINCS KOMMENT???????
|
|
122
|
+
unless Dir[@destination + '/xl/worksheets/_rels'].empty?
|
|
123
|
+
Find.find(@destination + '/xl/worksheets/_rels') do |path|
|
|
124
|
+
if File.basename(path).split(".").last=='rels'
|
|
125
|
+
f=Ox.load_file(path)
|
|
126
|
+
f.locate("Relationships/*").each do |x|
|
|
127
|
+
if x[:Target].include?"comments"
|
|
128
|
+
@sheets.each do |s|
|
|
129
|
+
if File.basename(path,".rels")=="sheet"+s[:sheetId]+".xml"
|
|
130
|
+
s[:comments]=x[:Target]
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
else
|
|
138
|
+
@sheets.each do |s|
|
|
139
|
+
s[:comments]=nil
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Invokes the Sharedstrings helper class
|
|
145
|
+
def shstrings
|
|
146
|
+
strings = Sharedstrings.new()
|
|
147
|
+
File.open(@destination + '/xl/sharedStrings.xml', 'r') do |f|
|
|
148
|
+
Ox.sax_parse(strings, f)
|
|
149
|
+
end
|
|
150
|
+
@sharedstrings=strings.stringarray
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Parses the comments related to the actual sheet.
|
|
154
|
+
# @param [String] commentfile
|
|
155
|
+
# @return [Array] a collection of comments relative to the Excel sheet currently processed
|
|
156
|
+
def mkcomments(commentfile)
|
|
157
|
+
unless commentfile.nil?
|
|
158
|
+
comms = Comments.new()
|
|
159
|
+
File.open(@destination + '/xl/'+commentfile.gsub('../', ''), 'r') do |f|
|
|
160
|
+
Ox.sax_parse(comms, f)
|
|
161
|
+
end
|
|
162
|
+
return comms.commarray
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Returns an array of Matrix objects.
|
|
167
|
+
# For each sheet, matrixto first checks the address (xlcoords) of the
|
|
168
|
+
# last cell in the cellarray, then builds a *nil*-filled Matrix object of
|
|
169
|
+
# size *xlcoords.x, xlcoords.y*.
|
|
170
|
+
#
|
|
171
|
+
# The matrix will then be filled with Cell objects according to their coordinates.
|
|
172
|
+
#
|
|
173
|
+
# If the *copymerge* parameter is *true*, it creates a submatrix (minor)
|
|
174
|
+
# of every mergegroup (based on the mergedcells array relative to the actual
|
|
175
|
+
# sheet), and after the only meaningful cell of the minor is found, it is
|
|
176
|
+
# copied back to the remaining cells of the group. The coordinates (xlcoords)
|
|
177
|
+
# of each copied cell is changed to reflect the actual Excel coordinate.
|
|
178
|
+
#
|
|
179
|
+
# The matrix will replace the array of cells in the actual sheet.
|
|
180
|
+
# @param [Bool ] copymerge
|
|
181
|
+
# @return [Matrix] a Matrix that stores the cell values, and, depending on the copymerge parameter, will copy the merged value
|
|
182
|
+
# into every merged cell
|
|
183
|
+
def matrixto(copymerge)
|
|
184
|
+
@sheets.each_with_index do |sheet, i|
|
|
185
|
+
m=Matrix.build(sheet[:cells].last.y+1, sheet[:cells].last.x+1) {nil}
|
|
186
|
+
sheet[:cells].each do |c|
|
|
187
|
+
m[c.y, c.x]=c
|
|
188
|
+
end
|
|
189
|
+
if copymerge==true
|
|
190
|
+
sheet[:mergedcells].each do |mc|
|
|
191
|
+
a = mc.split(':')
|
|
192
|
+
x1=x(a[0])
|
|
193
|
+
y1=y(a[0])
|
|
194
|
+
x2=x(a[1])
|
|
195
|
+
y2=y(a[1])
|
|
196
|
+
mrange=m.minor(y1..y2, x1..x2)
|
|
197
|
+
valuecell=mrange.to_a.flatten.compact[0]
|
|
198
|
+
(x1..x2).each do |col|
|
|
199
|
+
(y1..y2).each do |row|
|
|
200
|
+
if valuecell != nil
|
|
201
|
+
valuecell.xlcoords=(col.col_name)+(row+1).to_s
|
|
202
|
+
m[col, row]=valuecell
|
|
203
|
+
else
|
|
204
|
+
valuecell=Cell.new
|
|
205
|
+
valuecell.xlcoords=(col.col_name)+(row+1).to_s
|
|
206
|
+
m[col, row]=valuecell
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
s=Sheet.new
|
|
213
|
+
s.name=@sheets[i][:name]; s.sheetId=@sheets[i][:sheetId]; s.relationId=@sheets[i][:relationId]
|
|
214
|
+
s.data=m
|
|
215
|
+
@sheets[i]=s
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
data/lib/oxcelix.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'ox'
|
|
2
|
+
require 'find'
|
|
3
|
+
require 'matrix'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'zip'
|
|
6
|
+
require 'oxcelix/cellhelper'
|
|
7
|
+
require 'oxcelix/cell'
|
|
8
|
+
require 'oxcelix/sheet'
|
|
9
|
+
require 'oxcelix/workbook'
|
|
10
|
+
require 'oxcelix/sax/sharedstrings'
|
|
11
|
+
require 'oxcelix/sax/comments'
|
|
12
|
+
require 'oxcelix/sax/xlsheet'
|
|
13
|
+
|
|
14
|
+
class String
|
|
15
|
+
# Returns true if the given String represents a numeric value
|
|
16
|
+
# @return [Bool]
|
|
17
|
+
def numeric?
|
|
18
|
+
Float(self) != nil rescue false
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Matrix
|
|
23
|
+
# Set the cell value i,j to x, where i=row, and j=column.
|
|
24
|
+
# @param i [Integer] Row
|
|
25
|
+
# @param j [Integer] Column
|
|
26
|
+
# @param x [Object] New cell value
|
|
27
|
+
def []=(i, j, x)
|
|
28
|
+
@rows[i][j]=x
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class Fixnum
|
|
33
|
+
# Returns the column name corresponding to the given number. e.g: 1.col_name => 'B'
|
|
34
|
+
# @return [String]
|
|
35
|
+
def col_name
|
|
36
|
+
val=self/26
|
|
37
|
+
(val > 0 ? (val - 1).col_name : "") + (self % 26 + 65).chr
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# The namespace for all classes and modules included in Oxcelix.
|
|
42
|
+
module Oxcelix
|
|
43
|
+
|
|
44
|
+
end
|
data/oxcelix.gemspec
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'rake'
|
|
4
|
+
Gem::Specification.new do |s|
|
|
5
|
+
s.name = 'oxcelix'
|
|
6
|
+
s.version = '0.1.0'
|
|
7
|
+
s.date = '2013-10-06'
|
|
8
|
+
s.summary = 'A fast Excel 2007/2010 file parser'
|
|
9
|
+
s.description = 'A fast .xlsx file parser that returns a collection of Matrix objects'
|
|
10
|
+
s.authors = 'Giovanni Biczo'
|
|
11
|
+
s.homepage = 'http://github.com/gbiczo/oxcelix'
|
|
12
|
+
s.rubyforge_project = 'oxcelix'
|
|
13
|
+
|
|
14
|
+
s.files = FileList["LICENSE", "README.rdoc", "README.md",
|
|
15
|
+
"lib/*", "lib/oxcelix/*", "lib/oxcelix/sax/*",
|
|
16
|
+
"oxcelix.gemspec", "spec/*"].to_a
|
|
17
|
+
s.license = 'MIT'
|
|
18
|
+
|
|
19
|
+
s.add_runtime_dependency "ox", [">= 2.0.6"]
|
|
20
|
+
s.add_runtime_dependency "rubyzip", [">= 0.9.9"]
|
|
21
|
+
end
|
data/spec/cell_spec.rb
ADDED
data/spec/fixnum_spec.rb
ADDED
data/spec/matrix_spec.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#require './spec_helper'
|
|
2
|
+
require '../oxcelix.rb'
|
|
3
|
+
describe "Oxcelix module" do
|
|
4
|
+
# before :all do
|
|
5
|
+
describe 'Workbook' do
|
|
6
|
+
context 'normal' do
|
|
7
|
+
it "should open the excel file and return a Workbook object" do
|
|
8
|
+
file = './test.xlsx'
|
|
9
|
+
w=Oxcelix::Workbook.new(file)
|
|
10
|
+
w.sheets.size.should == 2
|
|
11
|
+
w.sheets[0].name.should=="Testsheet1"
|
|
12
|
+
w.sheets[1].name.should=="Testsheet2"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
context 'with excluded sheets' do
|
|
16
|
+
it "should open the sheets not excluded of the excel file" do
|
|
17
|
+
file = './test.xlsx'
|
|
18
|
+
w=Oxcelix::Workbook.new(file, {:exclude=>['Testsheet2']})
|
|
19
|
+
w.sheets.size.should==1
|
|
20
|
+
w.sheets[0].name.should=="Testsheet1"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
context 'with included sheets' do
|
|
24
|
+
it "should open only the included sheets of the excel file" do
|
|
25
|
+
file = './test.xlsx'
|
|
26
|
+
w=Oxcelix::Workbook.new(file, {:include=>['Testsheet2']})
|
|
27
|
+
w.sheets.size.should==1
|
|
28
|
+
w.sheets[0].name.should=="Testsheet2"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
context 'with merged cells copied group-wide'
|
|
32
|
+
it "should open the excel file and copy the merged cells trough the mergegroup" do
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
# end
|
|
36
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/string_spec.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#require './spec_helper'
|
|
2
|
+
require '../oxcelix.rb'
|
|
3
|
+
describe "String object" do
|
|
4
|
+
describe 'numeric?' do
|
|
5
|
+
context "with numbers" do
|
|
6
|
+
it "should return true" do
|
|
7
|
+
(1..100).each do |x|
|
|
8
|
+
x.to_s.numeric?.should == true
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
context "with strings"
|
|
12
|
+
it "should return false" do
|
|
13
|
+
('a'..'zz').each do |x|
|
|
14
|
+
x.numeric?.should == false
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/spec/test.xlsx
ADDED
|
Binary file
|
metadata
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: oxcelix
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Giovanni Biczo
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2013-10-06 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: ox
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ! '>='
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 2.0.6
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ! '>='
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: 2.0.6
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: rubyzip
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ! '>='
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: 0.9.9
|
|
38
|
+
type: :runtime
|
|
39
|
+
prerelease: false
|
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ! '>='
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: 0.9.9
|
|
46
|
+
description: A fast .xlsx file parser that returns a collection of Matrix objects
|
|
47
|
+
email:
|
|
48
|
+
executables: []
|
|
49
|
+
extensions: []
|
|
50
|
+
extra_rdoc_files: []
|
|
51
|
+
files:
|
|
52
|
+
- LICENSE
|
|
53
|
+
- README.rdoc
|
|
54
|
+
- README.md
|
|
55
|
+
- lib/oxcelix.rb
|
|
56
|
+
- lib/oxcelix/cell.rb
|
|
57
|
+
- lib/oxcelix/workbook.rb
|
|
58
|
+
- lib/oxcelix/sheet.rb
|
|
59
|
+
- lib/oxcelix/cellhelper.rb
|
|
60
|
+
- lib/oxcelix/sax/sharedstrings.rb
|
|
61
|
+
- lib/oxcelix/sax/xlsheet.rb
|
|
62
|
+
- lib/oxcelix/sax/comments.rb
|
|
63
|
+
- oxcelix.gemspec
|
|
64
|
+
- spec/test.xlsx
|
|
65
|
+
- spec/spec_helper.rb
|
|
66
|
+
- spec/fixnum_spec.rb
|
|
67
|
+
- spec/oxcelix_spec.rb
|
|
68
|
+
- spec/cell_spec.rb
|
|
69
|
+
- spec/matrix_spec.rb
|
|
70
|
+
- spec/string_spec.rb
|
|
71
|
+
homepage: http://github.com/gbiczo/oxcelix
|
|
72
|
+
licenses:
|
|
73
|
+
- MIT
|
|
74
|
+
post_install_message:
|
|
75
|
+
rdoc_options: []
|
|
76
|
+
require_paths:
|
|
77
|
+
- lib
|
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
|
+
none: false
|
|
80
|
+
requirements:
|
|
81
|
+
- - ! '>='
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '0'
|
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
|
+
none: false
|
|
86
|
+
requirements:
|
|
87
|
+
- - ! '>='
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
requirements: []
|
|
91
|
+
rubyforge_project: oxcelix
|
|
92
|
+
rubygems_version: 1.8.25
|
|
93
|
+
signing_key:
|
|
94
|
+
specification_version: 3
|
|
95
|
+
summary: A fast Excel 2007/2010 file parser
|
|
96
|
+
test_files: []
|
|
97
|
+
has_rdoc:
|