roo 2.0.1 → 2.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +17 -0
- data/.github/ISSUE_TEMPLATE +10 -0
- data/.gitignore +4 -0
- data/.travis.yml +10 -6
- data/CHANGELOG.md +116 -1
- data/Gemfile +3 -4
- data/Gemfile_ruby2 +30 -0
- data/Guardfile +1 -2
- data/README.md +56 -22
- data/Rakefile +1 -1
- data/lib/roo/base.rb +108 -245
- data/lib/roo/constants.rb +5 -0
- data/lib/roo/csv.rb +94 -87
- data/lib/roo/errors.rb +11 -0
- data/lib/roo/excelx/cell/base.rb +94 -0
- data/lib/roo/excelx/cell/boolean.rb +27 -0
- data/lib/roo/excelx/cell/date.rb +28 -0
- data/lib/roo/excelx/cell/datetime.rb +111 -0
- data/lib/roo/excelx/cell/empty.rb +19 -0
- data/lib/roo/excelx/cell/number.rb +87 -0
- data/lib/roo/excelx/cell/string.rb +19 -0
- data/lib/roo/excelx/cell/time.rb +43 -0
- data/lib/roo/excelx/cell.rb +33 -4
- data/lib/roo/excelx/comments.rb +33 -0
- data/lib/roo/excelx/coordinate.rb +12 -0
- data/lib/roo/excelx/extractor.rb +3 -4
- data/lib/roo/excelx/format.rb +64 -0
- data/lib/roo/excelx/shared.rb +32 -0
- data/lib/roo/excelx/shared_strings.rb +124 -4
- data/lib/roo/excelx/sheet.rb +12 -7
- data/lib/roo/excelx/sheet_doc.rb +108 -97
- data/lib/roo/excelx/styles.rb +1 -1
- data/lib/roo/excelx.rb +61 -103
- data/lib/roo/formatters/base.rb +15 -0
- data/lib/roo/formatters/csv.rb +84 -0
- data/lib/roo/formatters/matrix.rb +23 -0
- data/lib/roo/formatters/xml.rb +31 -0
- data/lib/roo/formatters/yaml.rb +40 -0
- data/lib/roo/libre_office.rb +1 -2
- data/lib/roo/link.rb +21 -2
- data/lib/roo/open_office.rb +468 -523
- data/lib/roo/spreadsheet.rb +3 -1
- data/lib/roo/tempdir.rb +21 -0
- data/lib/roo/utils.rb +7 -7
- data/lib/roo/version.rb +1 -1
- data/lib/roo.rb +8 -3
- data/roo.gemspec +2 -1
- data/spec/helpers.rb +5 -0
- data/spec/lib/roo/base_spec.rb +229 -0
- data/spec/lib/roo/csv_spec.rb +19 -0
- data/spec/lib/roo/excelx_spec.rb +97 -11
- data/spec/lib/roo/openoffice_spec.rb +18 -1
- data/spec/lib/roo/spreadsheet_spec.rb +20 -0
- data/spec/lib/roo/utils_spec.rb +1 -1
- data/spec/spec_helper.rb +5 -5
- data/test/all_ss.rb +12 -11
- data/test/excelx/cell/test_base.rb +63 -0
- data/test/excelx/cell/test_boolean.rb +36 -0
- data/test/excelx/cell/test_date.rb +38 -0
- data/test/excelx/cell/test_datetime.rb +45 -0
- data/test/excelx/cell/test_empty.rb +7 -0
- data/test/excelx/cell/test_number.rb +74 -0
- data/test/excelx/cell/test_string.rb +28 -0
- data/test/excelx/cell/test_time.rb +30 -0
- data/test/formatters/test_csv.rb +119 -0
- data/test/formatters/test_matrix.rb +76 -0
- data/test/formatters/test_xml.rb +78 -0
- data/test/formatters/test_yaml.rb +20 -0
- data/test/helpers/test_accessing_files.rb +60 -0
- data/test/helpers/test_comments.rb +43 -0
- data/test/helpers/test_formulas.rb +9 -0
- data/test/helpers/test_labels.rb +103 -0
- data/test/helpers/test_sheets.rb +55 -0
- data/test/helpers/test_styles.rb +62 -0
- data/test/roo/test_base.rb +182 -0
- data/test/roo/test_csv.rb +60 -0
- data/test/roo/test_excelx.rb +325 -0
- data/test/roo/test_libre_office.rb +9 -0
- data/test/roo/test_open_office.rb +289 -0
- data/test/test_helper.rb +116 -18
- data/test/test_roo.rb +362 -2088
- metadata +70 -4
- data/test/test_generic_spreadsheet.rb +0 -237
data/lib/roo/excelx/cell.rb
CHANGED
@@ -1,12 +1,22 @@
|
|
1
1
|
require 'date'
|
2
|
+
require 'roo/excelx/cell/base'
|
3
|
+
require 'roo/excelx/cell/boolean'
|
4
|
+
require 'roo/excelx/cell/datetime'
|
5
|
+
require 'roo/excelx/cell/date'
|
6
|
+
require 'roo/excelx/cell/empty'
|
7
|
+
require 'roo/excelx/cell/number'
|
8
|
+
require 'roo/excelx/cell/string'
|
9
|
+
require 'roo/excelx/cell/time'
|
2
10
|
|
3
11
|
module Roo
|
4
12
|
class Excelx
|
5
13
|
class Cell
|
6
|
-
attr_reader :
|
14
|
+
attr_reader :formula, :value, :excelx_type, :excelx_value, :style, :hyperlink, :coordinate
|
7
15
|
attr_writer :value
|
8
16
|
|
17
|
+
# DEPRECATED: Please use Cell.create_cell instead.
|
9
18
|
def initialize(value, type, formula, excelx_type, excelx_value, style, hyperlink, base_date, coordinate)
|
19
|
+
warn '[DEPRECATION] `Cell.new` is deprecated. Please use `Cell.create_cell` instead.'
|
10
20
|
@type = type
|
11
21
|
@formula = formula
|
12
22
|
@base_date = base_date if [:date, :datetime].include?(@type)
|
@@ -29,10 +39,29 @@ module Roo
|
|
29
39
|
end
|
30
40
|
end
|
31
41
|
|
42
|
+
def self.create_cell(type, *values)
|
43
|
+
case type
|
44
|
+
when :string
|
45
|
+
Cell::String.new(*values)
|
46
|
+
when :boolean
|
47
|
+
Cell::Boolean.new(*values)
|
48
|
+
when :number
|
49
|
+
Cell::Number.new(*values)
|
50
|
+
when :date
|
51
|
+
Cell::Date.new(*values)
|
52
|
+
when :datetime
|
53
|
+
Cell::DateTime.new(*values)
|
54
|
+
when :time
|
55
|
+
Cell::Time.new(*values)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Deprecated: use Roo::Excelx::Coordinate instead.
|
32
60
|
class Coordinate
|
33
61
|
attr_accessor :row, :column
|
34
62
|
|
35
63
|
def initialize(row, column)
|
64
|
+
warn '[DEPRECATION] `Roo::Excel::Cell::Coordinate` is deprecated. Please use `Roo::Excelx::Coordinate` instead.'
|
36
65
|
@row, @column = row, column
|
37
66
|
end
|
38
67
|
end
|
@@ -57,20 +86,20 @@ module Roo
|
|
57
86
|
def create_date(date)
|
58
87
|
yyyy, mm, dd = date.strftime('%Y-%m-%d').split('-')
|
59
88
|
|
60
|
-
Date.new(yyyy.to_i, mm.to_i, dd.to_i)
|
89
|
+
::Date.new(yyyy.to_i, mm.to_i, dd.to_i)
|
61
90
|
end
|
62
91
|
|
63
92
|
def create_datetime(date)
|
64
93
|
datetime_string = date.strftime('%Y-%m-%d %H:%M:%S.%N')
|
65
94
|
t = round_datetime(datetime_string)
|
66
95
|
|
67
|
-
DateTime.civil(t.year, t.month, t.day, t.hour, t.min, t.sec)
|
96
|
+
::DateTime.civil(t.year, t.month, t.day, t.hour, t.min, t.sec)
|
68
97
|
end
|
69
98
|
|
70
99
|
def round_datetime(datetime_string)
|
71
100
|
/(?<yyyy>\d+)-(?<mm>\d+)-(?<dd>\d+) (?<hh>\d+):(?<mi>\d+):(?<ss>\d+.\d+)/ =~ datetime_string
|
72
101
|
|
73
|
-
Time.new(yyyy.to_i, mm.to_i, dd.to_i, hh.to_i, mi.to_i, ss.to_r).round(0)
|
102
|
+
::Time.new(yyyy.to_i, mm.to_i, dd.to_i, hh.to_i, mi.to_i, ss.to_r).round(0)
|
74
103
|
end
|
75
104
|
end
|
76
105
|
end
|
data/lib/roo/excelx/comments.rb
CHANGED
@@ -20,3 +20,36 @@ module Roo
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
23
|
+
# xl/comments1.xml
|
24
|
+
# <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
25
|
+
# <comments xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
|
26
|
+
# <authors>
|
27
|
+
# <author />
|
28
|
+
# </authors>
|
29
|
+
# <commentList>
|
30
|
+
# <comment ref="B4" authorId="0">
|
31
|
+
# <text>
|
32
|
+
# <r>
|
33
|
+
# <rPr>
|
34
|
+
# <sz val="10" />
|
35
|
+
# <rFont val="Arial" />
|
36
|
+
# <family val="2" />
|
37
|
+
# </rPr>
|
38
|
+
# <t>Comment for B4</t>
|
39
|
+
# </r>
|
40
|
+
# </text>
|
41
|
+
# </comment>
|
42
|
+
# <comment ref="B5" authorId="0">
|
43
|
+
# <text>
|
44
|
+
# <r>
|
45
|
+
# <rPr>
|
46
|
+
# <sz val="10" />
|
47
|
+
# <rFont val="Arial" />
|
48
|
+
# <family val="2" />
|
49
|
+
# </rPr>
|
50
|
+
# <t>Comment for B5</t>
|
51
|
+
# </r>
|
52
|
+
# </text>
|
53
|
+
# </comment>
|
54
|
+
# </commentList>
|
55
|
+
# </comments>
|
data/lib/roo/excelx/extractor.rb
CHANGED
@@ -8,10 +8,9 @@ module Roo
|
|
8
8
|
private
|
9
9
|
|
10
10
|
def doc
|
11
|
-
@
|
12
|
-
|
13
|
-
|
14
|
-
end
|
11
|
+
raise FileNotFound, "#{@path} file not found" unless doc_exists?
|
12
|
+
|
13
|
+
::Roo::Utils.load_xml(@path).remove_namespaces!
|
15
14
|
end
|
16
15
|
|
17
16
|
def doc_exists?
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Roo
|
2
|
+
class Excelx
|
3
|
+
module Format
|
4
|
+
EXCEPTIONAL_FORMATS = {
|
5
|
+
'h:mm am/pm' => :date,
|
6
|
+
'h:mm:ss am/pm' => :date
|
7
|
+
}
|
8
|
+
|
9
|
+
STANDARD_FORMATS = {
|
10
|
+
0 => 'General'.freeze,
|
11
|
+
1 => '0'.freeze,
|
12
|
+
2 => '0.00'.freeze,
|
13
|
+
3 => '#,##0'.freeze,
|
14
|
+
4 => '#,##0.00'.freeze,
|
15
|
+
9 => '0%'.freeze,
|
16
|
+
10 => '0.00%'.freeze,
|
17
|
+
11 => '0.00E+00'.freeze,
|
18
|
+
12 => '# ?/?'.freeze,
|
19
|
+
13 => '# ??/??'.freeze,
|
20
|
+
14 => 'mm-dd-yy'.freeze,
|
21
|
+
15 => 'd-mmm-yy'.freeze,
|
22
|
+
16 => 'd-mmm'.freeze,
|
23
|
+
17 => 'mmm-yy'.freeze,
|
24
|
+
18 => 'h:mm AM/PM'.freeze,
|
25
|
+
19 => 'h:mm:ss AM/PM'.freeze,
|
26
|
+
20 => 'h:mm'.freeze,
|
27
|
+
21 => 'h:mm:ss'.freeze,
|
28
|
+
22 => 'm/d/yy h:mm'.freeze,
|
29
|
+
37 => '#,##0 ;(#,##0)'.freeze,
|
30
|
+
38 => '#,##0 ;[Red](#,##0)'.freeze,
|
31
|
+
39 => '#,##0.00;(#,##0.00)'.freeze,
|
32
|
+
40 => '#,##0.00;[Red](#,##0.00)'.freeze,
|
33
|
+
45 => 'mm:ss'.freeze,
|
34
|
+
46 => '[h]:mm:ss'.freeze,
|
35
|
+
47 => 'mmss.0'.freeze,
|
36
|
+
48 => '##0.0E+0'.freeze,
|
37
|
+
49 => '@'.freeze
|
38
|
+
}
|
39
|
+
|
40
|
+
def to_type(format)
|
41
|
+
format = format.to_s.downcase
|
42
|
+
if (type = EXCEPTIONAL_FORMATS[format])
|
43
|
+
type
|
44
|
+
elsif format.include?('#')
|
45
|
+
:float
|
46
|
+
elsif !format.match(/d+(?![\]])/).nil? || format.include?('y')
|
47
|
+
if format.include?('h') || format.include?('s')
|
48
|
+
:datetime
|
49
|
+
else
|
50
|
+
:date
|
51
|
+
end
|
52
|
+
elsif format.include?('h') || format.include?('s')
|
53
|
+
:time
|
54
|
+
elsif format.include?('%')
|
55
|
+
:percentage
|
56
|
+
else
|
57
|
+
:float
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
module_function :to_type
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Roo
|
2
|
+
class Excelx
|
3
|
+
# Public: Shared class for allowing sheets to share data. This should
|
4
|
+
# reduce memory usage and reduce the number of objects being passed
|
5
|
+
# to various inititializers.
|
6
|
+
class Shared
|
7
|
+
attr_accessor :comments_files, :sheet_files, :rels_files
|
8
|
+
def initialize(dir)
|
9
|
+
@dir = dir
|
10
|
+
@comments_files = []
|
11
|
+
@sheet_files = []
|
12
|
+
@rels_files = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def styles
|
16
|
+
@styles ||= Styles.new(File.join(@dir, 'roo_styles.xml'))
|
17
|
+
end
|
18
|
+
|
19
|
+
def shared_strings
|
20
|
+
@shared_strings ||= SharedStrings.new(File.join(@dir, 'roo_sharedStrings.xml'))
|
21
|
+
end
|
22
|
+
|
23
|
+
def workbook
|
24
|
+
@workbook ||= Workbook.new(File.join(@dir, 'roo_workbook.xml'))
|
25
|
+
end
|
26
|
+
|
27
|
+
def base_date
|
28
|
+
workbook.base_date
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -3,6 +3,14 @@ require 'roo/excelx/extractor'
|
|
3
3
|
module Roo
|
4
4
|
class Excelx
|
5
5
|
class SharedStrings < Excelx::Extractor
|
6
|
+
|
7
|
+
COMMON_STRINGS = {
|
8
|
+
t: "t",
|
9
|
+
r: "r",
|
10
|
+
html_tag_open: "<html>",
|
11
|
+
html_tag_closed: "</html>"
|
12
|
+
}
|
13
|
+
|
6
14
|
def [](index)
|
7
15
|
to_a[index]
|
8
16
|
end
|
@@ -11,13 +19,32 @@ module Roo
|
|
11
19
|
@array ||= extract_shared_strings
|
12
20
|
end
|
13
21
|
|
22
|
+
def to_html
|
23
|
+
@html ||= extract_html
|
24
|
+
end
|
25
|
+
|
26
|
+
# Use to_html or to_a for html returns
|
27
|
+
# See what is happening with commit???
|
28
|
+
def use_html?(index)
|
29
|
+
to_html[index][/<([biu]|sup|sub)>/]
|
30
|
+
end
|
31
|
+
|
14
32
|
private
|
15
33
|
|
34
|
+
def fix_invalid_shared_strings(doc)
|
35
|
+
invalid = { '_x000D_' => "\n" }
|
36
|
+
xml = doc.to_s
|
37
|
+
return doc unless xml[/#{invalid.keys.join('|')}/]
|
38
|
+
|
39
|
+
::Nokogiri::XML(xml.gsub(/#{invalid.keys.join('|')}/, invalid))
|
40
|
+
end
|
41
|
+
|
16
42
|
def extract_shared_strings
|
17
43
|
return [] unless doc_exists?
|
18
44
|
|
45
|
+
document = fix_invalid_shared_strings(doc)
|
19
46
|
# read the shared strings xml document
|
20
|
-
|
47
|
+
document.xpath('/sst/si').map do |si|
|
21
48
|
shared_string = ''
|
22
49
|
si.children.each do |elem|
|
23
50
|
case elem.name
|
@@ -32,6 +59,99 @@ module Roo
|
|
32
59
|
shared_string
|
33
60
|
end
|
34
61
|
end
|
35
|
-
|
36
|
-
|
37
|
-
|
62
|
+
|
63
|
+
def extract_html
|
64
|
+
return [] unless doc_exists?
|
65
|
+
fix_invalid_shared_strings(doc)
|
66
|
+
# read the shared strings xml document
|
67
|
+
doc.xpath('/sst/si').map do |si|
|
68
|
+
html_string = '<html>'
|
69
|
+
si.children.each do |elem|
|
70
|
+
case elem.name
|
71
|
+
when 'r'
|
72
|
+
html_string << extract_html_r(elem)
|
73
|
+
when 't'
|
74
|
+
html_string << elem.content
|
75
|
+
end # case elem.name
|
76
|
+
end # si.children.each do |elem|
|
77
|
+
html_string << '</html>'
|
78
|
+
end # doc.xpath('/sst/si').map do |si|
|
79
|
+
end # def extract_html
|
80
|
+
|
81
|
+
# The goal of this function is to take the following XML code snippet and create a html tag
|
82
|
+
# r_elem ::: XML Element that is in sharedStrings.xml of excel_book.xlsx
|
83
|
+
# {code:xml}
|
84
|
+
# <r>
|
85
|
+
# <rPr>
|
86
|
+
# <i/>
|
87
|
+
# <b/>
|
88
|
+
# <u/>
|
89
|
+
# <vertAlign val="subscript"/>
|
90
|
+
# <vertAlign val="superscript"/>
|
91
|
+
# </rPr>
|
92
|
+
# <t>TEXT</t>
|
93
|
+
# </r>
|
94
|
+
# {code}
|
95
|
+
#
|
96
|
+
# Expected Output ::: "<html><sub|sup><b><i><u>TEXT</u></i></b></sub|/sup></html>"
|
97
|
+
def extract_html_r(r_elem)
|
98
|
+
str = ''
|
99
|
+
xml_elems = {
|
100
|
+
sub: false,
|
101
|
+
sup: false,
|
102
|
+
b: false,
|
103
|
+
i: false,
|
104
|
+
u: false
|
105
|
+
}
|
106
|
+
b, i, u, sub, sup = false, false, false, false, false
|
107
|
+
r_elem.children.each do |elem|
|
108
|
+
case elem.name
|
109
|
+
when 'rPr'
|
110
|
+
elem.children.each do |rPr_elem|
|
111
|
+
case rPr_elem.name
|
112
|
+
when 'b'
|
113
|
+
# set formatting for Bold to true
|
114
|
+
xml_elems[:b] = true
|
115
|
+
when 'i'
|
116
|
+
# set formatting for Italics to true
|
117
|
+
xml_elems[:i] = true
|
118
|
+
when 'u'
|
119
|
+
# set formatting for Underline to true
|
120
|
+
xml_elems[:u] = true
|
121
|
+
when 'vertAlign'
|
122
|
+
# See if the Vertical Alignment is subscript or superscript
|
123
|
+
case rPr_elem.xpath('@val').first.value
|
124
|
+
when 'subscript'
|
125
|
+
# set formatting for Subscript to true and Superscript to false ... Can't have both
|
126
|
+
xml_elems[:sub] = true
|
127
|
+
xml_elems[:sup] = false
|
128
|
+
when 'superscript'
|
129
|
+
# set formatting for Superscript to true and Subscript to false ... Can't have both
|
130
|
+
xml_elems[:sup] = true
|
131
|
+
xml_elems[:sub] = false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
when 't'
|
136
|
+
str << create_html(elem.content, xml_elems)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
str
|
140
|
+
end # extract_html_r
|
141
|
+
|
142
|
+
# This will return an html string
|
143
|
+
def create_html(text, formatting)
|
144
|
+
tmp_str = ''
|
145
|
+
formatting.each do |elem, val|
|
146
|
+
tmp_str << "<#{elem}>" if val
|
147
|
+
end
|
148
|
+
tmp_str << text
|
149
|
+
reverse_format = Hash[formatting.to_a.reverse]
|
150
|
+
reverse_format.each do |elem, val|
|
151
|
+
tmp_str << "</#{elem}>" if val
|
152
|
+
end
|
153
|
+
tmp_str
|
154
|
+
end
|
155
|
+
end # class SharedStrings < Excelx::Extractor
|
156
|
+
end # class Excelx
|
157
|
+
end # module Roo
|
data/lib/roo/excelx/sheet.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
|
+
require 'forwardable'
|
1
2
|
module Roo
|
2
3
|
class Excelx
|
3
4
|
class Sheet
|
4
|
-
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files] => :@shared
|
8
|
+
|
9
|
+
def initialize(name, shared, sheet_index, options = {})
|
5
10
|
@name = name
|
6
|
-
@
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@sheet = SheetDoc.new(
|
11
|
+
@shared = shared
|
12
|
+
@rels = Relationships.new(rels_files[sheet_index])
|
13
|
+
@comments = Comments.new(comments_files[sheet_index])
|
14
|
+
@sheet = SheetDoc.new(sheet_files[sheet_index], @rels, shared, options)
|
10
15
|
end
|
11
16
|
|
12
17
|
def cells
|
@@ -14,7 +19,7 @@ module Roo
|
|
14
19
|
end
|
15
20
|
|
16
21
|
def present_cells
|
17
|
-
@present_cells ||= cells.select { |_, cell| cell && cell.
|
22
|
+
@present_cells ||= cells.select { |_, cell| cell && !cell.empty? }
|
18
23
|
end
|
19
24
|
|
20
25
|
# Yield each row as array of Excelx::Cell objects
|
@@ -65,7 +70,7 @@ module Roo
|
|
65
70
|
|
66
71
|
def excelx_format(key)
|
67
72
|
cell = cells[key]
|
68
|
-
|
73
|
+
styles.style_format(cell.style).to_s if cell
|
69
74
|
end
|
70
75
|
|
71
76
|
def hyperlinks
|
data/lib/roo/excelx/sheet_doc.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
require 'forwardable'
|
1
2
|
require 'roo/excelx/extractor'
|
2
3
|
|
3
4
|
module Roo
|
4
5
|
class Excelx
|
5
6
|
class SheetDoc < Excelx::Extractor
|
6
|
-
|
7
|
+
extend Forwardable
|
8
|
+
delegate [:styles, :workbook, :shared_strings, :base_date] => :@shared
|
9
|
+
|
10
|
+
def initialize(path, relationships, shared, options = {})
|
7
11
|
super(path)
|
12
|
+
@shared = shared
|
8
13
|
@options = options
|
9
14
|
@relationships = relationships
|
10
|
-
@styles = styles
|
11
|
-
@shared_strings = shared_strings
|
12
|
-
@workbook = workbook
|
13
15
|
end
|
14
16
|
|
15
17
|
def cells(relationships)
|
@@ -37,83 +39,133 @@ module Roo
|
|
37
39
|
def each_cell(row_xml)
|
38
40
|
return [] unless row_xml
|
39
41
|
row_xml.children.each do |cell_element|
|
40
|
-
|
41
|
-
|
42
|
+
# If you're sure you're not going to need this hyperlinks you can discard it
|
43
|
+
hyperlinks = unless @options[:no_hyperlinks]
|
44
|
+
key = ::Roo::Utils.ref_to_key(cell_element['r'])
|
45
|
+
hyperlinks(@relationships)[key]
|
46
|
+
end
|
47
|
+
|
48
|
+
yield cell_from_xml(cell_element, hyperlinks)
|
42
49
|
end
|
43
50
|
end
|
44
51
|
|
45
52
|
private
|
46
53
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
style = cell_xml['s'].to_i # should be here
|
51
|
-
# c: <c r="A5" s="2">
|
52
|
-
# <v>22606</v>
|
53
|
-
# </c>, format: , tmp_type: float
|
54
|
-
value_type =
|
55
|
-
case cell_xml['t']
|
56
|
-
when 's'
|
54
|
+
def cell_value_type(type, format)
|
55
|
+
case type
|
56
|
+
when 's'.freeze
|
57
57
|
:shared
|
58
|
-
when 'b'
|
58
|
+
when 'b'.freeze
|
59
59
|
:boolean
|
60
|
-
when 'str'
|
60
|
+
when 'str'.freeze
|
61
61
|
:string
|
62
|
-
when 'inlineStr'
|
62
|
+
when 'inlineStr'.freeze
|
63
63
|
:inlinestr
|
64
64
|
else
|
65
|
-
format = @styles.style_format(style)
|
66
65
|
Excelx::Format.to_type(format)
|
67
66
|
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Internal: Creates a cell based on an XML clell..
|
70
|
+
#
|
71
|
+
# cell_xml - a Nokogiri::XML::Element. e.g.
|
72
|
+
# <c r="A5" s="2">
|
73
|
+
# <v>22606</v>
|
74
|
+
# </c>
|
75
|
+
# hyperlink - a String for the hyperlink for the cell or nil when no
|
76
|
+
# hyperlink is present.
|
77
|
+
#
|
78
|
+
# Examples
|
79
|
+
#
|
80
|
+
# cells_from_xml(<Nokogiri::XML::Element>, nil)
|
81
|
+
# # => <Excelx::Cell::String>
|
82
|
+
#
|
83
|
+
# Returns a type of <Excelx::Cell>.
|
84
|
+
def cell_from_xml(cell_xml, hyperlink)
|
85
|
+
coordinate = extract_coordinate(cell_xml['r'])
|
86
|
+
return Excelx::Cell::Empty.new(coordinate) if cell_xml.children.empty?
|
87
|
+
|
88
|
+
# NOTE: This is error prone, to_i will silently turn a nil into a 0.
|
89
|
+
# This works by coincidence because Format[0] is General.
|
90
|
+
style = cell_xml['s'].to_i
|
91
|
+
format = styles.style_format(style)
|
92
|
+
value_type = cell_value_type(cell_xml['t'], format)
|
68
93
|
formula = nil
|
69
|
-
|
94
|
+
|
70
95
|
cell_xml.children.each do |cell|
|
71
96
|
case cell.name
|
72
97
|
when 'is'
|
73
|
-
cell.
|
74
|
-
|
75
|
-
|
76
|
-
end
|
98
|
+
content_arr = cell.search('t').map(&:content)
|
99
|
+
unless content_arr.empty?
|
100
|
+
return Excelx::Cell.create_cell(:string, content_arr.join(''), formula, style, hyperlink, coordinate)
|
77
101
|
end
|
78
102
|
when 'f'
|
79
103
|
formula = cell.content
|
80
104
|
when 'v'
|
81
|
-
|
82
|
-
value_type =
|
83
|
-
if (cell.content.to_f - cell.content.to_f.floor).abs > 0.000001
|
84
|
-
:datetime
|
85
|
-
else
|
86
|
-
:date
|
87
|
-
end
|
88
|
-
end
|
89
|
-
excelx_type = [:numeric_or_formula, format.to_s]
|
90
|
-
value =
|
91
|
-
case value_type
|
92
|
-
when :shared
|
93
|
-
value_type = :string
|
94
|
-
excelx_type = :string
|
95
|
-
@shared_strings[cell.content.to_i]
|
96
|
-
when :boolean
|
97
|
-
(cell.content.to_i == 1 ? 'TRUE' : 'FALSE')
|
98
|
-
when :date, :time, :datetime
|
99
|
-
cell.content
|
100
|
-
when :formula
|
101
|
-
cell.content.to_f
|
102
|
-
when :string
|
103
|
-
excelx_type = :string
|
104
|
-
cell.content
|
105
|
-
else
|
106
|
-
value_type = :float
|
107
|
-
cell.content
|
108
|
-
end
|
109
|
-
return Excelx::Cell.new(value, value_type, formula, excelx_type, cell.content, style, hyperlink, @workbook.base_date, Excelx::Cell::Coordinate.new(row, column))
|
105
|
+
return create_cell_from_value(value_type, cell, formula, format, style, hyperlink, base_date, coordinate)
|
110
106
|
end
|
111
107
|
end
|
112
|
-
|
108
|
+
|
109
|
+
Excelx::Cell::Empty.new(coordinate)
|
110
|
+
end
|
111
|
+
|
112
|
+
def create_cell_from_value(value_type, cell, formula, format, style, hyperlink, base_date, coordinate)
|
113
|
+
# NOTE: format.to_s can replace excelx_type as an argument for
|
114
|
+
# Cell::Time, Cell::DateTime, Cell::Date or Cell::Number, but
|
115
|
+
# it will break some brittle tests.
|
116
|
+
excelx_type = [:numeric_or_formula, format.to_s]
|
117
|
+
|
118
|
+
# NOTE: There are only a few situations where value != cell.content
|
119
|
+
# 1. when a sharedString is used. value = sharedString;
|
120
|
+
# cell.content = id of sharedString
|
121
|
+
# 2. boolean cells: value = 'TRUE' | 'FALSE'; cell.content = '0' | '1';
|
122
|
+
# But a boolean cell should use TRUE|FALSE as the formatted value
|
123
|
+
# and use a Boolean for it's value. Using a Boolean value breaks
|
124
|
+
# Roo::Base#to_csv.
|
125
|
+
# 3. formula
|
126
|
+
case value_type
|
127
|
+
when :shared
|
128
|
+
value = shared_strings.use_html?(cell.content.to_i) ? shared_strings.to_html[cell.content.to_i] : shared_strings[cell.content.to_i]
|
129
|
+
Excelx::Cell.create_cell(:string, value, formula, style, hyperlink, coordinate)
|
130
|
+
when :boolean, :string
|
131
|
+
value = cell.content
|
132
|
+
Excelx::Cell.create_cell(value_type, value, formula, style, hyperlink, coordinate)
|
133
|
+
when :time, :datetime
|
134
|
+
cell_content = cell.content.to_f
|
135
|
+
# NOTE: A date will be a whole number. A time will have be > 1. And
|
136
|
+
# in general, a datetime will have decimals. But if the cell is
|
137
|
+
# using a custom format, it's possible to be interpreted incorrectly.
|
138
|
+
# cell_content.to_i == cell_content && standard_style?=> :date
|
139
|
+
#
|
140
|
+
# Should check to see if the format is standard or not. If it's a
|
141
|
+
# standard format, than it's a date, otherwise, it is a datetime.
|
142
|
+
# @styles.standard_style?(style_id)
|
143
|
+
# STANDARD_STYLES.keys.include?(style_id.to_i)
|
144
|
+
cell_type = if cell_content < 1.0
|
145
|
+
:time
|
146
|
+
elsif (cell_content - cell_content.floor).abs > 0.000001
|
147
|
+
:datetime
|
148
|
+
else
|
149
|
+
:date
|
150
|
+
end
|
151
|
+
Excelx::Cell.create_cell(cell_type, cell.content, formula, excelx_type, style, hyperlink, base_date, coordinate)
|
152
|
+
when :date
|
153
|
+
Excelx::Cell.create_cell(value_type, cell.content, formula, excelx_type, style, hyperlink, base_date, coordinate)
|
154
|
+
else
|
155
|
+
Excelx::Cell.create_cell(:number, cell.content, formula, excelx_type, style, hyperlink, coordinate)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def extract_coordinate(coordinate)
|
160
|
+
row, column = ::Roo::Utils.split_coordinate(coordinate)
|
161
|
+
|
162
|
+
Excelx::Coordinate.new(row, column)
|
113
163
|
end
|
114
164
|
|
115
165
|
def extract_hyperlinks(relationships)
|
116
|
-
|
166
|
+
return {} unless (hyperlinks = doc.xpath('/worksheet/hyperlinks/hyperlink'))
|
167
|
+
|
168
|
+
Hash[hyperlinks.map do |hyperlink|
|
117
169
|
if hyperlink.attribute('id') && (relationship = relationships[hyperlink.attribute('id').text])
|
118
170
|
[::Roo::Utils.ref_to_key(hyperlink.attributes['ref'].to_s), relationship.attribute('Target').text]
|
119
171
|
end
|
@@ -154,47 +206,6 @@ module Roo
|
|
154
206
|
return dimension.attributes['ref'].value
|
155
207
|
end
|
156
208
|
end
|
157
|
-
|
158
|
-
=begin
|
159
|
-
Datei xl/comments1.xml
|
160
|
-
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
161
|
-
<comments xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
|
162
|
-
<authors>
|
163
|
-
<author />
|
164
|
-
</authors>
|
165
|
-
<commentList>
|
166
|
-
<comment ref="B4" authorId="0">
|
167
|
-
<text>
|
168
|
-
<r>
|
169
|
-
<rPr>
|
170
|
-
<sz val="10" />
|
171
|
-
<rFont val="Arial" />
|
172
|
-
<family val="2" />
|
173
|
-
</rPr>
|
174
|
-
<t>Kommentar fuer B4</t>
|
175
|
-
</r>
|
176
|
-
</text>
|
177
|
-
</comment>
|
178
|
-
<comment ref="B5" authorId="0">
|
179
|
-
<text>
|
180
|
-
<r>
|
181
|
-
<rPr>
|
182
|
-
<sz val="10" />
|
183
|
-
<rFont val="Arial" />
|
184
|
-
<family val="2" />
|
185
|
-
</rPr>
|
186
|
-
<t>Kommentar fuer B5</t>
|
187
|
-
</r>
|
188
|
-
</text>
|
189
|
-
</comment>
|
190
|
-
</commentList>
|
191
|
-
</comments>
|
192
|
-
=end
|
193
|
-
=begin
|
194
|
-
if @comments_doc[self.sheets.index(sheet)]
|
195
|
-
read_comments(sheet)
|
196
|
-
end
|
197
|
-
=end
|
198
209
|
end
|
199
210
|
end
|
200
211
|
end
|