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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -0
  3. data/.github/ISSUE_TEMPLATE +10 -0
  4. data/.gitignore +4 -0
  5. data/.travis.yml +10 -6
  6. data/CHANGELOG.md +116 -1
  7. data/Gemfile +3 -4
  8. data/Gemfile_ruby2 +30 -0
  9. data/Guardfile +1 -2
  10. data/README.md +56 -22
  11. data/Rakefile +1 -1
  12. data/lib/roo/base.rb +108 -245
  13. data/lib/roo/constants.rb +5 -0
  14. data/lib/roo/csv.rb +94 -87
  15. data/lib/roo/errors.rb +11 -0
  16. data/lib/roo/excelx/cell/base.rb +94 -0
  17. data/lib/roo/excelx/cell/boolean.rb +27 -0
  18. data/lib/roo/excelx/cell/date.rb +28 -0
  19. data/lib/roo/excelx/cell/datetime.rb +111 -0
  20. data/lib/roo/excelx/cell/empty.rb +19 -0
  21. data/lib/roo/excelx/cell/number.rb +87 -0
  22. data/lib/roo/excelx/cell/string.rb +19 -0
  23. data/lib/roo/excelx/cell/time.rb +43 -0
  24. data/lib/roo/excelx/cell.rb +33 -4
  25. data/lib/roo/excelx/comments.rb +33 -0
  26. data/lib/roo/excelx/coordinate.rb +12 -0
  27. data/lib/roo/excelx/extractor.rb +3 -4
  28. data/lib/roo/excelx/format.rb +64 -0
  29. data/lib/roo/excelx/shared.rb +32 -0
  30. data/lib/roo/excelx/shared_strings.rb +124 -4
  31. data/lib/roo/excelx/sheet.rb +12 -7
  32. data/lib/roo/excelx/sheet_doc.rb +108 -97
  33. data/lib/roo/excelx/styles.rb +1 -1
  34. data/lib/roo/excelx.rb +61 -103
  35. data/lib/roo/formatters/base.rb +15 -0
  36. data/lib/roo/formatters/csv.rb +84 -0
  37. data/lib/roo/formatters/matrix.rb +23 -0
  38. data/lib/roo/formatters/xml.rb +31 -0
  39. data/lib/roo/formatters/yaml.rb +40 -0
  40. data/lib/roo/libre_office.rb +1 -2
  41. data/lib/roo/link.rb +21 -2
  42. data/lib/roo/open_office.rb +468 -523
  43. data/lib/roo/spreadsheet.rb +3 -1
  44. data/lib/roo/tempdir.rb +21 -0
  45. data/lib/roo/utils.rb +7 -7
  46. data/lib/roo/version.rb +1 -1
  47. data/lib/roo.rb +8 -3
  48. data/roo.gemspec +2 -1
  49. data/spec/helpers.rb +5 -0
  50. data/spec/lib/roo/base_spec.rb +229 -0
  51. data/spec/lib/roo/csv_spec.rb +19 -0
  52. data/spec/lib/roo/excelx_spec.rb +97 -11
  53. data/spec/lib/roo/openoffice_spec.rb +18 -1
  54. data/spec/lib/roo/spreadsheet_spec.rb +20 -0
  55. data/spec/lib/roo/utils_spec.rb +1 -1
  56. data/spec/spec_helper.rb +5 -5
  57. data/test/all_ss.rb +12 -11
  58. data/test/excelx/cell/test_base.rb +63 -0
  59. data/test/excelx/cell/test_boolean.rb +36 -0
  60. data/test/excelx/cell/test_date.rb +38 -0
  61. data/test/excelx/cell/test_datetime.rb +45 -0
  62. data/test/excelx/cell/test_empty.rb +7 -0
  63. data/test/excelx/cell/test_number.rb +74 -0
  64. data/test/excelx/cell/test_string.rb +28 -0
  65. data/test/excelx/cell/test_time.rb +30 -0
  66. data/test/formatters/test_csv.rb +119 -0
  67. data/test/formatters/test_matrix.rb +76 -0
  68. data/test/formatters/test_xml.rb +78 -0
  69. data/test/formatters/test_yaml.rb +20 -0
  70. data/test/helpers/test_accessing_files.rb +60 -0
  71. data/test/helpers/test_comments.rb +43 -0
  72. data/test/helpers/test_formulas.rb +9 -0
  73. data/test/helpers/test_labels.rb +103 -0
  74. data/test/helpers/test_sheets.rb +55 -0
  75. data/test/helpers/test_styles.rb +62 -0
  76. data/test/roo/test_base.rb +182 -0
  77. data/test/roo/test_csv.rb +60 -0
  78. data/test/roo/test_excelx.rb +325 -0
  79. data/test/roo/test_libre_office.rb +9 -0
  80. data/test/roo/test_open_office.rb +289 -0
  81. data/test/test_helper.rb +116 -18
  82. data/test/test_roo.rb +362 -2088
  83. metadata +70 -4
  84. data/test/test_generic_spreadsheet.rb +0 -237
@@ -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 :type, :formula, :value, :excelx_type, :excelx_value, :style, :hyperlink, :coordinate
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
@@ -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>
@@ -0,0 +1,12 @@
1
+ module Roo
2
+ class Excelx
3
+ class Coordinate
4
+ attr_accessor :row, :column
5
+
6
+ def initialize(row, column)
7
+ @row = row
8
+ @column = column
9
+ end
10
+ end
11
+ end
12
+ end
@@ -8,10 +8,9 @@ module Roo
8
8
  private
9
9
 
10
10
  def doc
11
- @doc ||=
12
- if doc_exists?
13
- ::Roo::Utils.load_xml(@path).remove_namespaces!
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
- doc.xpath('/sst/si').map do |si|
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
- end
36
- end
37
- end
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
@@ -1,12 +1,17 @@
1
+ require 'forwardable'
1
2
  module Roo
2
3
  class Excelx
3
4
  class Sheet
4
- def initialize(name, rels_path, sheet_path, comments_path, styles, shared_strings, workbook, options = {})
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
- @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)
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.value }
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
- @styles.style_format(cell.style).to_s if cell
73
+ styles.style_format(cell.style).to_s if cell
69
74
  end
70
75
 
71
76
  def hyperlinks
@@ -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
- def initialize(path, relationships, styles, shared_strings, workbook, options = {})
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
- key = ::Roo::Utils.ref_to_key(cell_element['r'])
41
- yield cell_from_xml(cell_element, hyperlinks(@relationships)[key])
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 cell_from_xml(cell_xml, hyperlink)
48
- # This is error prone, to_i will silently turn a nil into a 0
49
- # and it works by coincidence that Format[0] is general
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
- row, column = ::Roo::Utils.split_coordinate(cell_xml['r'])
94
+
70
95
  cell_xml.children.each do |cell|
71
96
  case cell.name
72
97
  when 'is'
73
- cell.children.each do |inline_str|
74
- if inline_str.name == 't'
75
- return Excelx::Cell.new(inline_str.content, :string, formula, :string, inline_str.content, style, hyperlink, @workbook.base_date, Excelx::Cell::Coordinate.new(row, column))
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
- if [:time, :datetime].include?(value_type) && cell.content.to_f >= 1.0
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
- Excelx::Cell.new(nil, nil, nil, nil, nil, nil, nil, nil, Excelx::Cell::Coordinate.new(row, column))
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
- Hash[doc.xpath('/worksheet/hyperlinks/hyperlink').map do |hyperlink|
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