roo 2.7.0 → 2.8.3
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 +5 -5
- data/.github/issue_template.md +16 -0
- data/.github/pull_request_template.md +14 -0
- data/.rubocop.yml +186 -0
- data/.travis.yml +12 -7
- data/CHANGELOG.md +53 -2
- data/LICENSE +2 -0
- data/README.md +29 -13
- data/lib/roo/base.rb +69 -61
- data/lib/roo/constants.rb +5 -3
- data/lib/roo/csv.rb +20 -12
- data/lib/roo/excelx/cell/base.rb +26 -12
- data/lib/roo/excelx/cell/boolean.rb +9 -6
- data/lib/roo/excelx/cell/date.rb +7 -7
- data/lib/roo/excelx/cell/datetime.rb +14 -18
- data/lib/roo/excelx/cell/empty.rb +3 -2
- data/lib/roo/excelx/cell/number.rb +35 -34
- data/lib/roo/excelx/cell/string.rb +3 -3
- data/lib/roo/excelx/cell/time.rb +4 -3
- data/lib/roo/excelx/cell.rb +10 -6
- data/lib/roo/excelx/comments.rb +3 -3
- data/lib/roo/excelx/coordinate.rb +11 -4
- data/lib/roo/excelx/extractor.rb +21 -3
- data/lib/roo/excelx/format.rb +38 -31
- data/lib/roo/excelx/images.rb +26 -0
- data/lib/roo/excelx/relationships.rb +12 -4
- data/lib/roo/excelx/shared.rb +10 -3
- data/lib/roo/excelx/shared_strings.rb +9 -15
- data/lib/roo/excelx/sheet.rb +49 -10
- data/lib/roo/excelx/sheet_doc.rb +89 -48
- data/lib/roo/excelx/styles.rb +3 -3
- data/lib/roo/excelx/workbook.rb +7 -3
- data/lib/roo/excelx.rb +42 -16
- data/lib/roo/helpers/default_attr_reader.rb +20 -0
- data/lib/roo/helpers/weak_instance_cache.rb +41 -0
- data/lib/roo/open_office.rb +8 -6
- data/lib/roo/spreadsheet.rb +1 -1
- data/lib/roo/utils.rb +70 -20
- data/lib/roo/version.rb +1 -1
- data/lib/roo.rb +4 -1
- data/roo.gemspec +13 -11
- data/spec/lib/roo/base_spec.rb +45 -3
- data/spec/lib/roo/excelx/relationships_spec.rb +43 -0
- data/spec/lib/roo/excelx/sheet_doc_spec.rb +11 -0
- data/spec/lib/roo/excelx_spec.rb +150 -31
- data/spec/lib/roo/strict_spec.rb +43 -0
- data/spec/lib/roo/utils_spec.rb +25 -3
- data/spec/lib/roo/weak_instance_cache_spec.rb +92 -0
- data/spec/lib/roo_spec.rb +0 -0
- data/spec/spec_helper.rb +1 -1
- data/test/excelx/cell/test_attr_reader_default.rb +72 -0
- data/test/excelx/cell/test_base.rb +5 -0
- data/test/excelx/cell/test_datetime.rb +6 -6
- data/test/excelx/cell/test_empty.rb +11 -0
- data/test/excelx/cell/test_number.rb +9 -0
- data/test/excelx/cell/test_string.rb +20 -0
- data/test/excelx/cell/test_time.rb +4 -4
- data/test/excelx/test_coordinate.rb +51 -0
- data/test/formatters/test_csv.rb +19 -2
- data/test/formatters/test_xml.rb +13 -9
- 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 +37 -1
- data/test/roo/test_excelx.rb +157 -13
- data/test/roo/test_open_office.rb +196 -33
- data/test/test_helper.rb +66 -22
- data/test/test_roo.rb +32 -881
- metadata +32 -14
- data/.github/ISSUE_TEMPLATE +0 -10
- data/Gemfile_ruby2 +0 -30
@@ -1,16 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Roo
|
2
4
|
class Excelx
|
3
5
|
class Cell
|
4
6
|
class Number < Cell::Base
|
5
|
-
attr_reader :value, :formula, :format, :cell_value, :
|
7
|
+
attr_reader :value, :formula, :format, :cell_value, :coordinate
|
8
|
+
|
9
|
+
# FIXME: change default_type to number. This will break brittle tests.
|
10
|
+
attr_reader_with_default default_type: :float
|
6
11
|
|
7
12
|
def initialize(value, formula, excelx_type, style, link, coordinate)
|
8
13
|
super
|
9
|
-
# FIXME: change @type to number. This will break brittle tests.
|
10
14
|
# FIXME: Excelx_type is an array, but the first value isn't used.
|
11
|
-
@type = :float
|
12
15
|
@format = excelx_type.last
|
13
|
-
@value = link
|
16
|
+
@value = link ? Roo::Link.new(link, value) : create_numeric(value)
|
14
17
|
end
|
15
18
|
|
16
19
|
def create_numeric(number)
|
@@ -21,48 +24,50 @@ module Roo
|
|
21
24
|
when /\.0/
|
22
25
|
Float(number)
|
23
26
|
else
|
24
|
-
(number.include?('.') || (/\A[-+]?\d+E[-+]
|
27
|
+
(number.include?('.') || (/\A[-+]?\d+E[-+]?\d+\z/i =~ number)) ? Float(number) : Integer(number, 10)
|
25
28
|
end
|
26
29
|
end
|
27
30
|
|
28
31
|
def formatted_value
|
29
32
|
return @cell_value if Excelx::ERROR_VALUES.include?(@cell_value)
|
30
33
|
|
31
|
-
formatter =
|
34
|
+
formatter = generate_formatter(@format)
|
32
35
|
if formatter.is_a? Proc
|
33
36
|
formatter.call(@cell_value)
|
34
|
-
elsif zero_padded_number?
|
35
|
-
"%0#{@format.size}d" % @cell_value
|
36
37
|
else
|
37
38
|
Kernel.format(formatter, @cell_value)
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
|
-
def
|
42
|
+
def generate_formatter(format)
|
42
43
|
# FIXME: numbers can be other colors besides red:
|
43
44
|
# [BLACK], [BLUE], [CYAN], [GREEN], [MAGENTA], [RED], [WHITE], [YELLOW], [COLOR n]
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
45
|
+
case format
|
46
|
+
when /^General$/i then '%.0f'
|
47
|
+
when '0' then '%.0f'
|
48
|
+
when /^(0+)$/ then "%0#{$1.size}d"
|
49
|
+
when /^0\.(0+)$/ then "%.#{$1.size}f"
|
50
|
+
when '#,##0' then number_format('%.0f')
|
51
|
+
when '#,##0.00' then number_format('%.2f')
|
52
|
+
when '0%'
|
53
|
+
proc do |number|
|
54
|
+
Kernel.format('%d%%', number.to_f * 100)
|
55
|
+
end
|
56
|
+
when '0.00%'
|
57
|
+
proc do |number|
|
58
|
+
Kernel.format('%.2f%%', number.to_f * 100)
|
59
|
+
end
|
60
|
+
when '0.00E+00' then '%.2E'
|
61
|
+
when '#,##0 ;(#,##0)' then number_format('%.0f', '(%.0f)')
|
62
|
+
when '#,##0 ;[Red](#,##0)' then number_format('%.0f', '[Red](%.0f)')
|
63
|
+
when '#,##0.00;(#,##0.00)' then number_format('%.2f', '(%.2f)')
|
64
|
+
when '#,##0.00;[Red](#,##0.00)' then number_format('%.2f', '[Red](%.2f)')
|
62
65
|
# FIXME: not quite sure what the format should look like in this case.
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
+
when '##0.0E+0' then '%.1E'
|
67
|
+
when '@' then proc { |number| number }
|
68
|
+
else
|
69
|
+
raise "Unknown format: #{format.inspect}"
|
70
|
+
end
|
66
71
|
end
|
67
72
|
|
68
73
|
private
|
@@ -77,10 +82,6 @@ module Roo
|
|
77
82
|
Kernel.format(formatter, number).reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
78
83
|
end
|
79
84
|
end
|
80
|
-
|
81
|
-
def zero_padded_number?
|
82
|
-
@format[/0+/] == @format
|
83
|
-
end
|
84
85
|
end
|
85
86
|
end
|
86
87
|
end
|
@@ -2,12 +2,12 @@ module Roo
|
|
2
2
|
class Excelx
|
3
3
|
class Cell
|
4
4
|
class String < Cell::Base
|
5
|
-
attr_reader :value, :formula, :format, :
|
5
|
+
attr_reader :value, :formula, :format, :cell_value, :coordinate
|
6
|
+
|
7
|
+
attr_reader_with_default default_type: :string, cell_type: :string
|
6
8
|
|
7
9
|
def initialize(value, formula, style, link, coordinate)
|
8
10
|
super(value, formula, nil, style, link, coordinate)
|
9
|
-
@type = @cell_type = :string
|
10
|
-
@value = link? ? Roo::Link.new(link, value) : value
|
11
11
|
end
|
12
12
|
|
13
13
|
def empty?
|
data/lib/roo/excelx/cell/time.rb
CHANGED
@@ -4,15 +4,16 @@ module Roo
|
|
4
4
|
class Excelx
|
5
5
|
class Cell
|
6
6
|
class Time < Roo::Excelx::Cell::DateTime
|
7
|
-
attr_reader :value, :formula, :format, :cell_value, :
|
7
|
+
attr_reader :value, :formula, :format, :cell_value, :coordinate
|
8
|
+
|
9
|
+
attr_reader_with_default default_type: :time
|
8
10
|
|
9
11
|
def initialize(value, formula, excelx_type, style, link, base_date, coordinate)
|
10
12
|
# NOTE: Pass all arguments to DateTime super class.
|
11
13
|
super
|
12
|
-
@type = :time
|
13
14
|
@format = excelx_type.last
|
14
15
|
@datetime = create_datetime(base_date, value)
|
15
|
-
@value = link
|
16
|
+
@value = link ? Roo::Link.new(link, value) : (value.to_f * 86_400).to_i
|
16
17
|
end
|
17
18
|
|
18
19
|
def formatted_value
|
data/lib/roo/excelx/cell.rb
CHANGED
@@ -40,19 +40,23 @@ module Roo
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def self.create_cell(type, *values)
|
43
|
+
cell_class(type)&.new(*values)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.cell_class(type)
|
43
47
|
case type
|
44
48
|
when :string
|
45
|
-
Cell::String
|
49
|
+
Cell::String
|
46
50
|
when :boolean
|
47
|
-
Cell::Boolean
|
51
|
+
Cell::Boolean
|
48
52
|
when :number
|
49
|
-
Cell::Number
|
53
|
+
Cell::Number
|
50
54
|
when :date
|
51
|
-
Cell::Date
|
55
|
+
Cell::Date
|
52
56
|
when :datetime
|
53
|
-
Cell::DateTime
|
57
|
+
Cell::DateTime
|
54
58
|
when :time
|
55
|
-
Cell::Time
|
59
|
+
Cell::Time
|
56
60
|
end
|
57
61
|
end
|
58
62
|
|
data/lib/roo/excelx/comments.rb
CHANGED
@@ -12,10 +12,10 @@ module Roo
|
|
12
12
|
def extract_comments
|
13
13
|
return {} unless doc_exists?
|
14
14
|
|
15
|
-
|
15
|
+
doc.xpath('//comments/commentList/comment').each_with_object({}) do |comment, hash|
|
16
16
|
value = (comment.at_xpath('./text/r/t') || comment.at_xpath('./text/t')).text
|
17
|
-
[::Roo::Utils.ref_to_key(comment
|
18
|
-
end
|
17
|
+
hash[::Roo::Utils.ref_to_key(comment['ref'].to_s)] = value
|
18
|
+
end
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
@@ -1,11 +1,18 @@
|
|
1
1
|
module Roo
|
2
2
|
class Excelx
|
3
|
-
class Coordinate
|
4
|
-
attr_accessor :row, :column
|
3
|
+
class Coordinate < ::Array
|
5
4
|
|
6
5
|
def initialize(row, column)
|
7
|
-
|
8
|
-
|
6
|
+
super() << row << column
|
7
|
+
freeze
|
8
|
+
end
|
9
|
+
|
10
|
+
def row
|
11
|
+
self[0]
|
12
|
+
end
|
13
|
+
|
14
|
+
def column
|
15
|
+
self[1]
|
9
16
|
end
|
10
17
|
end
|
11
18
|
end
|
data/lib/roo/excelx/extractor.rb
CHANGED
@@ -1,16 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "roo/helpers/weak_instance_cache"
|
4
|
+
|
1
5
|
module Roo
|
2
6
|
class Excelx
|
3
7
|
class Extractor
|
4
|
-
|
8
|
+
include Roo::Helpers::WeakInstanceCache
|
9
|
+
|
10
|
+
COMMON_STRINGS = {
|
11
|
+
t: "t",
|
12
|
+
r: "r",
|
13
|
+
s: "s",
|
14
|
+
ref: "ref",
|
15
|
+
html_tag_open: "<html>",
|
16
|
+
html_tag_closed: "</html>"
|
17
|
+
}
|
18
|
+
|
19
|
+
def initialize(path, options = {})
|
5
20
|
@path = path
|
21
|
+
@options = options
|
6
22
|
end
|
7
23
|
|
8
24
|
private
|
9
25
|
|
10
26
|
def doc
|
11
|
-
|
27
|
+
instance_cache(:@doc) do
|
28
|
+
raise FileNotFound, "#{@path} file not found" unless doc_exists?
|
12
29
|
|
13
|
-
|
30
|
+
::Roo::Utils.load_xml(@path).remove_namespaces!
|
31
|
+
end
|
14
32
|
end
|
15
33
|
|
16
34
|
def doc_exists?
|
data/lib/roo/excelx/format.rb
CHANGED
@@ -1,49 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Roo
|
2
4
|
class Excelx
|
3
5
|
module Format
|
6
|
+
extend self
|
4
7
|
EXCEPTIONAL_FORMATS = {
|
5
8
|
'h:mm am/pm' => :date,
|
6
9
|
'h:mm:ss am/pm' => :date
|
7
10
|
}
|
8
11
|
|
9
12
|
STANDARD_FORMATS = {
|
10
|
-
0 => 'General'
|
11
|
-
1 => '0'
|
12
|
-
2 => '0.00'
|
13
|
-
3 => '#,##0'
|
14
|
-
4 => '#,##0.00'
|
15
|
-
9 => '0%'
|
16
|
-
10 => '0.00%'
|
17
|
-
11 => '0.00E+00'
|
18
|
-
12 => '# ?/?'
|
19
|
-
13 => '# ??/??'
|
20
|
-
14 => 'mm-dd-yy'
|
21
|
-
15 => 'd-mmm-yy'
|
22
|
-
16 => 'd-mmm'
|
23
|
-
17 => 'mmm-yy'
|
24
|
-
18 => 'h:mm AM/PM'
|
25
|
-
19 => 'h:mm:ss AM/PM'
|
26
|
-
20 => 'h:mm'
|
27
|
-
21 => 'h:mm:ss'
|
28
|
-
22 => 'm/d/yy h:mm'
|
29
|
-
37 => '#,##0 ;(#,##0)'
|
30
|
-
38 => '#,##0 ;[Red](#,##0)'
|
31
|
-
39 => '#,##0.00;(#,##0.00)'
|
32
|
-
40 => '#,##0.00;[Red](#,##0.00)'
|
33
|
-
45 => 'mm:ss'
|
34
|
-
46 => '[h]:mm:ss'
|
35
|
-
47 => 'mmss.0'
|
36
|
-
48 => '##0.0E+0'
|
37
|
-
49 => '@'
|
13
|
+
0 => 'General',
|
14
|
+
1 => '0',
|
15
|
+
2 => '0.00',
|
16
|
+
3 => '#,##0',
|
17
|
+
4 => '#,##0.00',
|
18
|
+
9 => '0%',
|
19
|
+
10 => '0.00%',
|
20
|
+
11 => '0.00E+00',
|
21
|
+
12 => '# ?/?',
|
22
|
+
13 => '# ??/??',
|
23
|
+
14 => 'mm-dd-yy',
|
24
|
+
15 => 'd-mmm-yy',
|
25
|
+
16 => 'd-mmm',
|
26
|
+
17 => 'mmm-yy',
|
27
|
+
18 => 'h:mm AM/PM',
|
28
|
+
19 => 'h:mm:ss AM/PM',
|
29
|
+
20 => 'h:mm',
|
30
|
+
21 => 'h:mm:ss',
|
31
|
+
22 => 'm/d/yy h:mm',
|
32
|
+
37 => '#,##0 ;(#,##0)',
|
33
|
+
38 => '#,##0 ;[Red](#,##0)',
|
34
|
+
39 => '#,##0.00;(#,##0.00)',
|
35
|
+
40 => '#,##0.00;[Red](#,##0.00)',
|
36
|
+
45 => 'mm:ss',
|
37
|
+
46 => '[h]:mm:ss',
|
38
|
+
47 => 'mmss.0',
|
39
|
+
48 => '##0.0E+0',
|
40
|
+
49 => '@'
|
38
41
|
}
|
39
42
|
|
40
43
|
def to_type(format)
|
44
|
+
@to_type ||= {}
|
45
|
+
@to_type[format] ||= _to_type(format)
|
46
|
+
end
|
47
|
+
|
48
|
+
def _to_type(format)
|
41
49
|
format = format.to_s.downcase
|
42
50
|
if (type = EXCEPTIONAL_FORMATS[format])
|
43
51
|
type
|
44
52
|
elsif format.include?('#')
|
45
53
|
:float
|
46
|
-
elsif !format.match(/d+(?![\]])/).nil?
|
54
|
+
elsif format.include?('y') || !format.match(/d+(?![\]])/).nil?
|
47
55
|
if format.include?('h') || format.include?('s')
|
48
56
|
:datetime
|
49
57
|
else
|
@@ -58,7 +66,6 @@ module Roo
|
|
58
66
|
end
|
59
67
|
end
|
60
68
|
|
61
|
-
module_function :to_type
|
62
69
|
end
|
63
|
-
end
|
70
|
+
end
|
64
71
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'roo/excelx/extractor'
|
2
|
+
|
3
|
+
module Roo
|
4
|
+
class Excelx
|
5
|
+
class Images < Excelx::Extractor
|
6
|
+
|
7
|
+
# Returns: Hash { id1: extracted_file_name1 },
|
8
|
+
# Example: { "rId1"=>"roo_media_image1.png",
|
9
|
+
# "rId2"=>"roo_media_image2.png",
|
10
|
+
# "rId3"=>"roo_media_image3.png" }
|
11
|
+
def list
|
12
|
+
@images ||= extract_images_names
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def extract_images_names
|
18
|
+
return {} unless doc_exists?
|
19
|
+
|
20
|
+
doc.xpath('/Relationships/Relationship').each_with_object({}) do |rel, hash|
|
21
|
+
hash[rel['Id']] = "roo" + rel['Target'].gsub(/\.\.\/|\//, '_')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'roo/excelx/extractor'
|
2
4
|
|
3
5
|
module Roo
|
@@ -11,14 +13,20 @@ module Roo
|
|
11
13
|
@relationships ||= extract_relationships
|
12
14
|
end
|
13
15
|
|
16
|
+
def include_type?(type)
|
17
|
+
to_a.any? do |_, rel|
|
18
|
+
rel["Type"]&.include? type
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
14
22
|
private
|
15
23
|
|
16
24
|
def extract_relationships
|
17
|
-
return
|
25
|
+
return {} unless doc_exists?
|
18
26
|
|
19
|
-
|
20
|
-
[rel
|
21
|
-
end
|
27
|
+
doc.xpath('/Relationships/Relationship').each_with_object({}) do |rel, hash|
|
28
|
+
hash[rel['Id']] = rel
|
29
|
+
end
|
22
30
|
end
|
23
31
|
end
|
24
32
|
end
|
data/lib/roo/excelx/shared.rb
CHANGED
@@ -4,12 +4,15 @@ module Roo
|
|
4
4
|
# reduce memory usage and reduce the number of objects being passed
|
5
5
|
# to various inititializers.
|
6
6
|
class Shared
|
7
|
-
attr_accessor :comments_files, :sheet_files, :rels_files
|
8
|
-
def initialize(dir)
|
7
|
+
attr_accessor :comments_files, :sheet_files, :rels_files, :image_rels, :image_files
|
8
|
+
def initialize(dir, options = {})
|
9
9
|
@dir = dir
|
10
10
|
@comments_files = []
|
11
11
|
@sheet_files = []
|
12
12
|
@rels_files = []
|
13
|
+
@options = options
|
14
|
+
@image_rels = []
|
15
|
+
@image_files = []
|
13
16
|
end
|
14
17
|
|
15
18
|
def styles
|
@@ -17,7 +20,7 @@ module Roo
|
|
17
20
|
end
|
18
21
|
|
19
22
|
def shared_strings
|
20
|
-
@shared_strings ||= SharedStrings.new(File.join(@dir, 'roo_sharedStrings.xml'))
|
23
|
+
@shared_strings ||= SharedStrings.new(File.join(@dir, 'roo_sharedStrings.xml'), @options)
|
21
24
|
end
|
22
25
|
|
23
26
|
def workbook
|
@@ -27,6 +30,10 @@ module Roo
|
|
27
30
|
def base_date
|
28
31
|
workbook.base_date
|
29
32
|
end
|
33
|
+
|
34
|
+
def base_timestamp
|
35
|
+
workbook.base_timestamp
|
36
|
+
end
|
30
37
|
end
|
31
38
|
end
|
32
39
|
end
|
@@ -1,16 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'roo/excelx/extractor'
|
2
4
|
|
3
5
|
module Roo
|
4
6
|
class Excelx
|
5
7
|
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
|
-
|
14
8
|
def [](index)
|
15
9
|
to_a[index]
|
16
10
|
end
|
@@ -26,6 +20,7 @@ module Roo
|
|
26
20
|
# Use to_html or to_a for html returns
|
27
21
|
# See what is happening with commit???
|
28
22
|
def use_html?(index)
|
23
|
+
return false if @options[:disable_html_wrapper]
|
29
24
|
to_html[index][/<([biu]|sup|sub)>/]
|
30
25
|
end
|
31
26
|
|
@@ -45,7 +40,7 @@ module Roo
|
|
45
40
|
document = fix_invalid_shared_strings(doc)
|
46
41
|
# read the shared strings xml document
|
47
42
|
document.xpath('/sst/si').map do |si|
|
48
|
-
shared_string =
|
43
|
+
shared_string = +""
|
49
44
|
si.children.each do |elem|
|
50
45
|
case elem.name
|
51
46
|
when 'r'
|
@@ -65,7 +60,7 @@ module Roo
|
|
65
60
|
fix_invalid_shared_strings(doc)
|
66
61
|
# read the shared strings xml document
|
67
62
|
doc.xpath('/sst/si').map do |si|
|
68
|
-
html_string = '<html>'
|
63
|
+
html_string = '<html>'.dup
|
69
64
|
si.children.each do |elem|
|
70
65
|
case elem.name
|
71
66
|
when 'r'
|
@@ -95,7 +90,7 @@ module Roo
|
|
95
90
|
#
|
96
91
|
# Expected Output ::: "<html><sub|sup><b><i><u>TEXT</u></i></b></sub|/sup></html>"
|
97
92
|
def extract_html_r(r_elem)
|
98
|
-
str =
|
93
|
+
str = +""
|
99
94
|
xml_elems = {
|
100
95
|
sub: false,
|
101
96
|
sup: false,
|
@@ -103,7 +98,6 @@ module Roo
|
|
103
98
|
i: false,
|
104
99
|
u: false
|
105
100
|
}
|
106
|
-
b, i, u, sub, sup = false, false, false, false, false
|
107
101
|
r_elem.children.each do |elem|
|
108
102
|
case elem.name
|
109
103
|
when 'rPr'
|
@@ -141,13 +135,13 @@ module Roo
|
|
141
135
|
|
142
136
|
# This will return an html string
|
143
137
|
def create_html(text, formatting)
|
144
|
-
tmp_str =
|
138
|
+
tmp_str = +""
|
145
139
|
formatting.each do |elem, val|
|
146
140
|
tmp_str << "<#{elem}>" if val
|
147
141
|
end
|
148
142
|
tmp_str << text
|
149
|
-
|
150
|
-
|
143
|
+
|
144
|
+
formatting.reverse_each do |elem, val|
|
151
145
|
tmp_str << "</#{elem}>" if val
|
152
146
|
end
|
153
147
|
tmp_str
|
data/lib/roo/excelx/sheet.rb
CHANGED
@@ -4,11 +4,15 @@ module Roo
|
|
4
4
|
class Sheet
|
5
5
|
extend Forwardable
|
6
6
|
|
7
|
-
delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files] => :@shared
|
7
|
+
delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files, :image_rels] => :@shared
|
8
|
+
|
9
|
+
attr_reader :images
|
8
10
|
|
9
11
|
def initialize(name, shared, sheet_index, options = {})
|
10
12
|
@name = name
|
11
13
|
@shared = shared
|
14
|
+
@sheet_index = sheet_index
|
15
|
+
@images = Images.new(image_rels[sheet_index]).list
|
12
16
|
@rels = Relationships.new(rels_files[sheet_index])
|
13
17
|
@comments = Comments.new(comments_files[sheet_index])
|
14
18
|
@sheet = SheetDoc.new(sheet_files[sheet_index], @rels, shared, options)
|
@@ -19,7 +23,14 @@ module Roo
|
|
19
23
|
end
|
20
24
|
|
21
25
|
def present_cells
|
22
|
-
@present_cells ||=
|
26
|
+
@present_cells ||= begin
|
27
|
+
warn %{
|
28
|
+
[DEPRECATION] present_cells is deprecated. Alternate:
|
29
|
+
with activesupport => cells[key].presence
|
30
|
+
without activesupport => cells[key]&.presence
|
31
|
+
}
|
32
|
+
cells.select { |_, cell| cell&.presence }
|
33
|
+
end
|
23
34
|
end
|
24
35
|
|
25
36
|
# Yield each row as array of Excelx::Cell objects
|
@@ -39,33 +50,33 @@ module Roo
|
|
39
50
|
|
40
51
|
def row(row_number)
|
41
52
|
first_column.upto(last_column).map do |col|
|
42
|
-
cells[[row_number, col]]
|
43
|
-
end
|
53
|
+
cells[[row_number, col]]&.value
|
54
|
+
end
|
44
55
|
end
|
45
56
|
|
46
57
|
def column(col_number)
|
47
58
|
first_row.upto(last_row).map do |row|
|
48
|
-
cells[[row, col_number]]
|
49
|
-
end
|
59
|
+
cells[[row, col_number]]&.value
|
60
|
+
end
|
50
61
|
end
|
51
62
|
|
52
63
|
# returns the number of the first non-empty row
|
53
64
|
def first_row
|
54
|
-
@first_row ||=
|
65
|
+
@first_row ||= first_last_row_col[:first_row]
|
55
66
|
end
|
56
67
|
|
57
68
|
def last_row
|
58
|
-
@last_row ||=
|
69
|
+
@last_row ||= first_last_row_col[:last_row]
|
59
70
|
end
|
60
71
|
|
61
72
|
# returns the number of the first non-empty column
|
62
73
|
def first_column
|
63
|
-
@first_column ||=
|
74
|
+
@first_column ||= first_last_row_col[:first_column]
|
64
75
|
end
|
65
76
|
|
66
77
|
# returns the number of the last non-empty column
|
67
78
|
def last_column
|
68
|
-
@last_column ||=
|
79
|
+
@last_column ||= first_last_row_col[:last_column]
|
69
80
|
end
|
70
81
|
|
71
82
|
def excelx_format(key)
|
@@ -107,6 +118,34 @@ module Roo
|
|
107
118
|
(cell.coordinate.column - 1 - last_column).times { pad << nil }
|
108
119
|
pad
|
109
120
|
end
|
121
|
+
|
122
|
+
def first_last_row_col
|
123
|
+
@first_last_row_col ||= begin
|
124
|
+
first_row = last_row = first_col = last_col = nil
|
125
|
+
|
126
|
+
cells.each do |(row, col), cell|
|
127
|
+
next unless cell&.presence
|
128
|
+
first_row ||= row
|
129
|
+
last_row ||= row
|
130
|
+
first_col ||= col
|
131
|
+
last_col ||= col
|
132
|
+
|
133
|
+
if row > last_row
|
134
|
+
last_row = row
|
135
|
+
elsif row < first_row
|
136
|
+
first_row = row
|
137
|
+
end
|
138
|
+
|
139
|
+
if col > last_col
|
140
|
+
last_col = col
|
141
|
+
elsif col < first_col
|
142
|
+
first_col = col
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
{first_row: first_row, last_row: last_row, first_column: first_col, last_column: last_col}
|
147
|
+
end
|
148
|
+
end
|
110
149
|
end
|
111
150
|
end
|
112
151
|
end
|