<< 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