<< Masa.20101207-update-outline-spreadsheet | 2010 | Masa.20101203-update-outline-spreadsheet >>
Question
Experiment
lib/spreadsheet/excel/reader.rb#read_worksheet
def read_worksheet worksheet, offset
@pos = offset
@detected_rows = {}
previous = nil
while tuple = get_next_chunk
pos, op, len, work = tuple
if((offset = @current_row_block_offset) && !in_row_block?(op, previous))
@current_row_block_offset = nil
offset[1] = pos - offset[0]
end
case op
when :eof # ● EOF ➜ 6.36
postread_worksheet worksheet
return
when :guts
p "found guts"
...
test.rb (edit a little bit and just copy a xls file)
require 'spreadsheet'
file = ARGV[0]
book = Spreadsheet.open(file, 'rb')
sheet= book.worksheet(0)
5.times {|i| sheet[0,i] = i}
book.write "out.xls"
Result
masa@masa ~/ywesee/spreadsheet $ ruby -I lib test.rb hidden.xls "found guts" "found guts" "found guts" masa@masa ~/ywesee/spreadsheet $ ruby -I lib test.rb outline.xls "found guts" "found guts" "found guts" masa@masa ~/ywesee/spreadsheet $ ruby -I lib test.rb hidden_by_sp.xls masa@masa ~/ywesee/spreadsheet $
Notes
Question
Reference
This record contains information about the layout of outline symbols. Offset Size Contents 0 2 Width of the area to display row outlines (left of the sheet), in pixel 2 2 Height of the area to display column outlines (above the sheet), in pixel 4 2 Number of visible row outline levels (used row levels + 1; or 0, if not used) 6 2 Number of visible column outline levels (used column levels + 1; or 0, if not used)
References
Memo
1.3 Byte Order All data items containing more than one byte are stored using the Little-Endian method.
Biff GUTS of hidden.xls and outline.xls (8 byte)
hidden.xls: BIFF GUTS (80h) 8 00 00 00 00 00 00 00 00 outline.xls: BIFF GUTS (80h) 8 00 00 1D 00 00 00 02 00
outline.xls
These are hex number in little endian, so
Experiment
lib/spreadsheet/excel/reader.rb#read_worksheet
def read_worksheet worksheet, offset
@pos = offset
@detected_rows = {}
previous = nil
while tuple = get_next_chunk
pos, op, len, work = tuple
if((offset = @current_row_block_offset) && !in_row_block?(op, previous))
@current_row_block_offset = nil
offset[1] = pos - offset[0]
end
case op
...
when :guts # GUTS 5.53
read_guts worksheet, work, pos, len
...
end
...
def read_guts worksheet, work, pos, len
# Offset Size Contents
# 0 2 Width of the area to display row outlines (left of the sheet), in pixel
# 2 2 Height of the area to display column outlines (above the sheet), in pixel
# 4 2 Number of visible row outline levels (used row levels + 1; or 0, if not used)
# 6 2 Number of visible column outline levels (used column levels + 1; or 0, if not used)
p "found guts"
print "pos=\t\t", pos, "\n"
print "len=\t\t", len, "\n"
width, height, row_level, col_level = work.unpack 'v4'
print "width=\t\t", width, "\n"
print "height=\t\t", height, "\n"
print "row_level=\t", row_level, "\n"
print "col_level=\t", col_level, "\n"
puts
end
Result
masa@masa ~/ywesee/spreadsheet $ ruby -I lib test.rb hidden.xls "found guts" pos= 12162 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "found guts" pos= 12638 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "found guts" pos= 13028 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 masa@masa ~/ywesee/spreadsheet $ ruby -I lib test.rb outline.xls "found guts" pos= 12162 len= 12 width= 0 height= 29 row_level= 0 col_level= 2 "found guts" pos= 12638 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "found guts" pos= 13028 len= 12 width= 0 height= 0 row_level= 0 col_level= 0
Notes
Memo
Experiment
lib/spreadsheet/excel/writer/worksheet.rb
def write_guts
data = [0,29,0,2]
write_op opcode(:guts), data.pack('v4')
end
create_new_file.rb
require 'spreadsheet'
book = Spreadsheet::Workbook.new
sheet = book.create_worksheet
5.times {|i| sheet[0,i] = i}
sheet.column(2).hidden = true
#sheet.column(2).outline_level = 256
sheet.column(3).hidden = true
#sheet.column(3).outline_level = 256
book.write 'out.xls'
Create a new file
masa@masa ~/ywesee/spreadsheet $ ruby -I lib create_new_file.rb
Note
Check GUTS
masa@masa ~/ywesee/spreadsheet $ ruby -I lib edit_little.rb out.xls "found guts" pos= 641 len= 12 width= 0 height= 29 row_level= 0 col_level= 2 masa@masa ~/ywesee/spreadsheet $ ruby -I lib edit_little.rb outline.xls "found guts" pos= 12162 len= 12 width= 0 height= 29 row_level= 0 col_level= 2 "found guts" pos= 12638 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "found guts" pos= 13028 len= 12 width= 0 height= 0 row_level= 0 col_level= 0
Notes
Experiment
lib/spreadsheet/excel/reader.rb#read_colinfo
def read_colinfo worksheet, work, pos, len
p "getin read_colinfo"
# Offset Size Contents
# 0 2 Index to first column in the range
# 2 2 Index to last column in the range
# 4 2 Width of the columns in 1/256 of the width of the zero
# character, using default font (first FONT record in the
# file)
# 6 2 Index to XF record (➜ 6.115) for default column formatting
# 8 2 Option flags:
# Bits Mask Contents
# 0 0x0001 1 = Columns are hidden
# 10-8 0x0700 Outline level of the columns (0 = no outline)
# 12 0x1000 1 = Columns are collapsed
# 10 2 Not used
first, last, width, xf, opts = work.unpack binfmt(:colinfo)[0..-2]
#print "binfmt(:colinfo)=", binfmt(:colinfo), "\n"
#print "binfmt(:colinfo)[0..-2]=", binfmt(:colinfo)[0..-2], "\n"
print "first - last=\t", first, " - ", last, "\n"
print "width=\t\t", width, "\n"
print "xl=\t\t", xf, "\n"
print "hidden=\t\t", (opts & 0x0001) > 0,"\n"
print "collapsed=\t", (opts & 0x1000) > 0, "\n"
print "outline_level=\t",(opts & 0x0700),"\n\n"
lib/spreadsheet/excel/writer/worksheet.rb#write_colinfo
def write_colinfo bunch
col = bunch.first
width = col.width.to_f * 256
xf_idx = @workbook.xf_index @worksheet.workbook, col.default_format
opts = 0
opts |= 0x0001 if col.hidden?
#opts |= col.outline_level.to_i << 8
opts |= col.outline_level
create_new_file.rb
require 'spreadsheet'
book = Spreadsheet::Workbook.new
sheet = book.create_worksheet
5.times {|i| sheet[0,i] = i}
sheet.column(2).hidden = true
sheet.column(2).outline_level = 256
sheet.column(3).hidden = true
sheet.column(3).outline_level = 256
book.write 'out.xls'
Result
masa@masa ~/ywesee/spreadsheet $ ruby -I lib edit_little.rb test.xls "found guts" pos= 641 len= 12 width= 0 height= 29 row_level= 0 col_level= 2 "getin read_colinfo" first - last= 0 - 1 width= 2560 xl= 0 hidden= false collapsed= false outline_level= 0 "getin read_colinfo" first - last= 2 - 3 width= 2560 xl= 0 hidden= true collapsed= false outline_level= 256 "getin read_colinfo" first - last= 4 - 4 width= 2560 xl= 0 hidden= false collapsed= false outline_level= 0 masa@masa ~/ywesee/spreadsheet $ ruby -I lib edit_little.rb outline.xls "found guts" pos= 12162 len= 12 width= 0 height= 29 row_level= 0 col_level= 2 "getin read_colinfo" first - last= 2 - 3 width= 2925 xl= 15 hidden= false collapsed= false outline_level= 256 "found guts" pos= 12638 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "found guts" pos= 13028 len= 12 width= 0 height= 0 row_level= 0 col_level= 0
Notes
5.110.2 Option Flags Option flags, BIFF3-BIFF8: Bits Mask Contents 0 0001H 0 = Show formula results 1 = Show formulas 1 0002H 0 = Do not show grid lines 1 = Show grid lines 2 0004H 0 = Do not show sheet headers 1 = Show sheet headers 3 0008H 0 = Panes are not frozen 1 = Panes are frozen (freeze) 4 0010H 0 = Show zero values as empty cells 1 = Show zero values 5 0020H 0 = Manual grid line colour 1 = Automatic grid line colour 6 0040H 0 = Columns from left to right 1 = Columns from right to left 7 0080H 0 = Do not show outline symbols 1 = Show outline symbols 8 0100H 0 = Keep splits if pane freeze is removed 1 = Remove splits if pane freeze is removed 9 0200H 0 = Sheet not selected 1 = Sheet selected (BIFF5-BIFF8) 10 0400H 0 = Sheet not active 1 = Sheet active (BIFF5-BIFF8) 11 0800H 0 = Show in normal view 1 = Show in page break preview (BIFF8)
Note
Check Biff data
Experiment
def read_window2 worksheet, work, pos, len
...
flags, _ = work.unpack 'v'
p "getin read_window2"
print "show outline symbol=\t", (flags & 0x0080) > 0, "\n\n"
worksheet.selected = flags & 0x0200 > 0
end
Result
masa@masa ~/ywesee/spreadsheet $ ruby -I lib edit_little.rb hidden.xls "getin read_window2" show outline symbol= true "getin read_window2" show outline symbol= true "getin read_window2" show outline symbol= true masa@masa ~/ywesee/spreadsheet $ ruby -I lib edit_little.rb outline.xls "getin read_window2" show outline symbol= true "getin read_window2" show outline symbol= true "getin read_window2" show outline symbol= true masa@masa ~/ywesee/spreadsheet $ ruby -I lib edit_little.rb hidden_by_sp.xls "getin read_window2" show outline symbol= false
Note
Experiment
lib/spreadsheet/excel/writer/worksheet.rb#write_window2
def write_window2
flags = 0x0536 # Show grid lines, sheet headers, zero values. Automatic
# grid line colour, Remove slits if pane freeze is removed,
# Sheet is active.
if @worksheet.selected
flags |= 0x0200
end
flags |= 0x0080
data = [ flags, 0, 0, 0, 0, 0 ].pack binfmt(:window2)
write_op opcode(:window2), data
end
Create out.xls
masa@masa ~/ywesee/spreadsheet $ cat create_new_file.rb
require 'spreadsheet'
book = Spreadsheet::Workbook.new
sheet = book.create_worksheet
5.times {|i| sheet[0,i] = i}
sheet.column(2).hidden = true
sheet.column(2).outline_level = 256
sheet.column(3).hidden = true
sheet.column(3).outline_level = 256
book.write 'out.xls'
masa@masa ~/ywesee/spreadsheet $ ruby -I lib create_new_file.rb
Result


Note
Experiment
create_new_file.rb
require 'spreadsheet'
book = Spreadsheet::Workbook.new
sheet = book.create_worksheet
5.times {|i| sheet[0,i] = i}
sheet.column(2).hidden = true
#sheet.column(2).outline_level = 256
sheet.column(3).hidden = true
#sheet.column(3).outline_level = 256
book.write 'out.xls'
Create a new file
masa@masa ~/ywesee/spreadsheet $ ruby -I lib create_new_file.rb
Result
Experiment
create_new_file.rb
require 'spreadsheet'
book = Spreadsheet::Workbook.new
sheet = book.create_worksheet
5.times {|i| sheet[0,i] = i}
#sheet.column(2).hidden = true
sheet.column(2).outline_level = 256
#sheet.column(3).hidden = true
sheet.column(3).outline_level = 256
book.write 'out.xls'
Result
Summary
Experiment
create_new_file.rb
require 'spreadsheet'
book = Spreadsheet::Workbook.new
sheet = book.create_worksheet
5.times {|j| 5.times {|i| sheet[j,i] = (i+1)*10**j}}
# col
sheet.column(2).hidden = true
sheet.column(2).outline_level = 256
sheet.column(3).hidden = true
sheet.column(3).outline_level = 256
# row
sheet.row(2).hidden = true
sheet.row(2).outline_level = 256
sheet.row(3).hidden = true
sheet.row(3).outline_level = 256
book.write 'out.xls'
Create a file
masa@masa ~/ywesee/spreadsheet $ ruby -I lib create_new_file.rb
Result

Hypothesis
Preparing sample xls files made by MS Office
Diff
Notes
Experiment (check row.outline_level and row.hidden?)
edit_little.rb
require 'spreadsheet'
file = ARGV[0]
book = Spreadsheet.open(file, 'rb')
sheet= book.worksheet(0)
5.times {|i| sheet[0,i] = i}
book.write "out.xls"
Result
masa@masa ~/ywesee/spreadsheet $ ruby -I lib edit_little.rb normal.xls "found guts" pos= 12162 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "getin read_window2" show outline symbol= true "found guts" pos= 12810 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "getin read_window2" show outline symbol= true "found guts" pos= 13200 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "getin read_window2" show outline symbol= true "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=0 row.hidden?=false
masa@masa ~/ywesee/spreadsheet $ ruby -I lib edit_little.rb hidden.xls "found guts" pos= 12162 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "getin read_window2" show outline symbol= true "found guts" pos= 12810 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "getin read_window2" show outline symbol= true "found guts" pos= 13200 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "getin read_window2" show outline symbol= true "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=0 row.hidden?=true "getin write_row" row.outline_level=0 row.hidden?=true "getin write_row" row.outline_level=0 row.hidden?=false
masa@masa ~/ywesee/spreadsheet $ ruby -I lib edit_little.rb outline.xls "found guts" pos= 12162 len= 12 width= 29 height= 0 row_level= 2 col_level= 0 "getin read_window2" show outline symbol= true "found guts" pos= 12810 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "getin read_window2" show outline symbol= true "found guts" pos= 13200 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "getin read_window2" show outline symbol= true "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=1 row.hidden?=false "getin write_row" row.outline_level=1 row.hidden?=false "getin write_row" row.outline_level=0 row.hidden?=false
masa@masa ~/ywesee/spreadsheet $ ruby -I lib edit_little.rb outline_hidden.xls "found guts" pos= 12162 len= 12 width= 29 height= 0 row_level= 2 col_level= 0 "getin read_window2" show outline symbol= true "found guts" pos= 12810 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "getin read_window2" show outline symbol= true "found guts" pos= 13200 len= 12 width= 0 height= 0 row_level= 0 col_level= 0 "getin read_window2" show outline symbol= true "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=1 row.hidden?=true "getin write_row" row.outline_level=1 row.hidden?=true "getin write_row" row.outline_level=0 row.hidden?=false
Notes
Experiment
create_new_file.rb
require 'spreadsheet'
book = Spreadsheet::Workbook.new
sheet = book.create_worksheet
5.times {|j| 5.times {|i| sheet[j,i] = (i+1)*10**j}}
# col
sheet.column(2).hidden = true
sheet.column(2).outline_level = 256
sheet.column(3).hidden = true
sheet.column(3).outline_level = 256
# row
sheet.row(2).hidden = true
sheet.row(2).outline_level = 1
sheet.row(3).hidden = true
sheet.row(3).outline_level = 1
book.write 'out.xls'
Create a new file
masa@masa ~/ywesee/spreadsheet $ ruby -I lib create_new_file.rb "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=1 row.hidden?=true "getin write_row" row.outline_level=1 row.hidden?=true "getin write_row" row.outline_level=0 row.hidden?=false
Result


Experiment
lib/spreadsheet/excel/writer/worksheet.rb#write_guts
def write_guts
#data = [0,29,0,2]
data = [29,29,2,2]
write_op opcode(:guts), data.pack('v4')
end
create_new_file.rb
require 'spreadsheet'
book = Spreadsheet::Workbook.new
sheet = book.create_worksheet
5.times {|j| 5.times {|i| sheet[j,i] = (i+1)*10**j}}
# col
sheet.column(2).hidden = true
sheet.column(2).outline_level = 256
sheet.column(3).hidden = true
sheet.column(3).outline_level = 256
# row
sheet.row(2).hidden = true
sheet.row(2).outline_level = 1
sheet.row(3).hidden = true
sheet.row(3).outline_level = 1
book.write 'out.xls'
Create a xls file
masa@masa ~/ywesee/spreadsheet $ ruby -I lib create_new_file.rb "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=1 row.hidden?=true "getin write_row" row.outline_level=1 row.hidden?=true "getin write_row" row.outline_level=0 row.hidden?=false
Check GUTS entry
masa@masa ~/ywesee/spreadsheet $ mv out.xls test.xls masa@masa ~/ywesee/spreadsheet $ ruby -I lib edit_little.rb test.xls "found guts" pos= 641 len= 12 width= 29 height= 29 row_level= 2 col_level= 2 "getin read_window2" show outline symbol= true "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=0 row.hidden?=false "getin write_row" row.outline_level=1 row.hidden?=true "getin write_row" row.outline_level=1 row.hidden?=true "getin write_row" row.outline_level=0 row.hidden?=false
Result

Note
GUTS information is important in rows and columns outline (grouping)
BraSt
Main
In order to show outline button,
WINDOWS flag masked by 0x0080 should be '1'
GUTS entry should be written
Memo
lib/spreadsheet/excel/writer/worksheet.rb|write_window2
def write_window2
flags = 0x0536 # Show grid lines, sheet headers, zero values. Automatic
# grid line colour, Remove slits if pane freeze is removed,
# Sheet is active.
if @worksheet.selected
flags |= 0x0200
end
flags |= 0x0080 # Show outline symbols
data = [ flags, 0, 0, 0, 0, 0 ].pack binfmt(:window2)
write_op opcode(:window2), data
end
lib/spreadsheet/excel/worksheet.rb
module Spreadsheet
module Excel
##
# Excel-specific Worksheet methods. These are mostly pertinent to the Excel
# reader, and to recording changes to the Worksheet. You should have no reason
# to use any of these.
class Worksheet < Spreadsheet::Worksheet
include Spreadsheet::Excel::Offset
offset :dimensions
attr_reader :offset, :ole, :links, :guts
def initialize opts = {}
@row_addresses = nil
super
@offset, @ole, @reader = opts[:offset], opts[:ole], opts[:reader]
@dimensions = nil
@links = {}
@guts = {}
end
lib/spreadsheet/excel/reader.rb
def read_guts worksheet, work, pos, len
# Offset Size Contents
# 0 2 Width of the area to display row outlines (left of the sheet), in pixel
# 2 2 Height of the area to display column outlines (above the sheet), in pixel
# 4 2 Number of visible row outline levels (used row levels + 1; or 0, if not used)
# 6 2 Number of visible column outline levels (used column levels + 1; or 0, if not used)
width, height, row_level, col_level = work.unpack 'v4'
worksheet.guts[:width] = width
worksheet.guts[:height] = height
worksheet.guts[:row_level] = row_level
worksheet.guts[:col_level] = col_level
end
lib/spreadsheet/excel/writer/worksheet.rb
def write_guts
data = [
self.guts[:width],
self.guts[:height],
self.guts[:row_level],
self.guts[:col_level]
]
write_op opcode(:guts), data.pack('v4')
end
Experiment
edit_little.rb
require 'spreadsheet'
file = ARGV[0]
book = Spreadsheet.open(file, 'rb')
sheet= book.worksheet(0)
5.times {|i| sheet[0,i] = i}
sheet.row(2).hidden = true
sheet.row(3).hidden = true
sheet.row(2).outline_level = 1
sheet.row(3).outline_level = 1
book.write "out.xls"
Run
masa@masa ~/ywesee/spreadsheet $ ruby -I lib edit_little.rb normal.xls
Result

