roo 2.0.1 → 2.7.1

Sign up to get free protection for your applications and to get access to all the features.
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