class Cell
attr_reader :index
def set_style(style)
style = StyleFormat.new(style) if style.is_a?(Hash)
@format_index = @parent.parent_wb.styles.add(style)
end
def row
@parent
end
def col
@index
end
end
class StringCell < Cell
def initialize(parent, index, format_index, sst_index)
@parent = parent
@index = index
@format_index = format_index
@sst_index = sst_index
end
def to_biff
LabelSSTRecord.new(@parent.index, @index, @format_index, @sst_index).to_biff
end
end
class BlankCell < Cell
def initialize(parent, index, format_index)
@parent = parent
@index = index
@format_index = format_index
end
def to_biff
BlankRecord.new(@parent.index, @index, @format_index).to_biff
end
end
class NumberCell < Cell
def initialize(parent, index, format_index, number)
@parent = parent
@index = index
@format_index = format_index
@number = number
end
def rk_record(rk_encoded)
RKRecord.new(@parent.index, @index, @format_index, rk_encoded).to_biff
end
# TODO test this section to be sure numbers are categorized and packed correctly.
def to_biff
# 30 bit signed int
in_range = (-0x20000000 <= @number) && (@number < 0x20000000)
is_int = (@number.to_i == @number)
if in_range && is_int
rk_encoded = 2 | (@number.to_i << 2)
return rk_record(rk_encoded)
end
# try scaling by 100 then using a 30 bit signed int
in_range = (-0x20000000 <= @number * 100) && (@number * 100 < 0x20000000)
round_trip = (@number.to_i*100) == @number*100
if in_range && round_trip
rk_encoded = (3 | (@number.to_i*100 << 2))
return rk_record(rk_encoded)
end
w0, w1, w2, w3 = [@number].pack('E').unpack('v4')
is_float_rk = (w0 == 0) && (w1 == 0) && (w2 & 0xFFFC) == w2
if is_float_rk
rk_encoded = (w3 << 16) | w2
return rk_record(rk_encoded)
end
w0, w1, w2, w3 = [@number * 100].pack('E').unpack('v4')
is_float_rk_100 = w0 == 0 && w1 == 0 && w2 & 0xFFFC == w2
if is_float_rk_100
rk_encoded = 1 | (w3 << 16) | w2
return rk_record(rk_encoded)
end
# If not an RK value, use a NumberRecord instead.
NumberRecord.new(@parent.index, @index, @format_index, @number).to_biff
end
end
class MulNumberCell < Cell
def initialize(parent, index, format_index, sst_index)
@parent = parent
@index = index
@format_index = format_index
@sst_index = sst_index
end
def to_biff
raise "not implemented"
end
end
class MulBlankCell < Cell
def initialize(parent, col1, col2, xf_idx)
raise unless col1 < col2
@parent = parent
@col1 = col1
@col2 = col2
@xf_idx = xf_idx
end
def to_biff
MulBlankRecord.new(@parent.index, @col1, @col2, @xf_idx).to_biff
end
end
class FormulaCell < Cell
def initialize(parent, index, format_index, formula, calc_flags = 0)
@parent = parent
@index = index
@format_index = format_index
@formula = formula
@calc_flags = calc_flags
end
def to_biff
args = [@parent.index, @index, @format_index, @formula.to_biff, @calc_flags]
FormulaRecord.new(*args).to_biff
end
end
class BooleanCell < Cell
def initialize(parent, index, format_index, number)
@parent = parent
@index = index
@format_index = format_index
@number = number
@is_error = 0
end
def to_biff
number = @number ? 1 : 0
BoolErrRecord.new(@parent.index, @index, @format_index, number, @is_error).to_biff
end
end
class ErrorCell < Cell
ERROR_CODES = {
0x00 => 0, # Intersection of two cell ranges is empty
0x07 => 7, # Division by zero
0x0F => 15, # Wrong type of operand
0x17 => 23, # Illegal or deleted cell reference
0x1D => 29, # Wrong function or range name
0x24 => 36, # Value range overflow
0x2A => 42, # Argument or function not available
'#NULL!' => 0, # Intersection of two cell ranges is empty
'#DIV/0!' => 7, # Division by zero
'#VALUE!' => 36, # Wrong type of operand
'#REF!' => 23, # Illegal or deleted cell reference
'#NAME?' => 29, # Wrong function or range name
'#NUM!' => 36, # Value range overflow
'#N/A!' => 42 # Argument or function not available
}
def initialize(parent, index, format_index, error_string_or_code)
@parent = parent
@index = index
@format_index = format_index
@number = ERROR_CODES[error_string_or_code]
@is_error = 1
raise "invalid error code #{error_string_or_code}" if @number.nil?
end
def to_biff
BoolErrRecord.new(@parent.index, @index, @format_index, @number, @is_error)
end
end