Skip to content

Commit

Permalink
Add merged cells functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
timon committed May 19, 2011
1 parent ced383f commit e5906f3
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 1 deletion.
1 change: 1 addition & 0 deletions lib/spreadsheet/excel/internals.rb
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ module Internals
:hlink => 0x01b8, # HLINK ➜ 6.52 (BIFF8 only)
:label => 0x0204, # LABEL ➜ 6.59 (BIFF2-BIFF7)
:labelsst => 0x00fd, # LABELSST ➜ 6.61 (BIFF8 only)
:mergedcells => 0x00e5, # ○○ MERGEDCELLS ➜ 5.67 (BIFF8 only)
:mulblank => 0x00be, # MULBLANK ➜ 6.64 (BIFF5-BIFF8)
:mulrk => 0x00bd, # MULRK ➜ 6.65 (BIFF5-BIFF8)
:number => 0x0203, # NUMBER ➜ 6.68
Expand Down
23 changes: 23 additions & 0 deletions lib/spreadsheet/excel/reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,27 @@ def read_window2 worksheet, work, pos, len
flags, _ = work.unpack 'v'
worksheet.selected = flags & 0x0200 > 0
end

def read_merged_cells worksheet, work, pos, len
# This record contains the addresses of merged cell ranges in the current sheet.
# Record MERGEDCELLS, BIFF8:
# Offset Size Contents
# 0 var. Cell range address list with merged ranges (➜ 2.5.15)
# If the record size exceeds the limit, it is not continued with a CONTINUE record,
# but another self-contained MERGEDCELLS record is started. The limit of 8224 bytes
# per record results in a maximum number of 1027 merged ranges.

worksheet.merged_cells.push *read_range_address_list(work, len)
#
# A cell range address list consists of a field with the number of ranges and the list
# of the range addresses.
# Cell range address list, BIFF2-BIFF8:
# Offset Size Contents
# 0 2 Number of following cell range addresses (nm)
# 2 6∙nm or 8∙nm List of nm cell range addresses (➜ 2.5.14)
#
end

def read_workbook
worksheet = nil
previous_op = nil
Expand Down Expand Up @@ -843,6 +864,8 @@ def read_worksheet worksheet, offset
read_hlink worksheet, work, pos, len
when :window2
read_window2 worksheet, work, pos, len
when :mergedcells # ○○ MERGEDCELLS ➜ 5.67
read_merged_cells worksheet, work, pos, len
else
if ROW_BLOCK_OPS.include?(op)
set_missing_row_address worksheet, work, pos, len
Expand Down
20 changes: 20 additions & 0 deletions lib/spreadsheet/excel/reader/biff5.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@ def read_string work, count_length=1
length, = work.unpack fmt
work[count_length, length]
end

def read_range_address_list work, len
# Cell range address, BIFF2-BIFF5:
# Offset Size Contents
# 0 2 Index to first row
# 2 2 Index to last row
# 4 1 Index to first column
# 5 1 Index to last column
#
offset = 0, results = []
return results if len < 2
count = work[0..1].unpack('v').first
offset = 2
count.times do |i|
results << work[offset...offset+6].unpack('v2c2')
offset += 6
end
results
end

end
end
end
Expand Down
21 changes: 21 additions & 0 deletions lib/spreadsheet/excel/reader/biff8.rb
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,27 @@ def read_string_header work, count_length=1, offset=0
owing = chars - have_chrs
[chars, flagsize, wide, phonetic, richtext, avbl, owing, skip]
end

def read_range_address_list work, len
# Cell range address, BIFF8:
# Offset Size Contents
# 0 2 Index to first row
# 2 2 Index to last row
# 4 2 Index to first column
# 6 2 Index to last column
# ! In several cases, BIFF8 still writes the BIFF2-BIFF5 format of a cell range address
# (using 8-bit values for the column indexes). This will be mentioned at the respective place.
#
offset = 0, results = []
return results if len < 2
count = work[0..1].unpack('v').first
offset = 2
count.times do |i|
results << work[offset...offset+8].unpack('v4')
offset += 8
end
results
end
##
# Insert null-characters into a compressed UTF-16 string
def wide string
Expand Down
15 changes: 15 additions & 0 deletions lib/spreadsheet/excel/writer/worksheet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ def write_from_scratch
# ○○ SELECTION ➜ 5.93
# ○ STANDARDWIDTH ➜ 5.101
# ○○ MERGEDCELLS ➜ 5.67
write_merged_cells
# ○ LABELRANGES ➜ 5.64
# ○ PHONETIC ➜ 5.77
# ○ Conditional Formatting Table ➜ 4.12
Expand Down Expand Up @@ -837,6 +838,20 @@ def write_window2
data = [ flags, 0, 0, 0, 0, 0 ].pack binfmt(:window2)
write_op opcode(:window2), data
end

def write_merged_cells
return unless @worksheet.merged_cells.any?
# FIXME standards say the record is limited by 1027 records at once
# And no CONTINUE is supported

merge_cells = @worksheet.merged_cells.dup
while (window = merge_cells.slice!(0...1027)).any?
count = window.size
data = ([count] + window.flatten).pack('v2v*')
write_op opcode(:mergedcells), data
end
end

def write_wsbool
bits = [
# Bit Mask Contents
Expand Down
9 changes: 8 additions & 1 deletion lib/spreadsheet/worksheet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Worksheet
include Spreadsheet::Encodings
include Enumerable
attr_accessor :name, :selected, :workbook
attr_reader :rows, :columns
attr_reader :rows, :columns, :merged_cells
def initialize opts={}
@default_format = nil
@selected = opts[:selected]
Expand All @@ -37,6 +37,7 @@ def initialize opts={}
@rows = []
@columns = []
@links = {}
@merged_cells = []
end
def active # :nodoc:
warn "Worksheet#active is deprecated. Please use Worksheet#selected instead."
Expand Down Expand Up @@ -251,6 +252,12 @@ def [] row, column
def []= row, column, value
row(row)[column] = value
end
##
# Merges multiple cells into one.
def merge_cells start_row, start_col, end_row, end_col
# FIXME enlarge or dup check
@merged_cells.push [start_row, end_row, start_col, end_col]
end
private
def index_of_first ary # :nodoc:
return unless ary
Expand Down

0 comments on commit e5906f3

Please sign in to comment.