roo 2.1.1 → 2.2.0
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/CHANGELOG.md +17 -0
- data/Guardfile +1 -2
- data/README.md +22 -2
- data/lib/roo.rb +1 -0
- data/lib/roo/base.rb +36 -36
- data/lib/roo/errors.rb +9 -0
- data/lib/roo/excelx.rb +19 -71
- data/lib/roo/excelx/cell.rb +32 -3
- 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 +101 -0
- data/lib/roo/excelx/cell/empty.rb +19 -0
- data/lib/roo/excelx/cell/number.rb +80 -0
- data/lib/roo/excelx/cell/string.rb +19 -0
- data/lib/roo/excelx/cell/time.rb +43 -0
- data/lib/roo/excelx/comments.rb +33 -0
- data/lib/roo/excelx/coordinate.rb +12 -0
- data/lib/roo/excelx/format.rb +64 -0
- data/lib/roo/excelx/shared.rb +32 -0
- data/lib/roo/excelx/sheet.rb +12 -7
- data/lib/roo/excelx/sheet_doc.rb +95 -91
- data/lib/roo/link.rb +21 -2
- data/lib/roo/open_office.rb +4 -1
- data/lib/roo/version.rb +1 -1
- data/roo.gemspec +1 -1
- data/spec/lib/roo/base_spec.rb +19 -2
- data/spec/lib/roo/excelx_spec.rb +13 -10
- data/spec/lib/roo/openoffice_spec.rb +18 -1
- data/test/excelx/cell/test_base.rb +64 -0
- data/test/excelx/cell/test_boolean.rb +38 -0
- data/test/excelx/cell/test_date.rb +43 -0
- data/test/excelx/cell/test_datetime.rb +48 -0
- data/test/excelx/cell/test_empty.rb +8 -0
- data/test/excelx/cell/test_number.rb +58 -0
- data/test/excelx/cell/test_string.rb +30 -0
- data/test/excelx/cell/test_time.rb +33 -0
- data/test/test_roo.rb +14 -8
- metadata +23 -2
@@ -0,0 +1,94 @@
|
|
1
|
+
module Roo
|
2
|
+
class Excelx
|
3
|
+
class Cell
|
4
|
+
class Base
|
5
|
+
attr_reader :cell_type, :cell_value, :value
|
6
|
+
|
7
|
+
# FIXME: I think style should be deprecated. Having a style attribute
|
8
|
+
# for a cell doesn't really accomplish much. It seems to be used
|
9
|
+
# when you want to export to excelx.
|
10
|
+
attr_reader :style
|
11
|
+
|
12
|
+
|
13
|
+
# FIXME: Updating a cell's value should be able tochange the cell's type,
|
14
|
+
# but that isn't currently possible. This will cause weird bugs
|
15
|
+
# when one changes the value of a Number cell to a String. e.g.
|
16
|
+
#
|
17
|
+
# cell = Cell::Number(*args)
|
18
|
+
# cell.value = 'Hello'
|
19
|
+
# cell.formatted_value # => Some unexpected value
|
20
|
+
#
|
21
|
+
# Here are two possible solutions to such issues:
|
22
|
+
# 1. Don't allow a cell's value to be updated. Use a method like
|
23
|
+
# `Sheet.update_cell` instead. The simple solution.
|
24
|
+
# 2. When `cell.value = ` is called, use injection to try and
|
25
|
+
# change the type of cell on the fly. But deciding what type
|
26
|
+
# of value to pass to `cell.value=`. isn't always obvious. e.g.
|
27
|
+
# `cell.value = Time.now` should convert a cell to a DateTime,
|
28
|
+
# not a Time cell. Time cells would be hard to recognize because
|
29
|
+
# they are integers. This approach would require a significant
|
30
|
+
# change to the code as written. The complex solution.
|
31
|
+
#
|
32
|
+
# If the first solution is used, then this method should be
|
33
|
+
# deprecated.
|
34
|
+
attr_writer :value
|
35
|
+
|
36
|
+
def initialize(value, formula, excelx_type, style, link, coordinate)
|
37
|
+
@link = !!link
|
38
|
+
@cell_value = value
|
39
|
+
@cell_type = excelx_type
|
40
|
+
@formula = formula
|
41
|
+
@style = style
|
42
|
+
@coordinate = coordinate
|
43
|
+
@type = :base
|
44
|
+
@value = link? ? Roo::Link.new(link, value) : value
|
45
|
+
end
|
46
|
+
|
47
|
+
def type
|
48
|
+
if formula?
|
49
|
+
:formula
|
50
|
+
elsif link?
|
51
|
+
:link
|
52
|
+
else
|
53
|
+
@type
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def formula?
|
58
|
+
!!@formula
|
59
|
+
end
|
60
|
+
|
61
|
+
def link?
|
62
|
+
!!@link
|
63
|
+
end
|
64
|
+
|
65
|
+
alias_method :formatted_value, :value
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
formatted_value
|
69
|
+
end
|
70
|
+
|
71
|
+
# DEPRECATED: Please use link instead.
|
72
|
+
def hyperlink
|
73
|
+
warn '[DEPRECATION] `hyperlink` is deprecated. Please use `link` instead.'
|
74
|
+
end
|
75
|
+
|
76
|
+
# DEPRECATED: Please use cell_value instead.
|
77
|
+
def excelx_value
|
78
|
+
warn '[DEPRECATION] `excelx_value` is deprecated. Please use `cell_value` instead.'
|
79
|
+
cell_value
|
80
|
+
end
|
81
|
+
|
82
|
+
# DEPRECATED: Please use cell_type instead.
|
83
|
+
def excelx_type
|
84
|
+
warn '[DEPRECATION] `excelx_type` is deprecated. Please use `cell_type` instead.'
|
85
|
+
cell_type
|
86
|
+
end
|
87
|
+
|
88
|
+
def empty?
|
89
|
+
false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Roo
|
2
|
+
class Excelx
|
3
|
+
class Cell
|
4
|
+
class Boolean < Cell::Base
|
5
|
+
attr_reader :value, :formula, :format, :cell_type, :cell_value, :link, :coordinate
|
6
|
+
|
7
|
+
def initialize(value, formula, style, link, coordinate)
|
8
|
+
super(value, formula, nil, style, link, coordinate)
|
9
|
+
@type = @cell_type = :boolean
|
10
|
+
@value = link? ? Roo::Link.new(link, value) : create_boolean(value)
|
11
|
+
end
|
12
|
+
|
13
|
+
def formatted_value
|
14
|
+
value ? 'TRUE'.freeze : 'FALSE'.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def create_boolean(value)
|
20
|
+
# FIXME: Using a boolean will cause methods like Base#to_csv to fail.
|
21
|
+
# Roo is using some method to ignore false/nil values.
|
22
|
+
value.to_i == 1 ? true : false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Roo
|
4
|
+
class Excelx
|
5
|
+
class Cell
|
6
|
+
class Date < Roo::Excelx::Cell::DateTime
|
7
|
+
attr_reader :value, :formula, :format, :cell_type, :cell_value, :link, :coordinate
|
8
|
+
|
9
|
+
def initialize(value, formula, excelx_type, style, link, base_date, coordinate)
|
10
|
+
# NOTE: Pass all arguments to the parent class, DateTime.
|
11
|
+
super
|
12
|
+
@type = :date
|
13
|
+
@format = excelx_type.last
|
14
|
+
@value = link? ? Roo::Link.new(link, value) : create_date(base_date, value)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def create_date(base_date, value)
|
20
|
+
date = base_date + value.to_i
|
21
|
+
yyyy, mm, dd = date.strftime('%Y-%m-%d').split('-')
|
22
|
+
|
23
|
+
::Date.new(yyyy.to_i, mm.to_i, dd.to_i)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Roo
|
4
|
+
class Excelx
|
5
|
+
class Cell
|
6
|
+
class DateTime < Cell::Base
|
7
|
+
attr_reader :value, :formula, :format, :cell_value, :link, :coordinate
|
8
|
+
|
9
|
+
def initialize(value, formula, excelx_type, style, link, base_date, coordinate)
|
10
|
+
super(value, formula, excelx_type, style, link, coordinate)
|
11
|
+
@type = :datetime
|
12
|
+
@format = excelx_type.last
|
13
|
+
@value = link? ? Roo::Link.new(link, value) : create_datetime(base_date, value)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Public: Returns formatted value for a datetime. Format's can be an
|
17
|
+
# standard excel format, or a custom format.
|
18
|
+
#
|
19
|
+
# Standard formats follow certain conventions. Date fields for
|
20
|
+
# days, months, and years are separated with hyhens or
|
21
|
+
# slashes ("-", /") (e.g. 01-JAN, 1/13/15). Time fields for
|
22
|
+
# hours, minutes, and seconds are separated with a colon (e.g.
|
23
|
+
# 12:45:01).
|
24
|
+
#
|
25
|
+
# If a custom format follows those conventions, then the custom
|
26
|
+
# format will be used for the a cell's formatted value.
|
27
|
+
# Otherwise, the formatted value will be in the following
|
28
|
+
# format: 'YYYY-mm-dd HH:MM:SS' (e.g. "2015-07-10 20:33:15").
|
29
|
+
#
|
30
|
+
# Examples
|
31
|
+
# formatted_value #=> '01-JAN'
|
32
|
+
#
|
33
|
+
# Returns a String representation of a cell's value.
|
34
|
+
def formatted_value
|
35
|
+
date_regex = /(?<date>[dmy]+[\-\/][dmy]+([\-\/][dmy]+)?)/
|
36
|
+
time_regex = /(?<time>(\[?[h]\]?+:)?[m]+(:?ss|:?s)?)/
|
37
|
+
|
38
|
+
formatter = @format.downcase.split(' ').map do |part|
|
39
|
+
if part[date_regex] == part
|
40
|
+
part.gsub(/#{DATE_FORMATS.keys.join('|')}/, DATE_FORMATS)
|
41
|
+
elsif part[time_regex]
|
42
|
+
part.gsub(/#{TIME_FORMATS.keys.join('|')}/, TIME_FORMATS)
|
43
|
+
else
|
44
|
+
warn 'Unable to parse custom format. Using "YYYY-mm-dd HH:MM:SS" format.'
|
45
|
+
return @value.strftime('%F %T')
|
46
|
+
end
|
47
|
+
end.join(' ')
|
48
|
+
|
49
|
+
@value.strftime(formatter)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
DATE_FORMATS = {
|
55
|
+
'yyyy'.freeze => '%Y'.freeze, # Year: 2000
|
56
|
+
'yy'.freeze => '%y'.freeze, # Year: 00
|
57
|
+
# mmmmm => J-D
|
58
|
+
'mmmm'.freeze => '%B'.freeze, # Month: January
|
59
|
+
'mmm'.freeze => '%^b'.freeze, # Month: JAN
|
60
|
+
'mm'.freeze => '%m'.freeze, # Month: 01
|
61
|
+
'm'.freeze => '%-m'.freeze, # Month: 1
|
62
|
+
'dddd'.freeze => '%A'.freeze, # Day of the Week: Sunday
|
63
|
+
'ddd'.freeze => '%^a'.freeze, # Day of the Week: SUN
|
64
|
+
'dd'.freeze => '%d'.freeze, # Day of the Month: 01
|
65
|
+
'd'.freeze => '%-d'.freeze, # Day of the Month: 1
|
66
|
+
# '\\\\'.freeze => ''.freeze, # NOTE: Fixes a custom format's output.
|
67
|
+
}
|
68
|
+
|
69
|
+
TIME_FORMATS = {
|
70
|
+
'hh'.freeze => '%H'.freeze, # Hour (24): 01
|
71
|
+
'h'.freeze => '%-k'.freeze, # Hour (24): 1
|
72
|
+
# 'hh'.freeze => '%I'.freeze, # Hour (12): 08
|
73
|
+
# 'h'.freeze => '%-l'.freeze, # Hour (12): 8
|
74
|
+
'mm'.freeze => '%M'.freeze, # Minute: 01
|
75
|
+
# FIXME: is this used? Seems like 'm' is used for month, not minute.
|
76
|
+
'm'.freeze => '%-M'.freeze, # Minute: 1
|
77
|
+
'ss'.freeze => '%S'.freeze, # Seconds: 01
|
78
|
+
's'.freeze => '%-S'.freeze, # Seconds: 1
|
79
|
+
'am/pm'.freeze => '%p'.freeze, # Meridian: AM
|
80
|
+
'000'.freeze => '%3N'.freeze, # Fractional Seconds: thousandth.
|
81
|
+
'00'.freeze => '%2N'.freeze, # Fractional Seconds: hundredth.
|
82
|
+
'0'.freeze => '%1N'.freeze, # Fractional Seconds: tenths.
|
83
|
+
}
|
84
|
+
|
85
|
+
def create_datetime(base_date, value)
|
86
|
+
date = base_date + value.to_f.round(6)
|
87
|
+
datetime_string = date.strftime('%Y-%m-%d %H:%M:%S.%N')
|
88
|
+
t = round_datetime(datetime_string)
|
89
|
+
|
90
|
+
::DateTime.civil(t.year, t.month, t.day, t.hour, t.min, t.sec)
|
91
|
+
end
|
92
|
+
|
93
|
+
def round_datetime(datetime_string)
|
94
|
+
/(?<yyyy>\d+)-(?<mm>\d+)-(?<dd>\d+) (?<hh>\d+):(?<mi>\d+):(?<ss>\d+.\d+)/ =~ datetime_string
|
95
|
+
|
96
|
+
::Time.new(yyyy.to_i, mm.to_i, dd.to_i, hh.to_i, mi.to_i, ss.to_r).round(0)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
module Roo
|
3
|
+
class Excelx
|
4
|
+
class Cell
|
5
|
+
class Empty < Cell::Base
|
6
|
+
attr_reader :value, :formula, :format, :cell_type, :cell_value, :hyperlink, :coordinate
|
7
|
+
|
8
|
+
def initialize(coordinate)
|
9
|
+
@value = @formula = @format = @cell_type = @cell_value = @hyperlink = nil
|
10
|
+
@coordinate = coordinate
|
11
|
+
end
|
12
|
+
|
13
|
+
def empty?
|
14
|
+
true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Roo
|
2
|
+
class Excelx
|
3
|
+
class Cell
|
4
|
+
class Number < Cell::Base
|
5
|
+
attr_reader :value, :formula, :format, :cell_value, :link, :coordinate
|
6
|
+
|
7
|
+
def initialize(value, formula, excelx_type, style, link, coordinate)
|
8
|
+
super
|
9
|
+
# FIXME: change @type to number. This will break brittle tests.
|
10
|
+
# FIXME: Excelx_type is an array, but the first value isn't used.
|
11
|
+
@type = :float
|
12
|
+
@format = excelx_type.last
|
13
|
+
@value = link? ? Roo::Link.new(link, value) : create_numeric(value)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_numeric(number)
|
17
|
+
case @format
|
18
|
+
when /%/
|
19
|
+
Float(number)
|
20
|
+
when /\.0/
|
21
|
+
Float(number)
|
22
|
+
else
|
23
|
+
number.include?('.') ? Float(number) : Integer(number)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def formatted_value
|
28
|
+
formatter = formats[@format]
|
29
|
+
if formatter.is_a? Proc
|
30
|
+
formatter.call(@cell_value)
|
31
|
+
else
|
32
|
+
Kernel.format(formatter, @cell_value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def formats
|
37
|
+
# FIXME: numbers can be other colors besides red:
|
38
|
+
# [BLACK], [BLUE], [CYAN], [GREEN], [MAGENTA], [RED], [WHITE], [YELLOW], [COLOR n]
|
39
|
+
{
|
40
|
+
'General' => '%.0f',
|
41
|
+
'0' => '%.0f',
|
42
|
+
'0.00' => '%.2f',
|
43
|
+
'#,##0' => proc do |number|
|
44
|
+
Kernel.format('%.0f', number).reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
45
|
+
end,
|
46
|
+
'#,##0.00' => proc do |number|
|
47
|
+
Kernel.format('%.2f', number).reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
48
|
+
end,
|
49
|
+
'0%' => proc do |number|
|
50
|
+
Kernel.format('%d%', number.to_f * 100)
|
51
|
+
end,
|
52
|
+
'0.00%' => proc do |number|
|
53
|
+
Kernel.format('%.2f%', number.to_f * 100)
|
54
|
+
end,
|
55
|
+
'0.00E+00' => '%.2E',
|
56
|
+
'#,##0 ;(#,##0)' => proc do |number|
|
57
|
+
formatter = number.to_i > 0 ? '%.0f' : '(%.0f)'
|
58
|
+
Kernel.format(formatter, number.to_f.abs).reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
59
|
+
end,
|
60
|
+
'#,##0 ;[Red](#,##0)' => proc do |number|
|
61
|
+
formatter = number.to_i > 0 ? '%.0f' : '[Red](%.0f)'
|
62
|
+
Kernel.format(formatter, number.to_f.abs).reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
63
|
+
end,
|
64
|
+
'#,##0.00;(#,##0.00)' => proc do |number|
|
65
|
+
formatter = number.to_i > 0 ? '%.2f' : '(%.2f)'
|
66
|
+
Kernel.format(formatter, number.to_f.abs).reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
67
|
+
end,
|
68
|
+
'#,##0.00;[Red](#,##0.00)' => proc do |number|
|
69
|
+
formatter = number.to_i > 0 ? '%.2f' : '[Red](%.2f)'
|
70
|
+
Kernel.format(formatter, number.to_f.abs).reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
71
|
+
end,
|
72
|
+
# FIXME: not quite sure what the format should look like in this case.
|
73
|
+
'##0.0E+0' => '%.1E',
|
74
|
+
'@' => proc { |number| number }
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Roo
|
2
|
+
class Excelx
|
3
|
+
class Cell
|
4
|
+
class String < Cell::Base
|
5
|
+
attr_reader :value, :formula, :format, :cell_type, :cell_value, :link, :coordinate
|
6
|
+
|
7
|
+
def initialize(value, formula, style, link, coordinate)
|
8
|
+
super(value, formula, nil, style, link, coordinate)
|
9
|
+
@type = @cell_type = :string
|
10
|
+
@value = link? ? Roo::Link.new(link, value) : value
|
11
|
+
end
|
12
|
+
|
13
|
+
def empty?
|
14
|
+
value.empty?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Roo
|
4
|
+
class Excelx
|
5
|
+
class Cell
|
6
|
+
class Time < Roo::Excelx::Cell::DateTime
|
7
|
+
attr_reader :value, :formula, :format, :cell_value, :link, :coordinate
|
8
|
+
|
9
|
+
def initialize(value, formula, excelx_type, style, link, base_date, coordinate)
|
10
|
+
# NOTE: Pass all arguments to DateTime super class.
|
11
|
+
super
|
12
|
+
@type = :time
|
13
|
+
@format = excelx_type.last
|
14
|
+
@datetime = create_datetime(base_date, value)
|
15
|
+
@value = link? ? Roo::Link.new(link, value) : (value.to_f * 86_400).to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
def formatted_value
|
19
|
+
formatter = @format.gsub(/#{TIME_FORMATS.keys.join('|')}/, TIME_FORMATS)
|
20
|
+
@datetime.strftime(formatter)
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :to_s, :formatted_value
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def create_datetime(base_date, value)
|
28
|
+
date = base_date + value.to_f.round(6)
|
29
|
+
datetime_string = date.strftime('%Y-%m-%d %H:%M:%S.%N')
|
30
|
+
t = round_datetime(datetime_string)
|
31
|
+
|
32
|
+
::DateTime.civil(t.year, t.month, t.day, t.hour, t.min, t.sec)
|
33
|
+
end
|
34
|
+
|
35
|
+
def round_datetime(datetime_string)
|
36
|
+
/(?<yyyy>\d+)-(?<mm>\d+)-(?<dd>\d+) (?<hh>\d+):(?<mi>\d+):(?<ss>\d+.\d+)/ =~ datetime_string
|
37
|
+
|
38
|
+
::Time.new(yyyy.to_i, mm.to_i, dd.to_i, hh.to_i, mi.to_i, ss.to_r).round(0)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
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>
|