roo 1.13.2 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +7 -0
- data/.simplecov +4 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +515 -0
- data/Gemfile +16 -10
- data/Guardfile +24 -0
- data/LICENSE +3 -1
- data/README.md +254 -0
- data/Rakefile +23 -23
- data/examples/roo_soap_client.rb +28 -31
- data/examples/roo_soap_server.rb +4 -6
- data/examples/write_me.rb +9 -10
- data/lib/roo/base.rb +334 -395
- data/lib/roo/csv.rb +120 -113
- data/lib/roo/excelx/cell.rb +77 -0
- data/lib/roo/excelx/comments.rb +22 -0
- data/lib/roo/excelx/extractor.rb +22 -0
- data/lib/roo/excelx/relationships.rb +25 -0
- data/lib/roo/excelx/shared_strings.rb +37 -0
- data/lib/roo/excelx/sheet.rb +107 -0
- data/lib/roo/excelx/sheet_doc.rb +200 -0
- data/lib/roo/excelx/styles.rb +64 -0
- data/lib/roo/excelx/workbook.rb +59 -0
- data/lib/roo/excelx.rb +413 -597
- data/lib/roo/font.rb +17 -0
- data/lib/roo/libre_office.rb +5 -0
- data/lib/roo/link.rb +15 -0
- data/lib/roo/{openoffice.rb → open_office.rb} +681 -496
- data/lib/roo/spreadsheet.rb +20 -23
- data/lib/roo/utils.rb +78 -0
- data/lib/roo/version.rb +3 -0
- data/lib/roo.rb +18 -24
- data/roo.gemspec +20 -204
- data/spec/lib/roo/base_spec.rb +1 -4
- data/spec/lib/roo/csv_spec.rb +21 -13
- data/spec/lib/roo/excelx/format_spec.rb +7 -6
- data/spec/lib/roo/excelx_spec.rb +424 -11
- data/spec/lib/roo/libreoffice_spec.rb +16 -6
- data/spec/lib/roo/openoffice_spec.rb +13 -8
- data/spec/lib/roo/spreadsheet_spec.rb +40 -12
- data/spec/lib/roo/utils_spec.rb +106 -0
- data/spec/spec_helper.rb +2 -1
- data/test/test_generic_spreadsheet.rb +117 -139
- data/test/test_helper.rb +9 -56
- data/test/test_roo.rb +274 -478
- metadata +65 -303
- data/CHANGELOG +0 -417
- data/Gemfile.lock +0 -78
- data/README.markdown +0 -126
- data/VERSION +0 -1
- data/lib/roo/excel.rb +0 -355
- data/lib/roo/excel2003xml.rb +0 -300
- data/lib/roo/google.rb +0 -292
- data/lib/roo/roo_rails_helper.rb +0 -83
- data/lib/roo/worksheet.rb +0 -18
- data/scripts/txt2html +0 -67
- data/spec/lib/roo/excel2003xml_spec.rb +0 -15
- data/spec/lib/roo/excel_spec.rb +0 -17
- data/spec/lib/roo/google_spec.rb +0 -64
- data/test/files/1900_base.xls +0 -0
- data/test/files/1900_base.xlsx +0 -0
- data/test/files/1904_base.xls +0 -0
- data/test/files/1904_base.xlsx +0 -0
- data/test/files/Bibelbund.csv +0 -3741
- data/test/files/Bibelbund.ods +0 -0
- data/test/files/Bibelbund.xls +0 -0
- data/test/files/Bibelbund.xlsx +0 -0
- data/test/files/Bibelbund.xml +0 -62518
- data/test/files/Bibelbund1.ods +0 -0
- data/test/files/Pfand_from_windows_phone.xlsx +0 -0
- data/test/files/bad_excel_date.xls +0 -0
- data/test/files/bbu.ods +0 -0
- data/test/files/bbu.xls +0 -0
- data/test/files/bbu.xlsx +0 -0
- data/test/files/bbu.xml +0 -152
- data/test/files/bode-v1.ods.zip +0 -0
- data/test/files/bode-v1.xls.zip +0 -0
- data/test/files/boolean.csv +0 -2
- data/test/files/boolean.ods +0 -0
- data/test/files/boolean.xls +0 -0
- data/test/files/boolean.xlsx +0 -0
- data/test/files/boolean.xml +0 -112
- data/test/files/borders.ods +0 -0
- data/test/files/borders.xls +0 -0
- data/test/files/borders.xlsx +0 -0
- data/test/files/borders.xml +0 -144
- data/test/files/bug-numbered-sheet-names.xlsx +0 -0
- data/test/files/bug-row-column-fixnum-float.xls +0 -0
- data/test/files/bug-row-column-fixnum-float.xml +0 -127
- data/test/files/comments.ods +0 -0
- data/test/files/comments.xls +0 -0
- data/test/files/comments.xlsx +0 -0
- data/test/files/csvtypes.csv +0 -1
- data/test/files/datetime.ods +0 -0
- data/test/files/datetime.xls +0 -0
- data/test/files/datetime.xlsx +0 -0
- data/test/files/datetime.xml +0 -142
- data/test/files/datetime_floatconv.xls +0 -0
- data/test/files/datetime_floatconv.xml +0 -148
- data/test/files/dreimalvier.ods +0 -0
- data/test/files/emptysheets.ods +0 -0
- data/test/files/emptysheets.xls +0 -0
- data/test/files/emptysheets.xlsx +0 -0
- data/test/files/emptysheets.xml +0 -105
- data/test/files/excel2003.xml +0 -21140
- data/test/files/false_encoding.xls +0 -0
- data/test/files/false_encoding.xml +0 -132
- data/test/files/file_item_error.xlsx +0 -0
- data/test/files/formula.ods +0 -0
- data/test/files/formula.xls +0 -0
- data/test/files/formula.xlsx +0 -0
- data/test/files/formula.xml +0 -134
- data/test/files/formula_parse_error.xls +0 -0
- data/test/files/formula_parse_error.xml +0 -1833
- data/test/files/formula_string_error.xlsx +0 -0
- data/test/files/html-escape.ods +0 -0
- data/test/files/link.xls +0 -0
- data/test/files/link.xlsx +0 -0
- data/test/files/matrix.ods +0 -0
- data/test/files/matrix.xls +0 -0
- data/test/files/named_cells.ods +0 -0
- data/test/files/named_cells.xls +0 -0
- data/test/files/named_cells.xlsx +0 -0
- data/test/files/no_spreadsheet_file.txt +0 -1
- data/test/files/numbers1.csv +0 -18
- data/test/files/numbers1.ods +0 -0
- data/test/files/numbers1.xls +0 -0
- data/test/files/numbers1.xlsx +0 -0
- data/test/files/numbers1.xml +0 -312
- data/test/files/numeric-link.xlsx +0 -0
- data/test/files/only_one_sheet.ods +0 -0
- data/test/files/only_one_sheet.xls +0 -0
- data/test/files/only_one_sheet.xlsx +0 -0
- data/test/files/only_one_sheet.xml +0 -67
- data/test/files/paragraph.ods +0 -0
- data/test/files/paragraph.xls +0 -0
- data/test/files/paragraph.xlsx +0 -0
- data/test/files/paragraph.xml +0 -127
- data/test/files/prova.xls +0 -0
- data/test/files/ric.ods +0 -0
- data/test/files/simple_spreadsheet.ods +0 -0
- data/test/files/simple_spreadsheet.xls +0 -0
- data/test/files/simple_spreadsheet.xlsx +0 -0
- data/test/files/simple_spreadsheet.xml +0 -225
- data/test/files/simple_spreadsheet_from_italo.ods +0 -0
- data/test/files/simple_spreadsheet_from_italo.xls +0 -0
- data/test/files/simple_spreadsheet_from_italo.xml +0 -242
- data/test/files/so_datetime.csv +0 -7
- data/test/files/style.ods +0 -0
- data/test/files/style.xls +0 -0
- data/test/files/style.xlsx +0 -0
- data/test/files/style.xml +0 -154
- data/test/files/time-test.csv +0 -2
- data/test/files/time-test.ods +0 -0
- data/test/files/time-test.xls +0 -0
- data/test/files/time-test.xlsx +0 -0
- data/test/files/time-test.xml +0 -131
- data/test/files/type_excel.ods +0 -0
- data/test/files/type_excel.xlsx +0 -0
- data/test/files/type_excelx.ods +0 -0
- data/test/files/type_excelx.xls +0 -0
- data/test/files/type_openoffice.xls +0 -0
- data/test/files/type_openoffice.xlsx +0 -0
- data/test/files/whitespace.ods +0 -0
- data/test/files/whitespace.xls +0 -0
- data/test/files/whitespace.xlsx +0 -0
- data/test/files/whitespace.xml +0 -184
- data/test/rm_sub_test.rb +0 -12
- data/test/rm_test.rb +0 -7
- data/website/index.html +0 -385
- data/website/index.txt +0 -423
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/screen.css +0 -130
- data/website/template.rhtml +0 -48
data/lib/roo/csv.rb
CHANGED
@@ -1,113 +1,120 @@
|
|
1
|
-
require 'csv'
|
2
|
-
require 'time'
|
3
|
-
|
4
|
-
# The CSV class can read csv files (must be separated with commas) which then
|
5
|
-
# can be handled like spreadsheets. This means you can access cells like A5
|
6
|
-
# within these files.
|
7
|
-
# The CSV class provides only string objects. If you want conversions to other
|
8
|
-
# types you have to do it yourself.
|
9
|
-
#
|
10
|
-
# You can pass options to the underlying CSV parse operation, via the
|
11
|
-
# :csv_options option.
|
12
|
-
#
|
13
|
-
|
14
|
-
class Roo::CSV < Roo::Base
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
sheet
|
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
|
-
|
1
|
+
require 'csv'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
# The CSV class can read csv files (must be separated with commas) which then
|
5
|
+
# can be handled like spreadsheets. This means you can access cells like A5
|
6
|
+
# within these files.
|
7
|
+
# The CSV class provides only string objects. If you want conversions to other
|
8
|
+
# types you have to do it yourself.
|
9
|
+
#
|
10
|
+
# You can pass options to the underlying CSV parse operation, via the
|
11
|
+
# :csv_options option.
|
12
|
+
#
|
13
|
+
|
14
|
+
class Roo::CSV < Roo::Base
|
15
|
+
|
16
|
+
attr_reader :filename
|
17
|
+
|
18
|
+
# Returns an array with the names of the sheets. In CSV class there is only
|
19
|
+
# one dummy sheet, because a csv file cannot have more than one sheet.
|
20
|
+
def sheets
|
21
|
+
['default']
|
22
|
+
end
|
23
|
+
|
24
|
+
def cell(row, col, sheet=nil)
|
25
|
+
sheet ||= default_sheet
|
26
|
+
read_cells(sheet)
|
27
|
+
@cell[normalize(row,col)]
|
28
|
+
end
|
29
|
+
|
30
|
+
def celltype(row, col, sheet=nil)
|
31
|
+
sheet ||= default_sheet
|
32
|
+
read_cells(sheet)
|
33
|
+
@cell_type[normalize(row,col)]
|
34
|
+
end
|
35
|
+
|
36
|
+
def cell_postprocessing(row,col,value)
|
37
|
+
value
|
38
|
+
end
|
39
|
+
|
40
|
+
def csv_options
|
41
|
+
@options[:csv_options] || {}
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
TYPE_MAP = {
|
47
|
+
String => :string,
|
48
|
+
Float => :float,
|
49
|
+
Date => :date,
|
50
|
+
DateTime => :datetime,
|
51
|
+
}
|
52
|
+
|
53
|
+
def celltype_class(value)
|
54
|
+
TYPE_MAP[value.class]
|
55
|
+
end
|
56
|
+
|
57
|
+
def each_row(options, &block)
|
58
|
+
if uri?(filename)
|
59
|
+
make_tmpdir do |tmpdir|
|
60
|
+
tmp_filename = download_uri(filename, tmpdir)
|
61
|
+
CSV.foreach(tmp_filename, options, &block)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
CSV.foreach(filename, options, &block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def read_cells(sheet = default_sheet)
|
69
|
+
sheet ||= default_sheet
|
70
|
+
return if @cells_read[sheet]
|
71
|
+
@first_row[sheet] = 1
|
72
|
+
@last_row[sheet] = 0
|
73
|
+
@first_column[sheet] = 1
|
74
|
+
@last_column[sheet] = 1
|
75
|
+
rownum = 1
|
76
|
+
each_row csv_options do |row|
|
77
|
+
row.each_with_index do |elem,i|
|
78
|
+
@cell[[rownum,i+1]] = cell_postprocessing rownum,i+1, elem
|
79
|
+
@cell_type[[rownum,i+1]] = celltype_class @cell[[rownum,i+1]]
|
80
|
+
if i+1 > @last_column[sheet]
|
81
|
+
@last_column[sheet] += 1
|
82
|
+
end
|
83
|
+
end
|
84
|
+
rownum += 1
|
85
|
+
@last_row[sheet] += 1
|
86
|
+
end
|
87
|
+
@cells_read[sheet] = true
|
88
|
+
#-- adjust @first_row if neccessary
|
89
|
+
while !row(@first_row[sheet]).any? and @first_row[sheet] < @last_row[sheet]
|
90
|
+
@first_row[sheet] += 1
|
91
|
+
end
|
92
|
+
#-- adjust @last_row if neccessary
|
93
|
+
while !row(@last_row[sheet]).any? and @last_row[sheet] and
|
94
|
+
@last_row[sheet] > @first_row[sheet]
|
95
|
+
@last_row[sheet] -= 1
|
96
|
+
end
|
97
|
+
#-- adjust @first_column if neccessary
|
98
|
+
while !column(@first_column[sheet]).any? and
|
99
|
+
@first_column[sheet] and
|
100
|
+
@first_column[sheet] < @last_column[sheet]
|
101
|
+
@first_column[sheet] += 1
|
102
|
+
end
|
103
|
+
#-- adjust @last_column if neccessary
|
104
|
+
while !column(@last_column[sheet]).any? and
|
105
|
+
@last_column[sheet] and
|
106
|
+
@last_column[sheet] > @first_column[sheet]
|
107
|
+
@last_column[sheet] -= 1
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def clean_sheet(sheet)
|
112
|
+
read_cells(sheet)
|
113
|
+
|
114
|
+
@cell.each_pair do |coord, value|
|
115
|
+
@cell[coord] = sanitize_value(value) if value.is_a?(::String)
|
116
|
+
end
|
117
|
+
|
118
|
+
@cleaned[sheet] = true
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Roo
|
4
|
+
class Excelx
|
5
|
+
class Cell
|
6
|
+
attr_reader :type, :formula, :value, :excelx_type, :excelx_value, :style, :hyperlink, :coordinate
|
7
|
+
attr_writer :value
|
8
|
+
|
9
|
+
def initialize(value, type, formula, excelx_type, excelx_value, style, hyperlink, base_date, coordinate)
|
10
|
+
@type = type
|
11
|
+
@formula = formula
|
12
|
+
@base_date = base_date if [:date, :datetime].include?(@type)
|
13
|
+
@excelx_type = excelx_type
|
14
|
+
@excelx_value = excelx_value
|
15
|
+
@style = style
|
16
|
+
@value = type_cast_value(value)
|
17
|
+
@value = Roo::Link.new(hyperlink, @value.to_s) if hyperlink
|
18
|
+
@coordinate = coordinate
|
19
|
+
end
|
20
|
+
|
21
|
+
def type
|
22
|
+
case
|
23
|
+
when @formula
|
24
|
+
:formula
|
25
|
+
when @value.is_a?(Roo::Link)
|
26
|
+
:link
|
27
|
+
else
|
28
|
+
@type
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Coordinate
|
33
|
+
attr_accessor :row, :column
|
34
|
+
|
35
|
+
def initialize(row, column)
|
36
|
+
@row, @column = row, column
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def type_cast_value(value)
|
43
|
+
case @type
|
44
|
+
when :float, :percentage
|
45
|
+
value.to_f
|
46
|
+
when :date
|
47
|
+
create_date(@base_date + value.to_i)
|
48
|
+
when :datetime
|
49
|
+
create_datetime(@base_date + value.to_f.round(6))
|
50
|
+
when :time
|
51
|
+
value.to_f * 86_400
|
52
|
+
else
|
53
|
+
value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_date(date)
|
58
|
+
yyyy, mm, dd = date.strftime('%Y-%m-%d').split('-')
|
59
|
+
|
60
|
+
Date.new(yyyy.to_i, mm.to_i, dd.to_i)
|
61
|
+
end
|
62
|
+
|
63
|
+
def create_datetime(date)
|
64
|
+
datetime_string = date.strftime('%Y-%m-%d %H:%M:%S.%N')
|
65
|
+
t = round_datetime(datetime_string)
|
66
|
+
|
67
|
+
DateTime.civil(t.year, t.month, t.day, t.hour, t.min, t.sec)
|
68
|
+
end
|
69
|
+
|
70
|
+
def round_datetime(datetime_string)
|
71
|
+
/(?<yyyy>\d+)-(?<mm>\d+)-(?<dd>\d+) (?<hh>\d+):(?<mi>\d+):(?<ss>\d+.\d+)/ =~ datetime_string
|
72
|
+
|
73
|
+
Time.new(yyyy.to_i, mm.to_i, dd.to_i, hh.to_i, mi.to_i, ss.to_r).round(0)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'roo/excelx/extractor'
|
2
|
+
|
3
|
+
module Roo
|
4
|
+
class Excelx
|
5
|
+
class Comments < Excelx::Extractor
|
6
|
+
def comments
|
7
|
+
@comments ||= extract_comments
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def extract_comments
|
13
|
+
return {} unless doc_exists?
|
14
|
+
|
15
|
+
Hash[doc.xpath('//comments/commentList/comment').map do |comment|
|
16
|
+
value = (comment.at_xpath('./text/r/t') || comment.at_xpath('./text/t')).text
|
17
|
+
[::Roo::Utils.ref_to_key(comment.attributes['ref'].to_s), value]
|
18
|
+
end]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Roo
|
2
|
+
class Excelx
|
3
|
+
class Extractor
|
4
|
+
def initialize(path)
|
5
|
+
@path = path
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def doc
|
11
|
+
@doc ||=
|
12
|
+
if doc_exists?
|
13
|
+
::Roo::Utils.load_xml(@path).remove_namespaces!
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def doc_exists?
|
18
|
+
@path && File.exist?(@path)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'roo/excelx/extractor'
|
2
|
+
|
3
|
+
module Roo
|
4
|
+
class Excelx
|
5
|
+
class Relationships < Excelx::Extractor
|
6
|
+
def [](index)
|
7
|
+
to_a[index]
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_a
|
11
|
+
@relationships ||= extract_relationships
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def extract_relationships
|
17
|
+
return [] unless doc_exists?
|
18
|
+
|
19
|
+
Hash[doc.xpath('/Relationships/Relationship').map do |rel|
|
20
|
+
[rel.attribute('Id').text, rel]
|
21
|
+
end]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'roo/excelx/extractor'
|
2
|
+
|
3
|
+
module Roo
|
4
|
+
class Excelx
|
5
|
+
class SharedStrings < Excelx::Extractor
|
6
|
+
def [](index)
|
7
|
+
to_a[index]
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_a
|
11
|
+
@array ||= extract_shared_strings
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def extract_shared_strings
|
17
|
+
return [] unless doc_exists?
|
18
|
+
|
19
|
+
# read the shared strings xml document
|
20
|
+
doc.xpath('/sst/si').map do |si|
|
21
|
+
shared_string = ''
|
22
|
+
si.children.each do |elem|
|
23
|
+
case elem.name
|
24
|
+
when 'r'
|
25
|
+
elem.children.each do |r_elem|
|
26
|
+
shared_string << r_elem.content if r_elem.name == 't'
|
27
|
+
end
|
28
|
+
when 't'
|
29
|
+
shared_string = elem.content
|
30
|
+
end
|
31
|
+
end
|
32
|
+
shared_string
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Roo
|
2
|
+
class Excelx
|
3
|
+
class Sheet
|
4
|
+
def initialize(name, rels_path, sheet_path, comments_path, styles, shared_strings, workbook, options = {})
|
5
|
+
@name = name
|
6
|
+
@rels = Relationships.new(rels_path)
|
7
|
+
@comments = Comments.new(comments_path)
|
8
|
+
@styles = styles
|
9
|
+
@sheet = SheetDoc.new(sheet_path, @rels, @styles, shared_strings, workbook, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def cells
|
13
|
+
@cells ||= @sheet.cells(@rels)
|
14
|
+
end
|
15
|
+
|
16
|
+
def present_cells
|
17
|
+
@present_cells ||= cells.select { |_, cell| cell && cell.value }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Yield each row as array of Excelx::Cell objects
|
21
|
+
# accepts options max_rows (int) (offset by 1 for header),
|
22
|
+
# pad_cells (boolean) and offset (int)
|
23
|
+
def each_row(options = {}, &block)
|
24
|
+
row_count = 0
|
25
|
+
options[:offset] ||= 0
|
26
|
+
@sheet.each_row_streaming do |row|
|
27
|
+
break if options[:max_rows] && row_count == options[:max_rows] + options[:offset] + 1
|
28
|
+
if block_given? && !(options[:offset] && row_count < options[:offset])
|
29
|
+
block.call(cells_for_row_element(row, options))
|
30
|
+
end
|
31
|
+
row_count += 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def row(row_number)
|
36
|
+
first_column.upto(last_column).map do |col|
|
37
|
+
cells[[row_number, col]]
|
38
|
+
end.map { |cell| cell && cell.value }
|
39
|
+
end
|
40
|
+
|
41
|
+
def column(col_number)
|
42
|
+
first_row.upto(last_row).map do |row|
|
43
|
+
cells[[row, col_number]]
|
44
|
+
end.map { |cell| cell && cell.value }
|
45
|
+
end
|
46
|
+
|
47
|
+
# returns the number of the first non-empty row
|
48
|
+
def first_row
|
49
|
+
@first_row ||= present_cells.keys.map { |row, _| row }.min
|
50
|
+
end
|
51
|
+
|
52
|
+
def last_row
|
53
|
+
@last_row ||= present_cells.keys.map { |row, _| row }.max
|
54
|
+
end
|
55
|
+
|
56
|
+
# returns the number of the first non-empty column
|
57
|
+
def first_column
|
58
|
+
@first_column ||= present_cells.keys.map { |_, col| col }.min
|
59
|
+
end
|
60
|
+
|
61
|
+
# returns the number of the last non-empty column
|
62
|
+
def last_column
|
63
|
+
@last_column ||= present_cells.keys.map { |_, col| col }.max
|
64
|
+
end
|
65
|
+
|
66
|
+
def excelx_format(key)
|
67
|
+
cell = cells[key]
|
68
|
+
@styles.style_format(cell.style).to_s if cell
|
69
|
+
end
|
70
|
+
|
71
|
+
def hyperlinks
|
72
|
+
@hyperlinks ||= @sheet.hyperlinks(@rels)
|
73
|
+
end
|
74
|
+
|
75
|
+
def comments
|
76
|
+
@comments.comments
|
77
|
+
end
|
78
|
+
|
79
|
+
def dimensions
|
80
|
+
@sheet.dimensions
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# Take an xml row and return an array of Excelx::Cell objects
|
86
|
+
# optionally pad array to header width(assumed 1st row).
|
87
|
+
# takes option pad_cells (boolean) defaults false
|
88
|
+
def cells_for_row_element(row_element, options = {})
|
89
|
+
return [] unless row_element
|
90
|
+
cell_col = 0
|
91
|
+
cells = []
|
92
|
+
@sheet.each_cell(row_element) do |cell|
|
93
|
+
cells.concat(pad_cells(cell, cell_col)) if options[:pad_cells]
|
94
|
+
cells << cell
|
95
|
+
cell_col = cell.coordinate.column
|
96
|
+
end
|
97
|
+
cells
|
98
|
+
end
|
99
|
+
|
100
|
+
def pad_cells(cell, last_column)
|
101
|
+
pad = []
|
102
|
+
(cell.coordinate.column - 1 - last_column).times { pad << nil }
|
103
|
+
pad
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|