roo 2.0.1 → 2.7.1
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 +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
|