spreadsheet-0.9.0/0000755000004100000410000000000012216006760014057 5ustar www-datawww-dataspreadsheet-0.9.0/.travis.yml0000644000004100000410000000102112216006760016162 0ustar www-datawww-datalanguage: ruby before_install: - gem install bundler bundler_args: --binstubs script: "ruby -w ./test/suite.rb" rvm: - ruby-head - 2.0.0 - 1.9.3 - 1.9.2 - 1.8.7 - rbx-19mode - rbx-18mode - jruby-head - jruby-19mode - jruby-18mode - ree matrix: allow_failures: - rvm: ruby-head - rvm: rbx-19mode - rvm: rbx-18mode - rvm: jruby-head - rvm: jruby-19mode - rvm: jruby-18mode - rvm: ree notifications: email: recipients: - yasaka@ywesee.com - zdavatz@ywesee.com spreadsheet-0.9.0/test/0000755000004100000410000000000012216006760015036 5ustar www-datawww-dataspreadsheet-0.9.0/test/excel/0000755000004100000410000000000012216006760016136 5ustar www-datawww-dataspreadsheet-0.9.0/test/excel/row.rb0000644000004100000410000000206112216006760017271 0ustar www-datawww-data#!/usr/bin/env ruby # Excel::TestRow -- Spreadsheet -- 12.10.2008 -- hwyss@ywesee.com $: << File.expand_path('../../lib', File.dirname(__FILE__)) require 'test/unit' require 'spreadsheet' module Spreadsheet module Excel class TestRow < Test::Unit::TestCase def setup @workbook = Excel::Workbook.new @worksheet = Excel::Worksheet.new @workbook.add_worksheet @worksheet end def test_date row = Row.new @worksheet, 0, [nil, 27627.6789] assert_equal Date.new(1975,8,21), row.date(1) end def test_to_a row = Row.new @worksheet, 0, [nil, 1, 27627.6789] row.set_format(2, Format.new(:number_format => 'DD.MM.YYYY')) assert_equal [nil, 1, Date.new(1975, 8, 21)], row.to_a end def test_datetime row = Row.new @worksheet, 0, [nil, 27627.765] d1 = DateTime.new(1975,8,21) + 0.765 d2 = row.datetime 1 assert_equal d1, d2 end def test_datetime_overflow row = Row.new @worksheet, 0, [nil, 40010.6666666666] d1 = DateTime.new(2009,07,16,16) d2 = row.datetime 1 assert_equal d1, d2 end end end end spreadsheet-0.9.0/test/excel/reader.rb0000755000004100000410000000143612216006760017734 0ustar www-datawww-data#!/usr/bin/env ruby # Excel::TestReader -- Spreadsheet -- 22.01.2013 $: << File.expand_path('../../../lib', File.dirname(__FILE__)) require 'test/unit' module Spreadsheet module Excel class TestReader < Test::Unit::TestCase def test_empty_file_error_on_setup reader = Spreadsheet::Excel::Reader.new empty_io = StringIO.new assert_raise RuntimeError do reader.setup empty_io end end def test_not_empty_file_error_on_setup reader = Spreadsheet::Excel::Reader.new data = File.expand_path File.join('test', 'data') path = File.join data, 'test_empty.xls' not_empty_io = File.open(path, "rb") assert_nothing_thrown do reader.setup not_empty_io end end end end endspreadsheet-0.9.0/test/excel/writer/0000755000004100000410000000000012216006760017452 5ustar www-datawww-dataspreadsheet-0.9.0/test/excel/writer/worksheet.rb0000644000004100000410000000424612216006760022020 0ustar www-datawww-data#!/usr/bin/env ruby # Excel::Writer::TestWorksheet -- Spreadheet -- 21.11.2007 -- hwyss@ywesee.com require 'test/unit' require 'spreadsheet/excel/writer/worksheet' module Spreadsheet module Excel module Writer class TestWorksheet < Test::Unit::TestCase def test_need_number sheet = Worksheet.new nil, nil assert_equal false, sheet.need_number?(10) assert_equal false, sheet.need_number?(114.55) assert_equal false, sheet.need_number?(0.1) assert_equal false, sheet.need_number?(0.01) assert_equal false, sheet.need_number?(0 / 0.0) # NaN assert_equal true, sheet.need_number?(0.001) assert_equal true, sheet.need_number?(10000000.0) end class RowMock attr_accessor :idx, :first_used, :first_unused, :height, :outline_level def initialize @idx, @first_used, @first_unused, @height, @outline_level = 0,0,0,0,1 end def method_missing name, *args nil end end def test_write_row_should_not_write_if_the_row_has_no_used_columns sheet = Worksheet.new nil, nil row = RowMock.new row.first_used = nil sheet.write_row row assert_equal '', sheet.data end def test_write_row_should_write_if_any_column_is_used sheet = Worksheet.new nil, nil row = RowMock.new sheet.write_row row assert_equal false, sheet.data.empty? end def test_strings book = Spreadsheet::Excel::Workbook.new sheet = book.create_worksheet writer = Worksheet.new book, sheet rowi = -1 assert_equal( {}, writer.strings ) sheet.row(rowi+=1).concat(["Hello", "World"]) assert_equal( {"Hello" => 1, "World" => 1}, writer.strings ) sheet.row(rowi+=1).concat(["Goodbye", "Cruel", "World", 2012]) assert_equal( {"Hello" => 1, "Goodbye" => 1, "Cruel" => 1, "World" => 2}, writer.strings ) end end end end end spreadsheet-0.9.0/test/excel/writer/workbook.rb0000644000004100000410000000644312216006760021643 0ustar www-datawww-data#!/usr/bin/env ruby # Excel::Writer::TestWorkbook -- Spreadsheet -- 20.07.2011 -- vanderhoorn@gmail.com $: << File.expand_path('../../../lib', File.dirname(__FILE__)) require 'test/unit' require 'spreadsheet' module Spreadsheet module Excel module Writer class TestWorkbook < Test::Unit::TestCase def setup @book = Spreadsheet::Excel::Workbook.new assert_instance_of Excel::Workbook, @book assert_equal @book.worksheets.size, 0 @workbook_writer = Excel::Writer::Workbook.new @book end def test_sanitize_worksheets assert_nothing_raised { @workbook_writer.sanitize_worksheets @book.worksheets } end def test_collect_formats assert_equal 17, @workbook_writer.collect_formats(@book).length # Expected for vanilla sheet = @book.create_worksheet rowi=-1 f1 = Spreadsheet::Format.new sheet.row(rowi+=1).default_format = f1 assert_equal 18, @workbook_writer.collect_formats(@book).length f2 = Spreadsheet::Format.new sheet.row(rowi+=1).default_format = f2 assert_equal 19, @workbook_writer.collect_formats(@book).length sheet.row(rowi+=1).default_format = f2 assert_equal 19, @workbook_writer.collect_formats(@book).length # Ignores duplicates end def test_xf_index sheet = @book.create_worksheet rowi=-1 f1 = Spreadsheet::Format.new sheet.row(rowi+=1).default_format = f1 @workbook_writer.collect_formats(@book) assert_equal 17, @workbook_writer.xf_index(@book, f1) f2 = Spreadsheet::Format.new sheet.row(rowi+=1).default_format = f2 @workbook_writer.collect_formats(@book) assert_equal 18, @workbook_writer.xf_index(@book, f2) end def test_write_fonts num_written = 0 sheet = @book.create_worksheet rowi=-1 # Stub inner #write_font as a counter: (class << @workbook_writer; self; end).send(:define_method, :write_font) do |*args| num_written += 1 end io = StringIO.new("") num_written = 0 @workbook_writer.collect_formats(@book) @workbook_writer.write_fonts(@book, io) assert_equal 1, num_written # Default format's font f1 = Spreadsheet::Format.new sheet.row(rowi+=1).default_format = f1 num_written = 0 @workbook_writer.collect_formats(@book) @workbook_writer.write_fonts(@book, io) assert_equal 1, num_written # No new fonts f2 = Spreadsheet::Format.new f2.font = Spreadsheet::Font.new("Foo") sheet.row(rowi+=1).default_format = f2 num_written = 0 @workbook_writer.collect_formats(@book) @workbook_writer.write_fonts(@book, io) assert_equal 2, num_written # 2 distinct fonts total f3 = Spreadsheet::Format.new f3.font = f2.font # Re-use previous font sheet.row(rowi+=1).default_format = f3 num_written = 0 @workbook_writer.collect_formats(@book) @workbook_writer.write_fonts(@book, io) assert_equal 2, num_written # 2 distinct fonts total still end end end end end spreadsheet-0.9.0/test/integration.rb0000644000004100000410000014620112216006760017712 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 # TestIntegration -- spreadheet -- 07.09.2011 -- mhatakeyama@ywesee.com # TestIntegration -- spreadheet -- 08.10.2007 -- hwyss@ywesee.com $: << File.expand_path('../lib', File.dirname(__FILE__)) require 'test/unit' require 'spreadsheet' require 'fileutils' module Spreadsheet class TestIntegration < Test::Unit::TestCase if RUBY_VERSION >= '1.9' class IconvStub def initialize to, from @to, @from = to, from end def iconv str dp = str.dup dp.force_encoding @from dp.encode @to end end @@iconv = IconvStub.new('UTF-16LE', 'UTF-8') @@bytesize = :bytesize else @@iconv = Iconv.new('UTF-16LE', 'UTF-8') @@bytesize = :size end def setup @var = File.expand_path 'var', File.dirname(__FILE__) FileUtils.mkdir_p @var @data = File.expand_path 'data', File.dirname(__FILE__) FileUtils.mkdir_p @data end def teardown Spreadsheet.client_encoding = 'UTF-8' FileUtils.rm_r @var end def test_copy__identical__file_paths path = File.join @data, 'test_copy.xls' copy = File.join @data, 'test_copy1.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book book.write copy assert_equal File.read(path), File.read(copy) ensure File.delete copy if File.exist? copy end def test_empty_workbook path = File.join @data, 'test_empty.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book assert_equal 8, book.biff_version assert_equal 'Microsoft Excel 97/2000/XP', book.version_string enc = 'UTF-16LE' if defined? Encoding enc = Encoding.find enc end assert_equal enc, book.encoding assert_equal 21, book.formats.size assert_equal 4, book.fonts.size assert_equal 0, book.sst.size sheet = book.worksheet 0 assert_equal 0, sheet.row_count assert_equal 0, sheet.column_count assert_nothing_raised do sheet.inspect end end def test_version_excel97__excel2010__utf16 Spreadsheet.client_encoding = 'UTF-16LE' assert_equal 'UTF-16LE', Spreadsheet.client_encoding path = File.join @data, 'test_version_excel97_2010.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book assert_equal 8, book.biff_version assert_equal @@iconv.iconv('Microsoft Excel 97/2000/XP'), book.version_string enc = 'UTF-16LE' if defined? Encoding enc = Encoding.find enc end assert_equal enc, book.encoding sheet = book.worksheet 0 row = sheet.row 9 assert_equal 0.00009, row[0] link = row[1] assert_instance_of Link, link assert_equal @@iconv.iconv('Link-Text'), link assert_equal @@iconv.iconv('http://scm.ywesee.com/spreadsheet'), link.url assert_equal @@iconv.iconv('http://scm.ywesee.com/spreadsheet'), link.href end def test_version_excel97__ooffice__utf16 Spreadsheet.client_encoding = 'UTF-16LE' assert_equal 'UTF-16LE', Spreadsheet.client_encoding path = File.join @data, 'test_version_excel97.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book assert_equal 8, book.biff_version assert_equal @@iconv.iconv('Microsoft Excel 97/2000/XP'), book.version_string enc = 'UTF-16LE' if defined? Encoding enc = Encoding.find enc end assert_equal enc, book.encoding assert_equal 25, book.formats.size assert_equal 5, book.fonts.size str1 = @@iconv.iconv('Shared String') str2 = @@iconv.iconv('Another Shared String') str3 = @@iconv.iconv('1234567890 ' * 1000) str4 = @@iconv.iconv('9876543210 ' * 1000) assert_valid_sst(book, :contains => [str1, str2, str3, str4]) sheet = book.worksheet 0 assert_equal 11, sheet.row_count assert_equal 12, sheet.column_count useds = [0,0,0,0,0,0,0,1,0,0,11] unuseds = [2,2,1,1,1,2,1,11,1,2,12] sheet.each do |row| assert_equal useds.shift, row.first_used assert_equal unuseds.shift, row.first_unused end assert unuseds.empty?, "not all rows were visited in Spreadsheet#each" row = sheet.row 0 assert_equal str1, row[0] assert_equal str1, sheet[0,0] assert_equal str1, sheet.cell(0,0) assert_equal str1, row[1] assert_equal str1, sheet[0,1] assert_equal str1, sheet.cell(0,1) row = sheet.row 1 assert_equal str2, row[0] assert_equal str2, sheet[1,0] assert_equal str2, sheet.cell(1,0) assert_equal str2, row[1] assert_equal str2, sheet[1,1] assert_equal str2, sheet.cell(1,1) row = sheet.row 2 assert_equal str3, row[0] assert_equal str3, sheet[2,0] assert_equal str3, sheet.cell(2,0) assert_nil row[1] assert_nil sheet[2,1] assert_nil sheet.cell(2,1) row = sheet.row 3 assert_equal str4, row[0] assert_equal str4, sheet[3,0] assert_equal str4, sheet.cell(3,0) assert_nil row[1] assert_nil sheet[3,1] assert_nil sheet.cell(3,1) row = sheet.row 4 assert_equal 0.25, row[0] assert_equal 0.25, sheet[4,0] assert_equal 0.25, sheet.cell(4,0) row = sheet.row 5 assert_equal 0.75, row[0] assert_equal 0.75, sheet[5,0] assert_equal 0.75, sheet.cell(5,0) date = Date.new 1975, 8, 21 assert_equal date, row[1] assert_equal date, sheet[5,1] assert_equal date, sheet.cell(5,1) row = sheet.row 6 assert_equal 1, row[0] assert_equal 1, sheet[6,0] assert_equal 1, sheet.cell(6,0) row = sheet.row 7 assert_nil row[0] assert_equal [1,2,3,4,5,6,7,8,9,0], row[1,10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet[7,1..10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet.cell(7,1..10) row = sheet.row 8 assert_equal 0.0001, row[0] row = sheet.row 9 assert_equal 0.00009, row[0] assert_equal :green, sheet.row(10).format(11).pattern_fg_color end def test_version_excel97__ooffice path = File.join @data, 'test_version_excel97.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book assert_equal 8, book.biff_version assert_equal 'Microsoft Excel 97/2000/XP', book.version_string enc = 'UTF-16LE' if defined? Encoding enc = Encoding.find enc end assert_equal enc, book.encoding assert_equal 25, book.formats.size assert_equal 5, book.fonts.size str1 = 'Shared String' str2 = 'Another Shared String' str3 = '1234567890 ' * 1000 str4 = '9876543210 ' * 1000 assert_valid_sst(book, :contains => [str1, str2, str3, str4]) sheet = book.worksheet 0 assert_equal 11, sheet.row_count assert_equal 12, sheet.column_count useds = [0,0,0,0,0,0,0,1,0,0,11] unuseds = [2,2,1,1,1,2,1,11,1,2,12] sheet.each do |row| assert_equal useds.shift, row.first_used assert_equal unuseds.shift, row.first_unused end assert unuseds.empty?, "not all rows were visited in Spreadsheet#each" row = sheet.row 0 assert_equal str1, row[0] assert_equal str1, sheet[0,0] assert_equal str1, sheet.cell(0,0) assert_equal str1, row[1] assert_equal str1, sheet[0,1] assert_equal str1, sheet.cell(0,1) row = sheet.row 1 assert_equal str2, row[0] assert_equal str2, sheet[1,0] assert_equal str2, sheet.cell(1,0) assert_equal str2, row[1] assert_equal str2, sheet[1,1] assert_equal str2, sheet.cell(1,1) row = sheet.row 2 assert_equal str3, row[0] assert_equal str3, sheet[2,0] assert_equal str3, sheet.cell(2,0) assert_nil row[1] assert_nil sheet[2,1] assert_nil sheet.cell(2,1) row = sheet.row 3 assert_equal str4, row[0] assert_equal str4, sheet[3,0] assert_equal str4, sheet.cell(3,0) assert_nil row[1] assert_nil sheet[3,1] assert_nil sheet.cell(3,1) row = sheet.row 4 assert_equal 0.25, row[0] assert_equal 0.25, sheet[4,0] assert_equal 0.25, sheet.cell(4,0) row = sheet.row 5 assert_equal 0.75, row[0] assert_equal 0.75, sheet[5,0] assert_equal 0.75, sheet.cell(5,0) date = Date.new 1975, 8, 21 assert_equal date, row[1] assert_equal date, sheet[5,1] assert_equal date, sheet.cell(5,1) row = sheet.row 6 assert_equal 1, row[0] assert_equal 1, sheet[6,0] assert_equal 1, sheet.cell(6,0) row = sheet.row 7 assert_nil row[0] assert_equal [1,2,3,4,5,6,7,8,9,0], row[1,10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet[7,1..10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet.cell(7,1..10) row = sheet.row 8 assert_equal 0.0001, row[0] row = sheet.row 9 assert_equal 0.00009, row[0] link = row[1] assert_instance_of Link, link assert_equal 'Link-Text', link assert_equal 'http://scm.ywesee.com/spreadsheet', link.url assert_equal 'http://scm.ywesee.com/spreadsheet', link.href end def test_version_excel95__ooffice__utf16 Spreadsheet.client_encoding = 'UTF-16LE' path = File.join @data, 'test_version_excel95.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book assert_equal 5, book.biff_version assert_equal @@iconv.iconv('Microsoft Excel 95'), book.version_string enc = 'WINDOWS-1252' if defined? Encoding enc = Encoding.find enc end assert_equal enc, book.encoding str1 = @@iconv.iconv('Shared String') str2 = @@iconv.iconv('Another Shared String') str3 = @@iconv.iconv(('1234567890 ' * 26)[0,255]) str4 = @@iconv.iconv(('9876543210 ' * 26)[0,255]) sheet = book.worksheet 0 assert_equal 8, sheet.row_count assert_equal 11, sheet.column_count useds = [0,0,0,0,0,0,0,1] unuseds = [2,2,1,1,1,1,1,11] sheet.each do |row| assert_equal useds.shift, row.first_used assert_equal unuseds.shift, row.first_unused end assert unuseds.empty?, "not all rows were visited in Spreadsheet#each" row = sheet.row 0 assert_equal str1, row[0] assert_equal str1, sheet[0,0] assert_equal str1, sheet.cell(0,0) assert_equal str1, row[1] assert_equal str1, sheet[0,1] assert_equal str1, sheet.cell(0,1) row = sheet.row 1 assert_equal str2, row[0] assert_equal str2, sheet[1,0] assert_equal str2, sheet.cell(1,0) assert_equal str2, row[1] assert_equal str2, sheet[1,1] assert_equal str2, sheet.cell(1,1) row = sheet.row 2 assert_equal 510, row[0].send(@@bytesize) assert_equal str3, row[0] assert_equal str3, sheet[2,0] assert_equal str3, sheet.cell(2,0) assert_nil row[1] assert_nil sheet[2,1] assert_nil sheet.cell(2,1) row = sheet.row 3 assert_equal 510, row[0].send(@@bytesize) assert_equal str4, row[0] assert_equal str4, sheet[3,0] assert_equal str4, sheet.cell(3,0) assert_nil row[1] assert_nil sheet[3,1] assert_nil sheet.cell(3,1) row = sheet.row 4 assert_equal 0.25, row[0] assert_equal 0.25, sheet[4,0] assert_equal 0.25, sheet.cell(4,0) row = sheet.row 5 assert_equal 0.75, row[0] assert_equal 0.75, sheet[5,0] assert_equal 0.75, sheet.cell(5,0) row = sheet.row 6 assert_equal 1, row[0] assert_equal 1, sheet[6,0] assert_equal 1, sheet.cell(6,0) row = sheet.row 7 assert_nil row[0] assert_equal [1,2,3,4,5,6,7,8,9,0], row[1,10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet[7,1..10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet.cell(7,1..10) end def test_version_excel95__ooffice path = File.join @data, 'test_version_excel95.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book assert_equal 5, book.biff_version assert_equal 'Microsoft Excel 95', book.version_string enc = 'WINDOWS-1252' if defined? Encoding enc = Encoding.find enc end assert_equal enc, book.encoding str1 = 'Shared String' str2 = 'Another Shared String' str3 = ('1234567890 ' * 26)[0,255] str4 = ('9876543210 ' * 26)[0,255] sheet = book.worksheet 0 assert_equal 8, sheet.row_count assert_equal 11, sheet.column_count useds = [0,0,0,0,0,0,0,1] unuseds = [2,2,1,1,1,1,1,11] sheet.each do |row| assert_equal useds.shift, row.first_used assert_equal unuseds.shift, row.first_unused end assert unuseds.empty?, "not all rows were visited in Spreadsheet#each" row = sheet.row 0 assert_equal str1, row[0] assert_equal str1, sheet[0,0] assert_equal str1, sheet.cell(0,0) assert_equal str1, row[1] assert_equal str1, sheet[0,1] assert_equal str1, sheet.cell(0,1) row = sheet.row 1 assert_equal str2, row[0] assert_equal str2, sheet[1,0] assert_equal str2, sheet.cell(1,0) assert_equal str2, row[1] assert_equal str2, sheet[1,1] assert_equal str2, sheet.cell(1,1) row = sheet.row 2 assert_equal 255, row[0].send(@@bytesize) assert_equal str3, row[0] assert_equal str3, sheet[2,0] assert_equal str3, sheet.cell(2,0) assert_nil row[1] assert_nil sheet[2,1] assert_nil sheet.cell(2,1) row = sheet.row 3 assert_equal 255, row[0].send(@@bytesize) assert_equal str4, row[0] assert_equal str4, sheet[3,0] assert_equal str4, sheet.cell(3,0) assert_nil row[1] assert_nil sheet[3,1] assert_nil sheet.cell(3,1) row = sheet.row 4 assert_equal 0.25, row[0] assert_equal 0.25, sheet[4,0] assert_equal 0.25, sheet.cell(4,0) row = sheet.row 5 assert_equal 0.75, row[0] assert_equal 0.75, sheet[5,0] assert_equal 0.75, sheet.cell(5,0) row = sheet.row 6 assert_equal 1, row[0] assert_equal 1, sheet[6,0] assert_equal 1, sheet.cell(6,0) row = sheet.row 7 assert_nil row[0] assert_equal [1,2,3,4,5,6,7,8,9,0], row[1,10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet[7,1..10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet.cell(7,1..10) end def test_version_excel5__ooffice path = File.join @data, 'test_version_excel5.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book assert_equal 5, book.biff_version assert_equal 'Microsoft Excel 95', book.version_string enc = 'WINDOWS-1252' if defined? Encoding enc = Encoding.find enc end assert_equal enc, book.encoding str1 = 'Shared String' str2 = 'Another Shared String' str3 = ('1234567890 ' * 26)[0,255] str4 = ('9876543210 ' * 26)[0,255] sheet = book.worksheet 0 assert_equal 8, sheet.row_count assert_equal 11, sheet.column_count useds = [0,0,0,0,0,0,0,1] unuseds = [2,2,1,1,1,1,1,11] sheet.each do |row| assert_equal useds.shift, row.first_used assert_equal unuseds.shift, row.first_unused end assert unuseds.empty?, "not all rows were visited in Spreadsheet#each" row = sheet.row 0 assert_equal str1, row[0] assert_equal str1, sheet[0,0] assert_equal str1, sheet.cell(0,0) assert_equal str1, row[1] assert_equal str1, sheet[0,1] assert_equal str1, sheet.cell(0,1) row = sheet.row 1 assert_equal str2, row[0] assert_equal str2, sheet[1,0] assert_equal str2, sheet.cell(1,0) assert_equal str2, row[1] assert_equal str2, sheet[1,1] assert_equal str2, sheet.cell(1,1) row = sheet.row 2 assert_equal 255, row[0].send(@@bytesize) assert_equal str3, row[0] assert_equal str3, sheet[2,0] assert_equal str3, sheet.cell(2,0) assert_nil row[1] assert_nil sheet[2,1] assert_nil sheet.cell(2,1) row = sheet.row 3 assert_equal 255, row[0].send(@@bytesize) assert_equal str4, row[0] assert_equal str4, sheet[3,0] assert_equal str4, sheet.cell(3,0) assert_nil row[1] assert_nil sheet[3,1] assert_nil sheet.cell(3,1) row = sheet.row 4 assert_equal 0.25, row[0] assert_equal 0.25, sheet[4,0] assert_equal 0.25, sheet.cell(4,0) row = sheet.row 5 assert_equal 0.75, row[0] assert_equal 0.75, sheet[5,0] assert_equal 0.75, sheet.cell(5,0) row = sheet.row 6 assert_equal 1, row[0] assert_equal 1, sheet[6,0] assert_equal 1, sheet.cell(6,0) row = sheet.row 7 assert_nil row[0] assert_equal [1,2,3,4,5,6,7,8,9,0], row[1,10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet[7,1..10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet.cell(7,1..10) end def test_worksheets path = File.join @data, 'test_copy.xls' book = Spreadsheet.open path sheets = book.worksheets assert_equal 3, sheets.size sheet = book.worksheet 0 assert_instance_of Excel::Worksheet, sheet assert_equal sheet, book.worksheet('Sheet1') end def test_worksheets__utf16 Spreadsheet.client_encoding = 'UTF-16LE' path = File.join @data, 'test_copy.xls' book = Spreadsheet.open path sheets = book.worksheets assert_equal 3, sheets.size sheet = book.worksheet 0 assert_instance_of Excel::Worksheet, sheet str = "S\000h\000e\000e\000t\0001\000" if RUBY_VERSION >= '1.9' str.force_encoding 'UTF-16LE' if str.respond_to?(:force_encoding) end assert_equal sheet, book.worksheet(str) end def test_read_datetime path = File.join @data, 'test_datetime.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book sheet = book.worksheet 0 time = sheet[0,0] assert_equal 22, time.hour assert_equal 00, time.min assert_equal 00, time.sec time = sheet[1,0] assert_equal 1899, time.year assert_equal 12, time.month assert_equal 30, time.day assert_equal 22, time.hour assert_equal 30, time.min assert_equal 45, time.sec time = sheet[0,1] assert_equal 1899, time.year assert_equal 12, time.month assert_equal 31, time.day assert_equal 4, time.hour assert_equal 30, time.min assert_equal 45, time.sec end def test_change_encoding path = File.join @data, 'test_version_excel95.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book assert_equal 5, book.biff_version assert_equal 'Microsoft Excel 95', book.version_string enc = 'WINDOWS-1252' if defined? Encoding enc = Encoding.find enc end assert_equal enc, book.encoding enc = 'WINDOWS-1256' if defined? Encoding enc = Encoding.find enc end book.encoding = enc path = File.join @var, 'test_change_encoding.xls' book.write path assert_nothing_raised do book = Spreadsheet.open path end assert_equal enc, book.encoding end def test_change_cell path = File.join @data, 'test_version_excel97.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book assert_equal 8, book.biff_version assert_equal 'Microsoft Excel 97/2000/XP', book.version_string path = File.join @var, 'test_change_cell.xls' str1 = 'Shared String' str2 = 'Another Shared String' str3 = '1234567890 ' * 1000 str4 = '9876543210 ' * 1000 str5 = "Link-Text" assert_valid_sst(book, :is => [str1, str2, str3, str4, str5]) sheet = book.worksheet 0 sheet[0,0] = 4 row = sheet.row 1 row[0] = 3 book.write path assert_nothing_raised do book = Spreadsheet.open path end sheet = book.worksheet 0 assert_equal 11, sheet.row_count assert_equal 12, sheet.column_count useds = [0,0,0,0,0,0,0,0,0,0,0] unuseds = [2,2,1,1,1,2,1,11,1,2,12] sheet.each do |rw| assert_equal useds.shift, rw.first_used assert_equal unuseds.shift, rw.first_unused end assert unuseds.empty?, "not all rows were visited in Spreadsheet#each" row = sheet.row 0 assert_equal 4, row[0] assert_equal 4, sheet[0,0] assert_equal 4, sheet.cell(0,0) assert_equal str1, row[1] assert_equal str1, sheet[0,1] assert_equal str1, sheet.cell(0,1) row = sheet.row 1 assert_equal 3, row[0] assert_equal 3, sheet[1,0] assert_equal 3, sheet.cell(1,0) assert_equal str2, row[1] assert_equal str2, sheet[1,1] assert_equal str2, sheet.cell(1,1) row = sheet.row 2 assert_equal str3, row[0] assert_equal str3, sheet[2,0] assert_equal str3, sheet.cell(2,0) assert_nil row[1] assert_nil sheet[2,1] assert_nil sheet.cell(2,1) row = sheet.row 3 assert_equal str4, row[0] assert_equal str4, sheet[3,0] assert_equal str4, sheet.cell(3,0) assert_nil row[1] assert_nil sheet[3,1] assert_nil sheet.cell(3,1) row = sheet.row 4 assert_equal 0.25, row[0] assert_equal 0.25, sheet[4,0] assert_equal 0.25, sheet.cell(4,0) row = sheet.row 5 assert_equal 0.75, row[0] assert_equal 0.75, sheet[5,0] assert_equal 0.75, sheet.cell(5,0) date = Date.new 1975, 8, 21 assert_equal date, row[1] assert_equal date, sheet[5,1] assert_equal date, sheet.cell(5,1) row = sheet.row 6 assert_equal 1, row[0] assert_equal 1, sheet[6,0] assert_equal 1, sheet.cell(6,0) row = sheet.row 7 assert_nil row[0] assert_equal [1,2,3,4,5,6,7,8,9,0], row[1,10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet[7,1..10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet.cell(7,1..10) row = sheet.row 8 assert_equal 0.0001, row[0] row = sheet.row 9 assert_equal 0.00009, row[0] end def test_change_cell__complete_sst_rewrite path = File.join @data, 'test_version_excel97.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book assert_equal 8, book.biff_version assert_equal 'Microsoft Excel 97/2000/XP', book.version_string path = File.join @var, 'test_change_cell.xls' str1 = 'Shared String' str2 = 'Another Shared String' str3 = '1234567890 ' * 1000 str4 = '9876543210 ' * 1000 str5 = 'Link-Text' assert_valid_sst(book, :is => [str1, str2, str3, str4, str5]) sheet = book.worksheet 0 sheet[0,0] = 4 str6 = 'A completely different String' sheet[0,1] = str6 row = sheet.row 1 row[0] = 3 book.write path assert_nothing_raised do book = Spreadsheet.open path end assert_valid_sst(book, :is => [str2, str3, str4, str5, str6]) sheet = book.worksheet 0 assert_equal 11, sheet.row_count assert_equal 12, sheet.column_count useds = [0,0,0,0,0,0,0,0,0,0,0] unuseds = [2,2,1,1,1,2,1,11,1,2,12] sheet.each do |rw| assert_equal useds.shift, rw.first_used assert_equal unuseds.shift, rw.first_unused end assert unuseds.empty?, "not all rows were visited in Spreadsheet#each" row = sheet.row 0 assert_equal 4, row[0] assert_equal 4, sheet[0,0] assert_equal 4, sheet.cell(0,0) assert_equal str6, row[1] assert_equal str6, sheet[0,1] assert_equal str6, sheet.cell(0,1) row = sheet.row 1 assert_equal 3, row[0] assert_equal 3, sheet[1,0] assert_equal 3, sheet.cell(1,0) assert_equal str2, row[1] assert_equal str2, sheet[1,1] assert_equal str2, sheet.cell(1,1) row = sheet.row 2 assert_equal str3, row[0] assert_equal str3, sheet[2,0] assert_equal str3, sheet.cell(2,0) assert_nil row[1] assert_nil sheet[2,1] assert_nil sheet.cell(2,1) row = sheet.row 3 assert_equal str4, row[0] assert_equal str4, sheet[3,0] assert_equal str4, sheet.cell(3,0) assert_nil row[1] assert_nil sheet[3,1] assert_nil sheet.cell(3,1) row = sheet.row 4 assert_equal 0.25, row[0] assert_equal 0.25, sheet[4,0] assert_equal 0.25, sheet.cell(4,0) row = sheet.row 5 assert_equal 0.75, row[0] assert_equal 0.75, sheet[5,0] assert_equal 0.75, sheet.cell(5,0) date = Date.new 1975, 8, 21 assert_equal date, row[1] assert_equal date, sheet[5,1] assert_equal date, sheet.cell(5,1) row = sheet.row 6 assert_equal 1, row[0] assert_equal 1, sheet[6,0] assert_equal 1, sheet.cell(6,0) row = sheet.row 7 assert_nil row[0] assert_equal [1,2,3,4,5,6,7,8,9,0], row[1,10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet[7,1..10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet.cell(7,1..10) row = sheet.row 8 assert_equal 0.0001, row[0] row = sheet.row 9 assert_equal 0.00009, row[0] end def test_write_to_stringio book = Spreadsheet::Excel::Workbook.new sheet = book.create_worksheet :name => 'My Worksheet' sheet[0,0] = 'my cell' data = StringIO.new '' assert_nothing_raised do book.write data end assert_nothing_raised do book = Spreadsheet.open data end assert_instance_of Spreadsheet::Excel::Workbook, book assert_equal 1, book.worksheets.size sheet = book.worksheet 0 assert_equal 'My Worksheet', sheet.name assert_equal 'my cell', sheet[0,0] end def test_write_new_workbook book = Spreadsheet::Workbook.new path = File.join @var, 'test_write_workbook.xls' sheet1 = book.create_worksheet str1 = 'My Shared String' str2 = 'Another Shared String' assert_equal 1, (str1.size + str2.size) % 2, "str3 should start at an odd offset to test splitting of wide strings" str3 = '–––––––––– ' * 1000 str4 = '1234567890 ' * 1000 fmt1 = Format.new :italic => true, :color => :blue sheet1.format_column 1, fmt1, :width => 20 fmt2 = Format.new(:weight => :bold, :color => :yellow) sheet1.format_column 2, fmt2 sheet1.format_column 3, Format.new(:weight => :bold, :color => :red) sheet1.format_column 6..9, fmt1 sheet1.format_column [4,5,7], fmt2 sheet1.row(0).height = 20 sheet1[0,0] = str1 sheet1.row(0).push str1 sheet1.row(1).concat [str2, str2] sheet1[2,0] = str3 sheet1[3,0] = str4 fmt = Format.new :color => 'red' sheet1[4,0] = 0.25 sheet1.row(4).set_format 0, fmt fmt = Format.new :color => 'aqua' sheet1[5,0] = 0.75 sheet1.row(5).set_format 0, fmt link = Link.new 'http://scm.ywesee.com/?p=spreadsheet;a=summary', 'The Spreadsheet GitWeb', 'top' sheet1[5,1] = link sheet1[6,0] = 1 fmt = Format.new :color => 'green' sheet1.row(6).set_format 0, fmt sheet1[6,1] = Date.new 2008, 10, 10 sheet1[6,2] = Date.new 2008, 10, 12 fmt = Format.new :number_format => 'D.M.YY' sheet1.row(6).set_format 1, fmt sheet1.update_row 7, nil, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 sheet1[8,0] = 0.0005 sheet1[8,1] = 0.005 sheet1[8,2] = 0.05 sheet1[8,3] = 10.5 sheet1[8,4] = 1.05 sheet1[8,5] = 100.5 sheet1[8,6] = 10.05 sheet1[8,7] = 1.005 sheet1[9,0] = 100.5 sheet1[9,1] = 10.05 sheet1[9,2] = 1.005 sheet1[9,3] = 1000.5 sheet1[9,4] = 100.05 sheet1[9,5] = 10.005 sheet1[9,6] = 1.0005 sheet1[10,0] = 10000.5 sheet1[10,1] = 1000.05 sheet1[10,2] = 100.005 sheet1[10,3] = 10.0005 sheet1[10,4] = 1.00005 sheet1.insert_row 9, ['a', 'b', 'c'] assert_equal 'a', sheet1[9,0] assert_equal 'b', sheet1[9,1] assert_equal 'c', sheet1[9,2] sheet1.delete_row 9 row = sheet1.row(11) row.height = 40 row.push 'x' row.pop book.create_worksheet :name => 'my name' #=> sheet2 book.write path Spreadsheet.client_encoding = 'UTF-16LE' str1 = @@iconv.iconv str1 str2 = @@iconv.iconv str2 str3 = @@iconv.iconv str3 str4 = @@iconv.iconv str4 assert_nothing_raised do book = Spreadsheet.open path end if RUBY_VERSION >= '1.9' assert_equal 'UTF-16LE', book.encoding.name else assert_equal 'UTF-16LE', book.encoding end assert_valid_sst(book, :contains => [str1, str2, str3, str4]) assert_equal 2, book.worksheets.size sheet = book.worksheets.first assert_instance_of Spreadsheet::Excel::Worksheet, sheet name = "W\000o\000r\000k\000s\000h\000e\000e\000t\0001\000" name.force_encoding 'UTF-16LE' if name.respond_to?(:force_encoding) assert_equal name, sheet.name assert_not_nil sheet.offset assert_not_nil col = sheet.column(1) assert_equal true, col.default_format.font.italic? assert_equal :blue, col.default_format.font.color assert_equal 20, col.width row = sheet.row 0 assert_equal col.default_format, row.format(1) assert_equal 20, row.height assert_equal str1, row[0] assert_equal str1, sheet[0,0] assert_equal str1, sheet.cell(0,0) assert_equal str1, row[1] assert_equal str1, sheet[0,1] assert_equal str1, sheet.cell(0,1) row = sheet.row 1 assert_equal str2, row[0] assert_equal str2, sheet[1,0] assert_equal str2, sheet.cell(1,0) assert_equal str2, row[1] assert_equal str2, sheet[1,1] assert_equal str2, sheet.cell(1,1) row = sheet.row 2 assert_equal str3, row[0] assert_equal str3, sheet[2,0] assert_equal str3, sheet.cell(2,0) assert_nil row[1] assert_nil sheet[2,1] assert_nil sheet.cell(2,1) row = sheet.row 3 assert_equal str4, row[0] assert_equal str4, sheet[3,0] assert_equal str4, sheet.cell(3,0) assert_nil row[1] assert_nil sheet[3,1] assert_nil sheet.cell(3,1) row = sheet.row 4 assert_equal :red, row.format(0).font.color assert_equal 0.25, row[0] assert_equal 0.25, sheet[4,0] assert_equal 0.25, sheet.cell(4,0) row = sheet.row 5 assert_equal :cyan, row.format(0).font.color assert_equal 0.75, row[0] assert_equal 0.75, sheet[5,0] assert_equal 0.75, sheet.cell(5,0) link = row[1] assert_instance_of Link, link url = @@iconv.iconv 'http://scm.ywesee.com/?p=spreadsheet;a=summary' assert_equal @@iconv.iconv('The Spreadsheet GitWeb'), link assert_equal url, link.url assert_equal @@iconv.iconv('top'), link.fragment row = sheet.row 6 assert_equal :green, row.format(0).font.color assert_equal 1, row[0] assert_equal 1, sheet[6,0] assert_equal 1, sheet.cell(6,0) assert_equal @@iconv.iconv('D.M.YY'), row.format(1).number_format date = Date.new 2008, 10, 10 assert_equal date, row[1] assert_equal date, sheet[6,1] assert_equal date, sheet.cell(6,1) assert_equal @@iconv.iconv('DD.MM.YYYY'), row.format(2).number_format date = Date.new 2008, 10, 12 assert_equal date, row[2] assert_equal date, sheet[6,2] assert_equal date, sheet.cell(6,2) row = sheet.row 7 assert_nil row[0] assert_equal [1,2,3,4,5,6,7,8,9,0], row[1,10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet[7,1..10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet.cell(7,1..10) assert_equal 0.0005, sheet1[8,0] assert_equal 0.005, sheet1[8,1] assert_equal 0.05, sheet1[8,2] assert_equal 10.5, sheet1[8,3] assert_equal 1.05, sheet1[8,4] assert_equal 100.5, sheet1[8,5] assert_equal 10.05, sheet1[8,6] assert_equal 1.005, sheet1[8,7] assert_equal 100.5, sheet1[9,0] assert_equal 10.05, sheet1[9,1] assert_equal 1.005, sheet1[9,2] assert_equal 1000.5, sheet1[9,3] assert_equal 100.05, sheet1[9,4] assert_equal 10.005, sheet1[9,5] assert_equal 1.0005, sheet1[9,6] assert_equal 10000.5, sheet1[10,0] assert_equal 1000.05, sheet1[10,1] assert_equal 100.005, sheet1[10,2] assert_equal 10.0005, sheet1[10,3] assert_equal 1.00005, sheet1[10,4] assert_equal 40, sheet1.row(11).height assert_instance_of Spreadsheet::Excel::Worksheet, sheet sheet = book.worksheets.last name = "m\000y\000 \000n\000a\000m\000e\000" name.force_encoding 'UTF-16LE' if name.respond_to?(:force_encoding) assert_equal name, sheet.name assert_not_nil sheet.offset end def test_write_new_workbook__utf16 Spreadsheet.client_encoding = 'UTF-16LE' book = Spreadsheet::Workbook.new path = File.join @var, 'test_write_workbook.xls' sheet1 = book.create_worksheet str1 = @@iconv.iconv 'Shared String' str2 = @@iconv.iconv 'Another Shared String' str3 = @@iconv.iconv('1234567890 ' * 1000) str4 = @@iconv.iconv('9876543210 ' * 1000) fmt = Format.new :italic => true, :color => :blue sheet1.format_column 1, fmt, :width => 20 sheet1[0,0] = str1 sheet1.row(0).push str1 sheet1.row(1).concat [str2, str2] sheet1[2,0] = str3 sheet1[3,0] = str4 fmt = Format.new :color => 'red' sheet1[4,0] = 0.25 sheet1.row(4).set_format 0, fmt fmt = Format.new :color => 'aqua' sheet1[5,0] = 0.75 sheet1.row(5).set_format 0, fmt sheet1[6,0] = 1 fmt = Format.new :color => 'green' sheet1.row(6).set_format 0, fmt sheet1[6,1] = Date.new 2008, 10, 10 sheet1[6,2] = Date.new 2008, 10, 12 fmt = Format.new :number_format => @@iconv.iconv("DD.MM.YYYY") sheet1.row(6).set_format 1, fmt sheet1.update_row 7, nil, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 sheet1.row(8).default_format = fmt sheet1[8,0] = @@iconv.iconv 'formatted when empty' book.create_worksheet :name => @@iconv.iconv("my name") #=> sheet2 book.write path Spreadsheet.client_encoding = 'UTF-8' str1 = 'Shared String' str2 = 'Another Shared String' str3 = '1234567890 ' * 1000 str4 = '9876543210 ' * 1000 assert_nothing_raised do book = Spreadsheet.open path end if RUBY_VERSION >= '1.9' assert_equal 'UTF-16LE', book.encoding.name else assert_equal 'UTF-16LE', book.encoding end assert_valid_sst(book, :is => [str1, str2, str3, str4, "formatted when empty"]) assert_equal 2, book.worksheets.size sheet = book.worksheets.first assert_instance_of Spreadsheet::Excel::Worksheet, sheet assert_equal "Worksheet1", sheet.name assert_not_nil sheet.offset assert_not_nil col = sheet.column(1) assert_equal true, col.default_format.font.italic? assert_equal :blue, col.default_format.font.color row = sheet.row 0 assert_equal col.default_format, row.format(1) assert_equal str1, row[0] assert_equal str1, sheet[0,0] assert_equal str1, sheet.cell(0,0) assert_equal str1, row[1] assert_equal str1, sheet[0,1] assert_equal str1, sheet.cell(0,1) row = sheet.row 1 assert_equal str2, row[0] assert_equal str2, sheet[1,0] assert_equal str2, sheet.cell(1,0) assert_equal str2, row[1] assert_equal str2, sheet[1,1] assert_equal str2, sheet.cell(1,1) row = sheet.row 2 assert_equal str3, row[0] assert_equal str3, sheet[2,0] assert_equal str3, sheet.cell(2,0) assert_nil row[1] assert_nil sheet[2,1] assert_nil sheet.cell(2,1) row = sheet.row 3 assert_equal str4, row[0] assert_equal str4, sheet[3,0] assert_equal str4, sheet.cell(3,0) assert_nil row[1] assert_nil sheet[3,1] assert_nil sheet.cell(3,1) row = sheet.row 4 assert_equal :red, row.format(0).font.color assert_equal 0.25, row[0] assert_equal 0.25, sheet[4,0] assert_equal 0.25, sheet.cell(4,0) row = sheet.row 5 assert_equal :cyan, row.format(0).font.color assert_equal 0.75, row[0] assert_equal 0.75, sheet[5,0] assert_equal 0.75, sheet.cell(5,0) row = sheet.row 6 assert_equal :green, row.format(0).font.color assert_equal 1, row[0] assert_equal 1, sheet[6,0] assert_equal 1, sheet.cell(6,0) assert_equal 'DD.MM.YYYY', row.format(1).number_format date = Date.new 2008, 10, 10 assert_equal date, row[1] assert_equal date, sheet[6,1] assert_equal date, sheet.cell(6,1) assert_equal 'DD.MM.YYYY', row.format(2).number_format date = Date.new 2008, 10, 12 assert_equal date, row[2] assert_equal date, sheet[6,2] assert_equal date, sheet.cell(6,2) row = sheet.row 7 assert_nil row[0] assert_equal [1,2,3,4,5,6,7,8,9,0], row[1,10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet[7,1..10] assert_equal [1,2,3,4,5,6,7,8,9,0], sheet.cell(7,1..10) row = sheet.row 8 assert_equal 'formatted when empty', row[0] assert_not_nil row.default_format assert_instance_of Spreadsheet::Excel::Worksheet, sheet sheet = book.worksheets.last assert_equal "my name", sheet.name assert_not_nil sheet.offset end def test_template template = File.join @data, 'test_copy.xls' output = File.join @var, 'test_template.xls' book = Spreadsheet.open template sheet1 = book.worksheet 0 sheet1.row(4).replace [ 'Daniel J. Berger', 'U.S.A.', 'Author of original code for Spreadsheet::Excel' ] book.write output assert_nothing_raised do book = Spreadsheet.open output end sheet = book.worksheet 0 row = sheet.row(4) assert_equal 'Daniel J. Berger', row[0] end def test_bignum smallnum = 0x1fffffff bignum = smallnum + 1 book = Spreadsheet::Workbook.new sheet = book.create_worksheet sheet[0,0] = bignum sheet[1,0] = -bignum sheet[0,1] = smallnum sheet[1,1] = -smallnum sheet[0,2] = bignum - 0.1 sheet[1,2] = -bignum - 0.1 sheet[0,3] = smallnum - 0.1 sheet[1,3] = -smallnum - 0.1 path = File.join @var, 'test_big-number.xls' book.write path assert_nothing_raised do book = Spreadsheet.open path end assert_equal bignum, book.worksheet(0)[0,0] assert_equal(-bignum, book.worksheet(0)[1,0]) assert_equal smallnum, book.worksheet(0)[0,1] assert_equal(-smallnum, book.worksheet(0)[1,1]) assert_equal bignum - 0.1, book.worksheet(0)[0,2] assert_equal(-bignum - 0.1, book.worksheet(0)[1,2]) assert_equal smallnum - 0.1, book.worksheet(0)[0,3] assert_equal(-smallnum - 0.1, book.worksheet(0)[1,3]) end def test_bigfloat # reported in http://rubyforge.org/tracker/index.php?func=detail&aid=24119&group_id=678&atid=2677 bigfloat = 10000000.0 book = Spreadsheet::Workbook.new sheet = book.create_worksheet sheet[0,0] = bigfloat sheet[0,1] = bigfloat + 0.1 sheet[0,2] = bigfloat - 0.1 sheet[1,0] = -bigfloat sheet[1,1] = -bigfloat + 0.1 sheet[1,2] = -bigfloat - 0.1 path = File.join @var, 'test_big-float.xls' book.write path assert_nothing_raised do book = Spreadsheet.open path end sheet = book.worksheet(0) assert_equal bigfloat, sheet[0,0] assert_equal bigfloat + 0.1, sheet[0,1] assert_equal bigfloat - 0.1, sheet[0,2] assert_equal(-bigfloat, sheet[1,0]) assert_equal(-bigfloat + 0.1, sheet[1,1]) assert_equal(-bigfloat - 0.1, sheet[1,2]) end def test_datetime__off_by_one # reported in http://rubyforge.org/tracker/index.php?func=detail&aid=24414&group_id=678&atid=2677 datetime1 = DateTime.new(2008) datetime2 = DateTime.new(2008, 1, 1, 1, 0, 1) date1 = Date.new(2008) date2 = Date.new(2009) book = Spreadsheet::Workbook.new sheet = book.create_worksheet sheet[0,0] = datetime1 sheet[0,1] = datetime2 sheet[1,0] = date1 sheet[1,1] = date2 path = File.join @var, 'test_datetime.xls' book.write path assert_nothing_raised do book = Spreadsheet.open path end sheet = book.worksheet(0) assert_equal datetime1, sheet[0,0] assert_equal datetime2, sheet[0,1] assert_equal date1, sheet[1,0] assert_equal date2, sheet[1,1] assert_equal date1, sheet.row(0).date(0) assert_equal datetime1, sheet.row(1).datetime(0) end def test_sharedfmla path = File.join @data, 'test_formula.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book sheet = book.worksheet 0 64.times do |idx| assert_equal '5026', sheet[idx.next, 2].value end end def test_missing_row_op path = File.join @data, 'test_missing_row.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book sheet = book.worksheet 0 assert_not_nil sheet[1,0] assert_not_nil sheet[2,1] end def test_changes path = File.join @data, 'test_changes.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book sheet = book.worksheet 1 sheet[20,0] = 'Ciao Mundo!' target = File.join @var, 'test_changes.xls' assert_nothing_raised do book.write target end end def test_long_sst_record path = File.join @data, 'test_long_sst_record.xls' book = Spreadsheet.open path sheet = book.worksheet(0) expected_result = 'A1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,907,908,909,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,930,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,975,976,977,978,979,980,981,982,983,984,985,986,987,988,989,990,991,992,993,994,995,996,997,998' assert_equal(expected_result, sheet[0,0]) end def test_special_chars book = Spreadsheet::Workbook.new sheet = book.create_worksheet (0..200).each { |i| sheet.row(i).push "ëçáéíóú" } assert_nothing_raised do book.write StringIO.new("", "w+") end end def test_read_protected_sheet path = File.join @data, "test_merged_and_protected.xls" book = Spreadsheet.open path sheet = book.worksheet(0) sheet.ensure_rows_read # FIXME HACK assert sheet.protected?, "Expected sheet to be protected" assert_equal Spreadsheet::Excel::Password.password_hash('testing'), sheet.password_hash end def test_write_protected_sheet path = File.join @var, 'test_protected.xls' book = Spreadsheet::Workbook.new sheet = book.create_worksheet sheet.protect! 'secret' assert_nothing_raised do book.write path end read_back = Spreadsheet.open path sheet = read_back.worksheet(0) sheet.ensure_rows_read # FIXME HACK assert sheet.protected?, "Expected sheet to be proteced" assert_equal Spreadsheet::Excel::Password.password_hash('secret'), sheet.password_hash end =begin def test_read_baltic path = File.join @data, 'test_baltic.xls' assert_nothing_raised do Spreadsheet.open path end end =end def test_write_frozen_string Spreadsheet.client_encoding = 'UTF-16LE' book = Spreadsheet::Workbook.new path = File.join @var, 'test_write_workbook.xls' sheet1 = book.create_worksheet str1 = "Frozen String.".freeze sheet1[0,0] = str1 sheet1.row(0).push str1 assert_nothing_raised do book.write path end end def test_read_merged_cells path = File.join(@data, 'test_merged_cells.xls') book = Spreadsheet.open(path) assert_equal 8, book.biff_version sheet = book.worksheet(0) sheet[0,0] # trigger read_worksheet assert_equal [[2, 4, 1, 1], [3, 3, 2, 3]], sheet.merged_cells end def test_read_borders path = File.join @data, 'test_borders.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book sheet = book.worksheet 0 format = sheet.row(0).format 0 assert_equal :none, format.left assert_equal :thin, format.top assert_equal :medium, format.right assert_equal :thick, format.bottom assert_equal :builtin_black, format.left_color assert_equal :red, format.top_color assert_equal :green, format.right_color assert_equal :yellow, format.bottom_color end def test_write_borders book = Spreadsheet::Workbook.new path = File.join @var, 'test_write_borders.xls' sheet1 = book.create_worksheet (sheet1.row(0).format 0).border = :hair (sheet1.row(0).format 0).border_color = :brown assert_nothing_raised do book.write path end book2 = Spreadsheet.open path assert_instance_of Excel::Workbook, book2 sheet2 = book2.worksheet 0 format = sheet2.row(0).format 0 assert_equal :hair, format.left assert_equal :brown, format.top_color end def test_adding_data_to_existing_file path = File.join @data, 'test_adding_data_to_existing_file.xls' book = Spreadsheet.open path assert_equal(1, book.worksheet(0).rows.count) book.worksheet(0).insert_row(1, [12, 23, 34, 45]) temp_file = Tempfile.new('temp') book.write(temp_file.path) temp_book = Spreadsheet.open temp_file.path assert_equal(2, temp_book.worksheet(0).rows.count) temp_file.unlink end def test_read_pagesetup path = File.join @data, 'test_pagesetup.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book sheet = book.worksheet(0) assert_equal(:landscape, sheet.pagesetup[:orientation]) assert_equal(130, sheet.pagesetup[:adjust_to]) end def test_write_pagesetup book = Spreadsheet::Workbook.new path = File.join @var, 'test_write_pagesetup.xls' sheet1 = book.create_worksheet sheet1.pagesetup[:orientation] = :landscape sheet1.pagesetup[:adjust_to] = 93 assert_nothing_raised do book.write path end book2 = Spreadsheet.open path assert_instance_of Excel::Workbook, book2 sheet2 = book2.worksheet(0) assert_equal(:landscape, sheet2.pagesetup[:orientation]) assert_equal(93, sheet2.pagesetup[:adjust_to]) end def test_read_margins path = File.join @data, 'test_margin.xls' book = Spreadsheet.open path assert_instance_of Excel::Workbook, book sheet = book.worksheet(0) assert_equal(2.0, sheet.margins[:left]) end def test_write_margins book = Spreadsheet::Workbook.new path = File.join @var, 'test_write_margins.xls' sheet1 = book.create_worksheet sheet1.margins[:left] = 3 assert_nothing_raised do book.write path end book2 = Spreadsheet.open path assert_instance_of Excel::Workbook, book2 sheet2 = book2.worksheet(0) assert_equal(3.0, sheet2.margins[:left]) end private # Validates the workbook's SST # Valid options: # :is => [array] # :contains => [array] # :length => num def assert_valid_sst(workbook, opts = {}) assert workbook.is_a?(Spreadsheet::Excel::Workbook) sst = workbook.sst assert sst.is_a?(Array) strings = sst.map do |entry| assert entry.is_a?(Spreadsheet::Excel::SstEntry) entry.content end sorted_strings = strings.sort # Make sure there are no duplicates, the whole point of the SST: assert_equal strings.uniq.sort, sorted_strings if opts[:is] assert_equal opts[:is].sort, sorted_strings end if opts[:contains] assert_equal [], opts[:contains] - sorted_strings end if opts[:length] assert_equal opts[:length], sorted_strings end end end end spreadsheet-0.9.0/test/font.rb0000644000004100000410000001341212216006760016332 0ustar www-datawww-data#!/usr/bin/env ruby # TestFont -- Spreadsheet -- 09.10.2008 -- hwyss@ywesee.com $: << File.expand_path('../lib', File.dirname(__FILE__)) require 'test/unit' require 'spreadsheet' module Spreadsheet class TestFont < Test::Unit::TestCase def setup @font = Font.new 'Arial' end def test_italic assert_equal false, @font.italic @font.italic! assert_equal true, @font.italic @font.italic = nil assert_equal false, @font.italic @font.italic = 1 assert_equal true, @font.italic end def test_encoding assert_equal :default, @font.encoding @font.encoding = :apple_roman assert_equal :apple_roman, @font.encoding @font.encoding = 'Chinese Simplified' assert_equal :chinese_simplified, @font.encoding assert_raises ArgumentError do @font.size = 'ascii' end assert_equal :chinese_simplified, @font.encoding @font.encoding = nil assert_equal :default, @font.encoding end def test_family assert_equal :none, @font.family @font.family = :roman assert_equal :roman, @font.family @font.family = 'Swiss' assert_equal :swiss, @font.family assert_raises ArgumentError do @font.size = :greek end assert_equal :swiss, @font.family @font.family = nil assert_equal :none, @font.family end def test_name assert_equal 'Arial', @font.name @font.name = 'Helvetica' assert_equal 'Helvetica', @font.name end def test_outline assert_equal false, @font.outline @font.outline! assert_equal true, @font.outline @font.outline = nil assert_equal false, @font.outline @font.outline = 1 assert_equal true, @font.outline end def test_escapement assert_equal :normal, @font.escapement @font.escapement = :superscript assert_equal :superscript, @font.escapement @font.escapement = 'sub' assert_equal :subscript, @font.escapement assert_raises ArgumentError do @font.size = "upwards" end assert_equal :subscript, @font.escapement @font.escapement = nil assert_equal :normal, @font.escapement end def test_shadow assert_equal false, @font.shadow @font.shadow! assert_equal true, @font.shadow @font.shadow = nil assert_equal false, @font.shadow @font.shadow = 1 assert_equal true, @font.shadow end def test_size assert_equal 10, @font.size @font.size = 12 assert_equal 12, @font.size @font.size = 11.2 assert_equal 11.2, @font.size assert_raises ArgumentError do @font.size = "123" end end def test_strikeout assert_equal false, @font.strikeout @font.strikeout! assert_equal true, @font.strikeout @font.strikeout = nil assert_equal false, @font.strikeout @font.strikeout = 1 assert_equal true, @font.strikeout end def test_underline assert_equal :none, @font.underline @font.underline = :single assert_equal :single, @font.underline @font.underline = 'double accounting' assert_equal :double_accounting, @font.underline assert_raises ArgumentError do @font.size = :triple end assert_equal :double_accounting, @font.underline @font.underline = nil assert_equal :none, @font.underline @font.underline = true assert_equal :single, @font.underline end def test_weight assert_equal :normal, @font.weight @font.weight = :bold assert_equal :bold, @font.weight @font.weight = 100 assert_equal 100, @font.weight assert_raises ArgumentError do @font.weight = Object.new end assert_equal 100, @font.weight @font.weight = 'bold' assert_equal :bold, @font.weight @font.weight = nil assert_equal :normal, @font.weight end def test_key expected = 'Arial_10_normal_normal_none_text_none_default' assert_equal expected, @font.key @font.name = 'Helvetica' expected = 'Helvetica_10_normal_normal_none_text_none_default' assert_equal expected, @font.key @font.size = 12 expected = 'Helvetica_12_normal_normal_none_text_none_default' assert_equal expected, @font.key @font.weight = :bold expected = 'Helvetica_12_bold_normal_none_text_none_default' assert_equal expected, @font.key @font.italic! expected = 'Helvetica_12_bold_italic_normal_none_text_none_default' assert_equal expected, @font.key @font.strikeout! expected = 'Helvetica_12_bold_italic_strikeout_normal_none_text_none_default' assert_equal expected, @font.key @font.outline! expected = 'Helvetica_12_bold_italic_strikeout_outline_normal_none_text_none_default' assert_equal expected, @font.key @font.shadow! expected = 'Helvetica_12_bold_italic_strikeout_outline_shadow_normal_none_text_none_default' assert_equal expected, @font.key @font.escapement = :super expected = 'Helvetica_12_bold_italic_strikeout_outline_shadow_superscript_none_text_none_default' assert_equal expected, @font.key @font.underline = :double expected = 'Helvetica_12_bold_italic_strikeout_outline_shadow_superscript_double_text_none_default' assert_equal expected, @font.key @font.color = :blue expected = 'Helvetica_12_bold_italic_strikeout_outline_shadow_superscript_double_blue_none_default' assert_equal expected, @font.key @font.family = :swiss expected = 'Helvetica_12_bold_italic_strikeout_outline_shadow_superscript_double_blue_swiss_default' assert_equal expected, @font.key @font.encoding = :iso_latin1 expected = 'Helvetica_12_bold_italic_strikeout_outline_shadow_superscript_double_blue_swiss_iso_latin1' assert_equal expected, @font.key end end end spreadsheet-0.9.0/test/row.rb0000644000004100000410000000163512216006760016177 0ustar www-datawww-data#!/usr/bin/env ruby # TestRow -- Spreadsheet -- 08.01.2009 -- hwyss@ywesee.com $: << File.expand_path('../../lib', File.dirname(__FILE__)) require 'test/unit' require 'spreadsheet' module Spreadsheet class TestRow < Test::Unit::TestCase def setup @workbook = Excel::Workbook.new @worksheet = Excel::Worksheet.new @workbook.add_worksheet @worksheet end def test_formatted row = Row.new @worksheet, 0, [nil, 1] assert_equal 2, row.formatted.size row.set_format 3, Format.new assert_equal 4, row.formatted.size end def test_concat row = Row.new @worksheet, 0, [nil, 1, nil] assert_equal [nil, 1, nil], row row.concat [2, nil] assert_equal [nil, 1, nil, 2, nil], row row.concat [3] assert_equal [nil, 1, nil, 2, nil, 3], row row.concat [nil, 4] assert_equal [nil, 1, nil, 2, nil, 3, nil, 4], row end end end spreadsheet-0.9.0/test/worksheet.rb0000644000004100000410000000724512216006760017406 0ustar www-datawww-data#!/usr/bin/env ruby # TestWorksheet -- Spreadheet -- 30.09.2008 -- hwyss@ywesee.com $: << File.expand_path('../lib', File.dirname(__FILE__)) require 'test/unit' require 'spreadsheet' module Spreadsheet class TestWorksheet < Test::Unit::TestCase def setup @book = Workbook.new @sheet = @book.create_worksheet end def test_cell_writer assert_nil @sheet[0,0] assert_equal 0, @sheet.column_count assert_equal 0, @sheet.row_count @sheet[0,0] = 'foo' assert_equal 'foo', @sheet[0,0] assert_equal 1, @sheet.column_count assert_equal 1, @sheet.row_count @sheet[1,0] = 'bar' assert_equal 1, @sheet.column_count assert_equal 2, @sheet.row_count @sheet[0,1] = 'bar' assert_equal 2, @sheet.column_count assert_equal 2, @sheet.row_count @sheet[1,0] = nil assert_equal 2, @sheet.column_count assert_equal 2, @sheet.row_count @sheet[0,1] = nil assert_equal 2, @sheet.column_count assert_equal 2, @sheet.row_count end def test_column_count assert_equal 0, @sheet.column_count @sheet.replace_row 3, nil, nil, 1, 2, 'foo, bar' assert_equal 3, @sheet.column_count @sheet.replace_row 8, nil, 'something', 4, 7, nil assert_equal 4, @sheet.column_count @sheet.replace_row 5, 4, 'something', 4, 7, nil assert_equal 5, @sheet.column_count @sheet.replace_row 5, nil, 'something', 4, 7, nil assert_equal 4, @sheet.column_count @sheet.replace_row 3 assert_equal 4, @sheet.column_count end def test_row_count assert_equal 0, @sheet.row_count @sheet.replace_row 3, nil, nil, 1, 2, 'foo, bar' assert_equal 1, @sheet.row_count @sheet.replace_row 8, nil, 'something', 4, 7, nil assert_equal 6, @sheet.row_count @sheet.replace_row 5, 4, 'something', 4, 7, nil assert_equal 6, @sheet.row_count @sheet.replace_row 5, nil, 'something', 4, 7, nil assert_equal 6, @sheet.row_count @sheet.replace_row 3 assert_equal 6, @sheet.row_count @sheet.delete_row 3 assert_equal 5, @sheet.row_count @sheet.delete_row 3 assert_equal 4, @sheet.row_count @sheet.delete_row 2 assert_equal 4, @sheet.row_count @sheet.delete_row 2 assert_equal 3, @sheet.row_count end def test_modify_column assert_equal 10, @sheet.column(0).width @sheet.column(1).width = 20 assert_equal 10, @sheet.column(0).width assert_equal 20, @sheet.column(1).width @sheet.column(0).width = 30 assert_equal 30, @sheet.column(0).width assert_equal 20, @sheet.column(1).width end def test_format_dates! rowi = -1 @sheet.format_dates! # No dates = no new formats assert_equal 1, @book.formats.length # Default format @sheet.row(rowi+=1).concat(["Hello", "World"]) @sheet.format_dates! # No dates = no new formats assert_equal 1, @book.formats.length @sheet.row(rowi+=1).concat([Date.new(2010,1,1)]) @sheet.format_dates! # 1 date = 1 new format assert_equal 2, @book.formats.length @sheet.row(rowi+=1).concat([Date.new(2011,1,1)]) @sheet.row(rowi+=1).concat([Date.new(2012,1,1)]) @sheet.row(rowi+=1).concat([Date.new(2013,1,1)]) @sheet.format_dates! # 4 dates = only 1 new format across them: assert_equal 3, @book.formats.length @sheet.row(rowi+=1).concat([Date.new(2014,1,1)]) @sheet.row(rowi).default_format = Format.new @sheet.row(rowi+=1).concat([Date.new(2015,1,1)]) @sheet.format_dates! # 6 dates = 2 new formats across them: assert_equal 6, @book.formats.length end end end spreadsheet-0.9.0/test/workbook.rb0000644000004100000410000000270612216006760017225 0ustar www-datawww-data#!/usr/bin/env ruby # TestWorkbook -- Spreadsheet -- 24.09.2008 -- hwyss@ywesee.com $: << File.expand_path('../lib', File.dirname(__FILE__)) require 'test/unit' require 'spreadsheet' require 'fileutils' require 'stringio' module Spreadsheet class TestWorkbook < Test::Unit::TestCase def setup @io = StringIO.new '' @book = Workbook.new end def test_writer__default_excel assert_instance_of Excel::Writer::Workbook, @book.writer(@io) end def test_sheet_count @worksheet1 = Excel::Worksheet.new @book.add_worksheet @worksheet1 assert_equal 1, @book.sheet_count @worksheet2 = Excel::Worksheet.new @book.add_worksheet @worksheet2 assert_equal 2, @book.sheet_count end def test_add_format assert_equal 1, @book.formats.length # Received a default format f1 = Format.new @book.add_format f1 assert_equal 2, @book.formats.length f2 = Format.new @book.add_format f2 assert_equal 3, @book.formats.length @book.add_format f2 assert_equal 3, @book.formats.length # Rejected duplicate insertion end def test_delete_worksheet original_count = @book.sheet_count @sheet = @book.create_worksheet changed_count = @book.sheet_count @book.delete_worksheet(@book.sheet_count - 1) assert_equal(changed_count, original_count + 1) assert_equal(original_count, @book.sheet_count) end end end spreadsheet-0.9.0/test/suite.rb0000755000004100000410000000051012216006760016513 0ustar www-datawww-data#!/usr/bin/env ruby # suite.rb -- spreadsheet -- 22.12.2011 -- jsaak@napalm.hu require 'rubygems' require 'bundler' require 'find' $VERBOSE = true here = File.dirname(__FILE__) $: << here Find.find(here) do |file| next if File.basename(file) == 'suite.rb' if file =~ /\.rb$/o require file[here.size+1..-1] end end spreadsheet-0.9.0/test/workbook_protection.rb0000644000004100000410000000101712216006760021465 0ustar www-datawww-data#!/usr/bin/env ruby $: << File.expand_path('../lib', File.dirname(__FILE__)) require 'test/unit' require 'spreadsheet' module Spreadsheet module Excel class TestWorkbook < Test::Unit::TestCase def test_password_hashing hashing_module = Spreadsheet::Excel::Password # Some examples found on the web assert_equal(0xFEF1, hashing_module.password_hash('abcdefghij')) assert_equal(hashing_module.password_hash('test'), hashing_module.password_hash('zzyw')) end end end end spreadsheet-0.9.0/test/data/0000755000004100000410000000000012216006760015747 5ustar www-datawww-dataspreadsheet-0.9.0/test/data/test_borders.xls0000644000004100000410000006400012216006760021176 0ustar www-datawww-dataࡱ> 21 g2ɀ\pRobert M Lowrey Ba==xl"88X@"1rCalibri1rCalibri1rCalibri1rCalibri1rCalibri1h8rCambria1,8rCalibri18rCalibri18rCalibri1rCalibri1rCalibri1<rCalibri1>rCalibri1?rCalibri14rCalibri14rCalibri1 rCalibri1 rCalibri1rCalibri1rCalibri1 rCalibri"$"#,##0_);\("$"#,##0\)!"$"#,##0_);[Red]\("$"#,##0\)""$"#,##0.00_);\("$"#,##0.00\)'""$"#,##0.00_);[Red]\("$"#,##0.00\)7*2_("$"* #,##0_);_("$"* \(#,##0\);_("$"* "-"_);_(@_).))_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)?,:_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)6+1_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)                                                                       ff + ) , *     P  P        `            a>   Q ||?J}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-}  00\);_(*}-}  00\);_(*}-}  00\);_(*}-}  00\);_(*}-}  00\);_(*}-} 00\);_(*}-} 00\);_(*}A} 00\);_(*ef;_(@_) }A} 00\);_(*ef;_(@_) }A} 00\);_(*ef;_(@_) }A} 00\);_(*ef;_(@_) }A} 00\);_(*ef;_(@_) }A} 00\);_(*ef ;_(@_) }A} 00\);_(*L;_(@_) }A} 00\);_(*L;_(@_) }A} 00\);_(*L;_(@_) }A} 00\);_(*L;_(@_) }A} 00\);_(*L;_(@_) }A} 00\);_(*L ;_(@_) }A} 00\);_(*23;_(@_) }A} 00\);_(*23;_(@_) }A} 00\);_(*23;_(@_) }A} 00\);_(*23;_(@_) }A}  00\);_(*23;_(@_) }A}! 00\);_(*23 ;_(@_) }A}" 00\);_(*;_(@_) }A}# 00\);_(*;_(@_) }A}$ 00\);_(*;_(@_) }A}% 00\);_(*;_(@_) }A}& 00\);_(*;_(@_) }A}' 00\);_(* ;_(@_) }A}( 00\);_(*;_(@_) }}) }00\);_(*;_(@_)    }}* 00\);_(*;_(@_) ??? ??? ??? ???}-}+ 00\);_(*}-}, 00\);_(*}-}- 00\);_(*}-}. 00\);_(*}-}/ 00\);_(*}A}0 a00\);_(*;_(@_) }A}1 00\);_(*;_(@_) }A}2 00\);_(*?;_(@_) }A}3 00\);_(*23;_(@_) }-}4 00\);_(*}}5 ??v00\);_(*̙;_(@_)    }A}6 }00\);_(*;_(@_) }A}7 e00\);_(*;_(@_) }}8 00\);_(*;_(@_)    }}9 ???00\);_(*;_(@_) ??? ??? ??? ???}-}: 00\);_(*}-}; 00\);_(*}U}< 00\);_(*;_(@_)  }-}= 00\);_(*}i}> 00\);_(*;_(@_)  P  20% - Accent1M 20% - Accent1 ef % 20% - Accent2M" 20% - Accent2 ef % 20% - Accent3M& 20% - Accent3 ef % 20% - Accent4M* 20% - Accent4 ef % 20% - Accent5M. 20% - Accent5 ef % 20% - Accent6M2 20% - Accent6  ef % 40% - Accent1M 40% - Accent1 L % 40% - Accent2M# 40% - Accent2 L渷 % 40% - Accent3M' 40% - Accent3 L % 40% - Accent4M+ 40% - Accent4 L % 40% - Accent5M/ 40% - Accent5 L % 40% - Accent6M3 40% - Accent6  Lմ % 60% - Accent1M 60% - Accent1 23 % 60% - Accent2M$ 60% - Accent2 23ږ % 60% - Accent3M( 60% - Accent3 23כ % 60% - Accent4M, 60% - Accent4 23 % 60% - Accent5M0 60% - Accent5 23 %! 60% - Accent6M4 60% - Accent6  23 % "Accent1AAccent1 O % #Accent2A!Accent2 PM % $Accent3A%Accent3 Y % %Accent4A)Accent4 d % &Accent5A-Accent5 K % 'Accent6A1Accent6  F %(Bad9Bad  %) Calculation Calculation  }% * Check Cell Check Cell  %????????? ???+ Comma,( Comma [0]-&Currency.. Currency [0]/Explanatory TextG5Explanatory Text % 0Good;Good  a%1 Heading 1G Heading 1 I}%O2 Heading 2G Heading 2 I}%?3 Heading 3G Heading 3 I}%234 Heading 49 Heading 4 I}% 5InputuInput ̙ ??v% 6 Linked CellK Linked Cell }% 7NeutralANeutral  e%3Normal % 8Noteb Note   9OutputwOutput  ???%????????? ???:$Percent ;Title1Title I}% <TotalMTotal %OO= Warning Text? Warning Text %XTableStyleMedium2PivotStyleLight16`.Sheet1.>Sheet2?Sheet38ccB g2ɀ |==  dMbP?_*+%,&ffffff?'ffffff?(?)?M Canon MG5300 series Printer ߁ odLetter BJDM VT$m,`Oj,`OjVT$m,v`Oj,v,v`OjXX'dVT$mVT$m@  VT$m   Canon MG5300 series Printer ߁ odLetter6qw"d333333?333333?&<3U; ;>2 >@ggD g2ɀ @?  dMbP?_*+%,&ffffff?'ffffff?(?)?",333333?333333?&<3U>@ggD g2ɀ @  dMbP?_*+%,&ffffff?'ffffff?(?)?",333333?333333?&<3U>@ggD Oh+'0@H`x Robert M LowreyRobert M LowreyMicrosoft Excel@h7S@zL8S՜.+,0HP X`hp x  Sheet1Sheet2Sheet3  Worksheets  "#$%&'(*+,-./0Root Entry F`=_TWorkbook:ASummaryInformation(!DocumentSummaryInformation8)spreadsheet-0.9.0/test/data/test_version_excel97.xls0000644000004100000410000007200012216006760022562 0ustar www-datawww-dataࡱ; 74  !"#$%&'()*+,-./012368Root Entry  \pCalc Ba=@=@ 8"1Arial1Arial1Arial1Arial1 Arial GENERAL MM/DD/YY                + ) , *         @ `O[Sheet1_Sheet2KaSheet3jb( 3  @@    Shared StringAnother Shared String*1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 < 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 *9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 987<6543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 Link-Text     dMbP?_%*+# &C&"Times New Roman,Normal"&12&A(%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }            ~ g/~ B "& -C6? 9̗?  PH0(  >@   yK  Link-TextyK Dhttp://scm.ywesee.com/spreadsheet   dMbP?_%*+# &C&"Times New Roman,Normal"&12&A(%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }  PH 0(  >@   dMbP?_%*+# &C&"Times New Roman,Normal"&12&A(%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }  PH0 0(   >@  FMicrosoft Excel 97-TabelleBiff8Oh+'0HPd x  Hannes Wyss Hannes Wyss1@@@FQM@)q՜.+,D՜.+,\Root EntryF5@Workbook cCompObjIOle SummaryInformation(DocumentSummaryInformation8tspreadsheet-0.9.0/test/data/test_version_excel97_2010.xls0000644000004100000410000012400012216006760023222 0ustar www-datawww-dataࡱ> QR  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPRoot Entry F2.[XWorkbook̘Ole SummaryInformation( Oh+'0h08 H T`kghjk@F@$W՜.+,D՜.+,HP X`hp x  Sheet1Sheet2Sheet3  Worksheets f2ـ\pkghjk Ba==x<8@"1Arial1Arial1Arial1Arial1Arial1bCalibri1 bCalibri1bCalibri14bCalibri1 bCalibri1bCalibri1bArial1bCalibri1,8bCalibri18bCalibri18bCalibri1 bArial1>bCalibri14bCalibri1<bCalibri1?bCalibri1h8bCambria1bCalibri1 bCalibri#,##0\ "Ft";\-#,##0\ "Ft"##,##0\ "Ft";[Red]\-#,##0\ "Ft"$#,##0.00\ "Ft";\-#,##0.00\ "Ft")$#,##0.00\ "Ft";[Red]\-#,##0.00\ "Ft">*9_-* #,##0\ "Ft"_-;\-* #,##0\ "Ft"_-;_-* "-"\ "Ft"_-;_-@_->)9_-* #,##0\ _F_t_-;\-* #,##0\ _F_t_-;_-* "-"\ _F_t_-;_-@_-F,A_-* #,##0.00\ "Ft"_-;\-* #,##0.00\ "Ft"_-;_-* "-"??\ "Ft"_-;_-@_-F+A_-* #,##0.00\ _F_t_-;\-* #,##0.00\ _F_t_-;_-* "-"??\ _F_t_-;_-@_- mm/dd/yy                                                                      ff + ) , *      P  P         `            a>       @ a  ||DZ}A} ##0.00\ ef _F_t_-;}A} ##0.00\ ef _F_t_-;}A} ##0.00\ ef _F_t_-;}A} ##0.00\ ef _F_t_-;}A} ##0.00\ ef _F_t_-;}A} ##0.00\ ef _F_t_-;}A} ##0.00\ L _F_t_-;}A} ##0.00\ L _F_t_-;}A} ##0.00\ L _F_t_-;}A} ##0.00\ L _F_t_-;}A} ##0.00\ L _F_t_-;}A} ##0.00\ L _F_t_-;}A} ##0.00\ 23 _F_t_-;}A} ##0.00\ 23 _F_t_-;}A} ##0.00\ 23 _F_t_-;}A} ##0.00\ 23 _F_t_-;}A}  ##0.00\ 23 _F_t_-;}A}! ##0.00\ 23 _F_t_-;}A}" ##0.00\  _F_t_-;}A}# ##0.00\  _F_t_-;}A}$ ##0.00\  _F_t_-;}A}% ##0.00\  _F_t_-;}A}& ##0.00\  _F_t_-;}A}' ##0.00\  _F_t_-;}A}( ##0.00\  _F_t_-;}}) }##0.00\  _F_t_-;   }}* ##0.00\  _F_t_-;??? ??? ??? ???}-}/ ##0.00\ }(}0  ##0.00\ }A}1 a##0.00\  _F_t_-;}A}2 ##0.00\  _F_t_-;}A}3 ##0.00\ ? _F_t_-;}A}4 ##0.00\ 23 _F_t_-;}-}5 ##0.00\ }(}6  ##0.00\ }}7 ??v##0.00\ ̙ _F_t_-;   }A}8 }##0.00\  _F_t_-;}A}9 e##0.00\  _F_t_-;}x}:##0.00\  _F  }}; ???##0.00\  _F??? ???  ??? ???}-}= ##0.00\ }U}> ##0.00\  _F }-}? ##0.00\ }(}C  ##0.00\  20% - Accent1M 20% - Accent1 ef % 20% - Accent2M" 20% - Accent2 ef % 20% - Accent3M& 20% - Accent3 ef % 20% - Accent4M* 20% - Accent4 ef % 20% - Accent5M. 20% - Accent5 ef % 20% - Accent6M2 20% - Accent6  ef % 40% - Accent1M 40% - Accent1 L % 40% - Accent2M# 40% - Accent2 L渷 % 40% - Accent3M' 40% - Accent3 L % 40% - Accent4M+ 40% - Accent4 L % 40% - Accent5M/ 40% - Accent5 L % 40% - Accent6M3 40% - Accent6  Lմ % 60% - Accent1M 60% - Accent1 23 % 60% - Accent2M$ 60% - Accent2 23ږ % 60% - Accent3M( 60% - Accent3 23כ % 60% - Accent4M, 60% - Accent4 23 % 60% - Accent5M0 60% - Accent5 23 %! 60% - Accent6M4 60% - Accent6  23 % "Accent1AAccent1 O % #Accent2A!Accent2 PM % $Accent3A%Accent3 Y % %Accent4A)Accent4 d % &Accent5A-Accent5 K % 'Accent6A1Accent6  F %(Bad9Bad  %) Calculation Calculation  }% * Check Cell Check Cell  %????????? ???+ Comma,( Comma [0]-&Currency.. Currency [0]/Explanatory TextG5Explanatory Text %0 F Followed Hyperlink   1Good;Good  a%2 Heading 1G Heading 1 I}%O3 Heading 2G Heading 2 I}%?4 Heading 3G Heading 3 I}%235 Heading 49 Heading 4 I}%64 Hyperlink   7InputuInput ̙ ??v% 8 Linked CellK Linked Cell }% 9NeutralANeutral  e%"Normal :Noteb Note   ;OutputwOutput  ???%????????? ???<$Percent =Title1Title I}% >TotalMTotal %OO? Warning Text? Warning Text %XTableStyleMedium2PivotStyleLight16`}Sheet1Sheet2Sheet3$4  Shared StringAnother Shared String*1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 < 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 *9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 987<6543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 Link-Text + ccPK![Content_Types].xmlN0EH-J@%ǎǢ|ș$زULTB l,3;rØJB+$G]7O٭VRv`dDJ| W%9ߘo41a%$g9 7,k2=p9Ѭ3ІMM!}rBq>8>o?MI\]12ol 鎑)Rhs Wth8j  XjulABd7yFۥom ^ejc{m8w6ۃpס7D~w~m(,9ݠs%\G]w9 Pf1*bB14!'%ZtNd3Ɉ@)|CA1eyY YRX{o_}/߼ӛ>{򏖗3IՉ_g|?E=}V~?_ۗOk$Uɧ,1=ND یc\LیiD3D]z"ut:x>\ptDrX'Q B:Ut,e:C8-Sfu<$QdAN;H\:>a Gu/KYˇT8;Y9eR,wpJEx]辅}H1EUl.Qw}2}g_s(KE$ Ͻ_T=!paAxN{|r9/ꡘㄧVhQɖRyju7ƽ)oɔjABÒxR*@4bXq0؁l/>#pFmLok0e:a O2G*L]ì]%V=|1pW! 0`.Wlj9HV3rq44K`fzV mV ,Sw\j-bmX)C7-`8HL^xu*N/43 z6H;8Cdm2iVֵy뤽V,ַr/9[kvxm:;k;v%y117e,1{@`VAz,96ؘpRXwa nְ lIhTSepRf3EX &$"JPp!'!{EQX?ݭh>m9nKLB<>JBs0\8z ibiORoB}($.(]@̗ʗ    yK  Link-TextyK Dhttp://scm.ywesee.com/spreadsheetggD f2ـ [  dMbP?_*+%# &C&"Times New Roman,Normal"&12&A(%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?&43U}  >@ ggD f2ـ B  dMbP?_*+%# &C&"Times New Roman,Normal"&12&A(%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?&43U}  >@ ggD DocumentSummaryInformation8CompObj k 8@ _PID_HLINKSA|]^"http://scm.ywesee.com/spreadsheet FMicrosoft Excel 2003 WorksheetBiff8Excel.Sheet.89qspreadsheet-0.9.0/test/data/test_changes.xls0000644000004100000410000004400012216006760021144 0ustar www-datawww-dataࡱ> "! \p Ba= ThisWorkbook=x9!8X@"1\[SO1\[SO1\[SO1\[SO1\[SO1\[SO1 \[SO1$\[SO1\Verdana+""#,##0;""\-#,##05""#,##0;[Red]""\-#,##07""#,##0.00;""\-#,##0.00A""#,##0.00;[Red]""\-#,##0.00i*2_ ""* #,##0_ ;_ ""* \-#,##0_ ;_ ""* "-"_ ;_ @_ .))_ * #,##0_ ;_ * \-#,##0_ ;_ * "-"_ ;_ @_ y,:_ ""* #,##0.00_ ;_ ""* \-#,##0.00_ ;_ ""* "-"??_ ;_ @_ 6+1_ * #,##0.00_ ;_ * \-#,##0.00_ ;_ * "-"??_ ;_ @_ \$#,##0_);\(\$#,##0\)\$#,##0_);[Red]\(\$#,##0\) \$#,##0.00_);\(\$#,##0.00\)% \$#,##0.00_);[Red]\(\$#,##0.00\)                  , * + )  (  `@ @  (p@ @     @ @ (0@ @   (@ @  ` c.^RlicensesSheet3VV8 WEPDScCgeN 7cCgMAC0W@W 7&9hncBl \cCgv:gvvMAC0W@WkXeQSheet:licenses,{NR 7] zNXT (WPMS|~/TRKNT \,gcCgeN[eQ|~ 71(u7b 9hncbNcOvTopo!jgkXeQvQ@b gv:gvvOo`(ek] zNXTOSR ;N/f{tQُWW) 7 vP[{vU_ 7vP[eQQlQ Agent ServerS>ecCgeNT{vU_NLr 7 ] zNXT O(ubNcOvcCg]wQ[,geNۏLubcCgOo`Ǐ z 700-1F-3C-93-BB-40 7f00-1F-3C-93-BB-4100-1F-3C-93-BB-4200-1F-3C-93-BB-4300-1F-3C-93-BB-4400-1F-3C-93-BB-4500-1F-3C-93-BB-4600-1F-3C-93-BB-4700-1F-3C-93-BB-4800-1F-3C-93-BB-49KFC 7cCgv[7b 7n^RS 7[e 700-1F-3C-93-BB-2800-1F-3C-93-BB-2900-1F-3C-93-BB-3000-1F-3C-93-BB-3100-1F-3C-93-BB-3200-1F-3C-93-BB-3300-21-86-5C-21-F100:14:2A:3E:5F:B6 72008-10-KFC-0001 7(353934c68cea1ee61fa5182f1fae400a807e8ef8 72008-10-KFC-0002 7(1cea9343afd07187568f4755591a298f7074a8a3 7 2008-10-KFC-0003 7 (8a0a59df3f7e298881740f777d6fc282a5cf3133 7 2008-10-KFC-0004 7 (cb252626a24b6beee215c8097243e1e45aa36793 7 2008-10-KFC-0005 7 (d3040dbfa7dfc50f09c5ccb9d4ab743509f91579 7 2008-10-KFC-0006 7 (0216b8d74a4ed81e429cc25bbd3c2c118768f6e1 7 2008-10-KFC-0007 7 (797a0129eb5ac19f0077e64130c28f62907e4962 7 2008-10-KFC-0008 7 (6b2cbc1311f10c77b328dbe0f41ba8c53f501101 7 2008-10-KFC-0009 7 (217f9eb0796dd07e5991bcade9fb3cc1a16f82b4 7 2008-10-KFC-0010 7 (52383e7da53d85ab6bf331269fee551b80e16798 7 2008-10-KFC-0011 7 (d1462fadd7dfd427f76af13077fd1788d286200a 7 2008-10-KFC-0012 7 (5033078bea80c5c68f6003168ba07924b33d0d63 7 2008-10-KFC-0013 7 (283b4fc74d73fe60762b2b9fda03d91ab369f141 7 2008-10-KFC-0014 7 (957245e21009589f8378c01951dbf5d59dac833a 7 2008-10-KFC-0015 7 (63fea368a2136ea55f7e9f16705d439507b42a19 7 2008-10-KFC-0016 7 (bb975fa6a2ae806d63b796ba5beda35fced5a2b2 7 00:13:d4:c8:4b:a52008-10-KFC-0017 7 (78b1d00be423da334d1396e41da89442b4a5e778 7 2008-10-KFC-0018 7 (b1c2c1c37c0bbafd916d8a24a4512e2ca635a76f 7 2008-10-KFC-0019(95e8a9780f41d8fcf5210cf9931c8e9c2feae3edJ= u D)   xGw  cc  y  dMbP?_*+%"??U ~ ? ~ @ ~ @ ~ @ ~ @ ~ @ Bx>@7 Sheet2  Y  dMbP?_*+%Mn/ dLetterwidm" d??U} } `} } +}                   !   " #   $ %   & '   ( )   * +   , -   . /   0 1   2 3   4 5   6 7   8 9   : ;   < =   ? @   A B  > C D,|8888888888888888888>@7 Sheet1    dMbP?_*+%"??U>@7 Sheet3 Oh+'0@HT` x Microsoft Excel@2@`M0՜.+,0 PXd lt| '  licensesSheet3   Root Entry FWorkbook SummaryInformation(DocumentSummaryInformation8spreadsheet-0.9.0/test/data/test_version_excel5.xls0000644000004100000410000027700012216006760022476 0ustar www-datawww-dataࡱ;   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~Root Entry  !"#$%&'()*+,/1 l \ Mr X B=@ 8@"1Arial1Arial1Arial1Arial1Arial GENERAL                + ) , *     =Sheet1 ;Sheet2 Sheet3 l   dMbP?_%*+! &C&"Times New Roman,Normal"&12&A&%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }      Shared String Shared StringAnother Shared StringAnother Shared String1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 129876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 98~ g~ /~ B "& >   l   dMbP?_%*+! &C&"Times New Roman,Normal"&12&A&%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }   >  l   dMbP?_%*+! &C&"Times New Roman,Normal"&12&A&%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }   >   FMicrosoft Excel 5.0-TabelleBiff5Oh+'0`HP d p |  Hannes Wyss0@@d@@FQM@G(`(u՜.+,D՜.+,\Root EntryF Book  Ole -SummaryInformation( aCompObj.JDocumentSummaryInformation80tspreadsheet-0.9.0/test/data/test_pagesetup.xls0000644000004100000410000007400012216006760021534 0ustar www-datawww-dataࡱ> .:  !"#$%&'()*+,-123456789R Fwm/WorkbookVSummaryInformation(0DocumentSummaryInformation8 O\pPavel Evstigneev Ba==00a68@"1Calibri1Calibri1Calibri1Calibri1Calibri1h8Cambria1,8Calibri18Calibri18Calibri1Calibri1Calibri1<Calibri1>Calibri1?Calibri14Calibri14Calibri1 Calibri1 Calibri1Calibri1Calibri1 Calibri1Calibri"Rp"#,##0_);\("Rp"#,##0\)#"Rp"#,##0_);[Red]\("Rp"#,##0\)$"Rp"#,##0.00_);\("Rp"#,##0.00\))$"Rp"#,##0.00_);[Red]\("Rp"#,##0.00\):*5_("Rp"* #,##0_);_("Rp"* \(#,##0\);_("Rp"* "-"_);_(@_).))_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)B,=_("Rp"* #,##0.00_);_("Rp"* \(#,##0.00\);_("Rp"* "-"??_);_(@_)6+1_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)                                                                       ff + ) , *     P  P        `            a>  " ||?گ}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-}  00\);_(*}-}  00\);_(*}-}  00\);_(*}-}  00\);_(*}-}  00\);_(*}-} 00\);_(*}-} 00\);_(*}-}+ 00\);_(*}-}, 00\);_(*}-}- 00\);_(*}-}. 00\);_(*}-}: 00\);_(*}-}; 00\);_(*}A}1 00\);_(*?_);_(@_}A}2 00\);_(*??_);_(@_}A}3 00\);_(*23?_);_(@_}-}4 00\);_(*}A}0 a00\);_(*?_);_(@_}A}( 00\);_(*?_);_(@_}A}7 e00\);_(*?_);_(@_}}5 ??v00\);_(*̙?_);_(@_  ib~ *}}9 ???00\);_(*?_);_(@_??? ??? ???ib~ ???*}}) }00\);_(*?_);_(@_  ib~ *}A}6 }00\);_(*?_);_(@_}}* 00\);_(*?_);_(@_??? ??? ???ib~ ???*}-}= 00\);_(*}}8 00\);_(*?_);_(@_  ib~ *}-}/ 00\);_(*}U}< 00\);_(*?_);_(@_ }A}" 00\);_(*?_);_(@_}A} 00\);_(*ef?_);_(@_}A} 00\);_(*L?_);_(@_}A} 00\);_(*23?_);_(@_}A}# 00\);_(*?_);_(@_}A} 00\);_(*ef?_);_(@_}A} 00\);_(*L?_);_(@_}A} 00\);_(*23?_);_(@_}A}$ 00\);_(*?_);_(@_}A} 00\);_(*ef?_);_(@_}A} 00\);_(*L?_);_(@_}A} 00\);_(*23?_);_(@_}A}% 00\);_(*?_);_(@_}A} 00\);_(*ef?_);_(@_}A} 00\);_(*L?_);_(@_}A} 00\);_(*23?_);_(@_}A}& 00\);_(*?_);_(@_}A} 00\);_(*ef?_);_(@_}A} 00\);_(*L?_);_(@_}A}  00\);_(*23?_);_(@_}A}' 00\);_(* ?_);_(@_}A} 00\);_(*ef ?_);_(@_}A} 00\);_(*L ?_);_(@_}A}! 00\);_(*23 ?_);_(@_}-}> 00\);_(* 20% - Accent1M 20% - Accent1 ef % 20% - Accent2M" 20% - Accent2 ef % 20% - Accent3M& 20% - Accent3 ef % 20% - Accent4M* 20% - Accent4 ef % 20% - Accent5M. 20% - Accent5 ef % 20% - Accent6M2 20% - Accent6  ef % 40% - Accent1M 40% - Accent1 L % 40% - Accent2M# 40% - Accent2 L渷 % 40% - Accent3M' 40% - Accent3 L % 40% - Accent4M+ 40% - Accent4 L % 40% - Accent5M/ 40% - Accent5 L % 40% - Accent6M3 40% - Accent6  Lմ % 60% - Accent1M 60% - Accent1 23 % 60% - Accent2M$ 60% - Accent2 23ږ % 60% - Accent3M( 60% - Accent3 23כ % 60% - Accent4M, 60% - Accent4 23 % 60% - Accent5M0 60% - Accent5 23 %! 60% - Accent6M4 60% - Accent6  23 % "Accent1AAccent1 O % #Accent2A!Accent2 PM % $Accent3A%Accent3 Y % %Accent4A)Accent4 d % &Accent5A-Accent5 K % 'Accent6A1Accent6  F %(Bad9Bad  %) Calculation Calculation  }% * Check Cell Check Cell  %????????? ???+ Comma,( Comma [0]-&Currency.. Currency [0]/Explanatory TextG5Explanatory Text % 0Good;Good  a%1 Heading 1G Heading 1 I}%O2 Heading 2G Heading 2 I}%?3 Heading 3G Heading 3 I}%234 Heading 49 Heading 4 I}% 5InputuInput ̙ ??v% 6 Linked CellK Linked Cell }% 7NeutralANeutral  e%3Normal % 8Noteb Note   9OutputwOutput  ???%????????? ???:$Percent ;Title1Title I}% <TotalMTotal %OO= Warning Text? Warning Text %XTableStyleMedium9PivotStyleMedium48dq:Fc-2NWgFSWc-2NWgFSW̙̙3f3fff3f3f33333f33333\`;Sheet1"1&this is landscape with "ajust to" 130% $/  PK!pO[Content_Types].xmlj0Eжr(΢]yl#!MB;.n̨̽\A1&ҫ QWKvUbOX#&1`RT9<l#$>r `С-;c=1gMԯNDJ++2a,/$nECA6٥D-ʵ? dXiJF8,nx (MKoP(\HbWϿ})zg'8yV#x'˯?oOz3?^?O?~B,z_=yǿ~xPiL$M>7Ck9I#L nꎊ)f>\<|HL|3.ŅzI2O.&e>Ƈ8qBۙ5toG1sD1IB? }J^wi(#SKID ݠ1eBp{8yC]$f94^c>Y[XE>#{Sq c8 >;-&~ ..R(zy s^Fvԇ$*cߓqrB3' }'g7t4Kf"߇ފAV_] 2H7Hk;hIf;ZX_Fڲe}NM;SIvưõ[H5Dt(?]oQ|fNL{d׀O&kNa4%d8?L_H-Ak1h fx-jWBxlB -6j>},khxd׺rXg([x?eޓϲكkS1'|^=aѱnRvPK! ѐ'theme/theme/_rels/themeManager.xml.relsM 0wooӺ&݈Э5 6?$Q ,.aic21h:qm@RN;d`o7gK(M&$R(.1r'JЊT8V"AȻHu}|$b{P8g/]QAsم(#L[PK-!pO[Content_Types].xmlPK-!֧6 -_rels/.relsPK-!kytheme/theme/themeManager.xmlPK-!0ktheme/theme/theme1.xmlPK-! ѐ' theme/theme/_rels/themeManager.xml.relsPK] O U V  dMbP?_*+%,&?'?Ms com.apple.print.PageFormat.PMHorizontalRes com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMHorizontalRes 300 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMOrientation com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMOrientation 2 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMScaling com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMScaling 1.3 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalRes com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalRes 300 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalScaling com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalScaling 1.3 com.apple.print.ticket.stateFlag 0 com.apple.print.subTicket.paper_info_ticket PMPPDPaperCodeName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray PMPPDPaperCodeName A4 com.apple.print.ticket.stateFlag 0 PMTiogaPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray PMTiogaPaperName iso-a4 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMAdjustedPageRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPageRect 0 0 1791.6666666666667 2509.6153846153848 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMAdjustedPaperRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPaperRect -57.692307692307622 -57.692307692307693 1849.3589743589744 2641.0256410256411 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMPaperName iso-a4 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMUnadjustedPageRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPageRect 0 0 783 559 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMUnadjustedPaperRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPaperRect -18 -18 824 577 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.ppd.PMPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.ppd.PMPaperName A4 com.apple.print.ticket.stateFlag 0 com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.type com.apple.print.PaperInfoTicket com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.type com.apple.print.PageFormatTicket Mz,, : Re{HH(h " ??U , > >>>2>@dd@  7ggD  ՜.+,0 PXt | 'Pavel production Sheet1  Worksheets F$Microsoft Excel 97 - 2004 Worksheet8FIBExcel.Sheet.8 Oh+'0pHPl 'Pavel EvstigneevPavel EvstigneevMicrosoft Macintosh Excel@`@`GPICTR HHR RHH}RR3kZo{kZkZwg9wg9wsswo{o{wg9wPk[ssco{w9kZF1JRkZg9^V^swBZRkZPkZo{wVo{ZVckZF1JRsg9F1swJRF1kZUkZo{{Vo{VkZJRkZF1R^g9ZZcswB^NskZNg9kZccg9g9Ro{{kZ{{g9wwsw{{kZ)^Zo{wkZwkZwo{o{wkZw>s{{w{{w{wwwwZZwINsg9Rw^wkZ^{kZ^kZg9{skZg9JRwo{wo{wkZ{kZw{kZw{ww^Vo{JRkZo{B{c{^w{g9o{Ns^cwg9o{BBo{g9=sF1JRNsVRwwo{Zw.F1kZNsF1NsRcNsV^^g9RNs9JRVg9=JRkZF1o{kZ5^ZB=9ssNs5NsJRNswBF1{wRNs o{RBsJRZg9Bcc ^o{o{5^g9JRcF1RswwZJR{w?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~Root Entry  !"#$%&'()*+,/1 l \ Mr X B=@ 8@"1Arial1Arial1Arial1Arial1Arial GENERAL                + ) , *     =Sheet1 ;Sheet2 Sheet3 l   dMbP?_%*+! &C&"Times New Roman,Normal"&12&A&%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }      Shared String Shared StringAnother Shared StringAnother Shared String1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 129876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 98~ g~ /~ B "& >   l   dMbP?_%*+! &C&"Times New Roman,Normal"&12&A&%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }   >  l   dMbP?_%*+! &C&"Times New Roman,Normal"&12&A&%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }   >   FMicrosoft Excel 5.0-TabelleBiff5Oh+'0`HP d p |  Hannes Wyss0@@d@@FQM@G(`(u՜.+,D՜.+,\Root EntryF Book  Ole -SummaryInformation( aCompObj.JDocumentSummaryInformation80tspreadsheet-0.9.0/test/data/test_formula.xls0000644000004100000410000011200012216006760021175 0ustar www-datawww-dataࡱ; GD  !"#$%&'()*+,-./0123456789:;<=>?@ABCFHRoot Entry  \pCalc Ba=@=@ 8X"1Arial1Arial1Arial1Arial GENERAL                + ) , *  ` eeeZR3  @@   ACKNOWLEDGEDMGT AREA TECH NUMBERU9N9U9P9X7ZRO9J9ZRM9VNCNENB7D9S ?   dMbP?_%*+&?'?('}'}?)'}'}?"d,, ` `? ` `?Uc           @@50265026   5026   5026   5026   5026   5026   5026   5026    5026    5026    5026    5026    5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026 !"#$%&'()*+,-./0123456789:;<=>?    5026 ! !! 5026 " "" 5026 # ## 5026 $ $$ 5026 % %% 5026 & && 5026 ' '' 5026 ( (( 5026 ) )) 5026 * ** 5026 + ++ 5026 , ,, 5026 - -- 5026 . .. 5026 / // 5026 0 00 5026 1 11 5026 2 22 5026 3 33 5026 4 44 5026 5 55 5026 6 66 5026 7 77 5026 8 88 5026 9 99 5026 : :: 5026 ; ;; 5026 < << 5026 = == 5026 > >> 5026 ? ?? 5026@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ @ @@ 5026 A AA AA@50265026 B BB A5026 C CC A5026 D DD A5026 E EE A5026 F FF A5026 G GG A5026 H HH A5026 I II A5026 J JJ A5026 K KK A5026 L LL A5026 M MM A5026 N NN A5026 O OO A5026 P PP A5026 Q QQ A5026 R RR A5026 S SS A5026 T TT A5026 U UU A5026 V VV A5026 W WW A5026 X XX A5026 Y YY A5026 Z ZZ A5026 [ [[ A5026 \ \\ A5026 ] ]] A5026 ^ ^^ A5026 _ __ A5026`abcdefghijklmnopqrstuvwxyz{|}~ ` `` A5026 a aa A5026 b bb A5026 c cc A5026 d dd A5026 e ee A5026 f ff A5026 g gg A5026 h hh A5026 i ii A5026 j jj A5026 k kk A5026 l ll A5026 m mm A5026 n nn A5026 o oo A5026 p pp A5026 q qq A5026 r rr A5026 s s s A5026 t t t A5026 u u u A5026 v v v A5026 w w w A5026 x x x A5026 y y y A5026 z z z A5026 { { { A5026 | | | A5026 } } } A5026 ~ ~ ~ A5026    A5026    A5026    @50265026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026   5026    5026    5026    5026    5026    5026    5026    5026    5026    5026   5026   5026   5026   5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026   5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    @50265026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026    5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026        5026   @@50265026   5026   5026   5026   5026   5026   5026    5026    5026    5026    5026    5026    5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026   5026 !"#$%&'()*+,-./0123456789:;<=>?    5026 ! !! 5026 " "" 5026 # ## 5026 $ $$ 5026 % %% 5026 & && 5026 ' '' 5026 ( (( 5026 ) )) 5026 * ** 5026 + ++ 5026 , ,, 5026 - -- 5026 . .. 5026 / // 5026 0 00 5026 1 11 5026 2 22 5026 3 33 5026 4 44 5026 5 55 5026 6 66 5026 7 77 5026 8 88 5026 9 99 5026 : :: 5026 ; ;; 5026 < << 5026 = == 5026 > >> 5026 ? ?? 5026@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ @ @@ 5026 A AA AAb"50265026 B BB A5026 C C C A5026 D D D A5026 E E E A5026 F F F A5026 G G G A5026 H H H A5026 I I I A5026 J J J A5026 K K K A5026 L L L A5026 M M M A5026 N NN A5026 O OO A5026 P PP A5026 Q QQ A5026 R RR A5026 S SS A5026 T TT A5026 U UU A5026 V V V A5026 W W W A5026 X X X A5026 Y Y Y A5026 Z Z Z A5026 [ [ [ A5026 \ \ \ A5026 ] ] ] A5026 ^ ^ ^ A5026 _ __ A5026`ab ` ` ` A5026 a a a A5026 b b b A5026PH0(  >@  FMicrosoft Excel 97-TabelleBiff8Oh+'0@H \ h t  marianna0@@@"Y@r ՜.+,D՜.+,\Root EntryFE@Workbook_CompObjIOle SummaryInformation(DocumentSummaryInformation8tspreadsheet-0.9.0/test/data/test_missing_row.xls0000644000004100000410000027300012216006760022100 0ustar www-datawww-dataࡱ>   AB=%r8X"1Arial1Arial1Arial1Arial1Arial1 Arial1h Arial Cyr1Arial                         83ffff̙̙3f3fff3f3f33333f33333nSheet1 } < @>3@0<<0 ?5@540G B5;5:0=0;0 >=545;L=8:, 29.06.2009 3.>2>AB8.06:00 K=:8. B>38 =545;8.06:1017>@ @>AA89A:>9 ?@5AAK.06:2506:3006:37B4KE 8 "C@87<.06:4117>@ 70@C156=>9 ?@5AAK.06:4771C:0 8=25AB>@0.=45:A CAC-40.06:51 07:0007:1007:1407:2107:3007:3907:4307:5108:0008:1008:1408:2108:3008:3808:4208:5009:0009:1009:1409:2109:3009:3809:4209:5010:0010:1310:1710:2310:3010:36& !?5F2K?CA:. B:@KB85 B>@3>2 2 >AA88.10:4011:0011:12>2>AB8 :><?0=89.11:2311:30  D>:CA5.IT-187=5A: B5=45=F88 @K=:0.> ?>4AG5B0< M:A?5@B>2, 87-70 :@878A0 B5<?K @>AB0 ;845@>2 IT-@K=:0 >?CAB8;8AL 4> C@>2=O 2004 3>40. 5A<>B@O =0 AB>;L ?5AA8<8AB8G=K5 ?>:070B5;8, EC4H55 4;O @>AA89A:>9 >B@0A;8 5I5 2?5@548.  MB>< 3>4C ?045=85 @K=:0 <>65B A>AB028BL >:>;> 50%.  ?5@2CN >G5@54L >B ?@8>1@5B5=8O 2KA>:8E B5E=>;>389 1K;8 2K=C645=K >B:070BLAO 10=:8, @8B59;5@K 8 4525;>?5@K. !>:@0I0NBAO 8 3>AC40@AB25==K5 2;>65=8O 2 IT-?@>5:BK.11:3612:0012:120@C156=K9 187=5A.12:2312:30 ><?0=88.><?0=8O "!B@0E>2>9 4>< !"."!B@0E>2>9 4>< !" 70 ?5@2K5 5 <5AOF52 2009 3>40 A>1@0; ?@5<89 =0 AC<<C 7,5 <;@4 @C1;59. 07<5@ 2K?;0B A>AB028; 3,9 <;@4 @C1;59. 081>;55 2KA>:85 B5<?K @>AB0 :><?0=8O ?@>45<>=AB@8@>20;0 ?> AB@0E>20=8N 2 0280F8>==>9 8 :>A<8G5A:>9 AD5@0E, 4>1@>2>;L=><C <548F8=A:><C AB@0E>20=8N, AB@0E>20=8N 8<CI5AB20. @C38< ?>:070B5;5< CAB>9G82>AB8 AB@0E>2I8:0 >AB05BAO AB018;L=>ABL :;85=BA:>9 107K. ! O=20@O ?> <09 1K;> 70:;NG5=> A N@848G5A:8<8 ;8F0<8 >:>;> 100 BKA. 4>3>2>@>2, A D878G5A:8<8 ;8F0<8  1,1 <;=.12:3613:0013:1213:2313:3080;>3.!: 8AB>@8O A ?@>4>;65=85<.P! 1 <0@B0 2 >AA88 =0G0;0 459AB2>20BL A8AB5<0 ?@O<>3> C@53C;8@>20=8O C1KB:>2 ?> !. >48B5;8, ?>AB@0402H85 2 ", B5?5@L <>3CB >1@0I0BLAO A B@51>20=85< > 2>7<5I5=88 C1KB:>2 =5?>A@54AB25==> 2 A2>N AB@0E>2CN :><?0=8N, 0 =5 2 :><?0=8N, 2 :>B>@>9 1K; 70AB@0E>20= 28=>2=8: 020@88. "5>@5B8G5A:8 =>2K5 ?@028;0 4>;6=K 1K;8 >1;53G8BL 687=L 02B>2;045;LF0<. > =0 ?@0:B8:5 2>A?>;L7>20BLAO 8<8 =5 B0:-B> ?@>AB>. 59AB2CNI89 70:>= ?>425@3AO :@8B8:5 8=D8=0. @><5 B>3>, 45OB5;L=>ABLN AB@0E>2KE :><?0=89 =0 @K=:5 ! 708=B5@5A>20;0AL 0=B8<>=>?>;L=0O A;C610: $! =0H;0 ?@87=0:8 >3@0=8G5=8O :>=:C@5=F88.13:3614:0014:1214:2314:30)>18;L=K5 B5;5D>=K: 4>25@O9, => ?@>25@O9.5D8F8B <>18;L=KE B5;5D>=>2 <>65B 2>7=8:=CBL =0 @>AA89A:>< @K=:5 2 1;8609H85 4=8. !>B=8 BKAOG B5;5D>=>2 254CI8E <8@>2KE ?@>872>48B5;59 A:>?8;8AL =0 B0<>65==KE A:;040E. !@>:8 ?>AB02>: B5;5D>=>2 A@K20NBAO,:0: A;54AB285, :><?0=88 =5ACB C1KB:8. A5 MB8 A>1KB8O ?@>87>H;8 ?>A;5 ?>O2;5=8O =>2KE ?@028; @0AB0<>6820=8O <>18;L=KE B5;5D>=>2. "5?5@L ?0@B8N 3@C70 ?@>25@ONB =5 2K1>@>G=>, 0 ?>HBCG=>.  G5<C ?@8254CB B0:85 459AB28O B0<>65==8:>2?14:3615:0015:1215:2315:30><?0=8O ";:>0 >AA8O".";:>0 >AA8O" >B:@K;0 =0 !0<0@A:>< <5B0;;C@38G5A:>< 702>45 C=8:0;L=CN 4;O =0H59 AB@0=K ;8=8N ;0:8@>20=8O 4;O ?@>872>4AB20 2KA>:>:0G5AB25==>9 0;N<8=852>9 ;5=BK A ?>:@KB85<. 0?CA: =>2>9 ;8=88 2K2>48B Alcoa =0 ;848@CNI85 ?>78F88 2 <8@5 ?> ?@>872>4AB25==K< 2>7<>6=>ABO< 2 >B=>H5=88 <0B5@80;>2 4;O 10=>G=>9 ?@><KH;5==>AB8. !53>4=O @>AA89A:85 0:B82K Alcoa ?@54AB02;5=K 42C<O :@C?=59H8<8 2 AB@0=5 ?@54?@8OB8O<8 ?> ?@>872>4AB2C 0;N<8=852KE ?>;CD01@8:0B>2: !0<0@A:89 <5B0;;C@38G5A:89 702>4 8 ";:>0 5B0;;C@3 CA". ! 2005 3>40 Alcoa 8=25AB8@>20;0 2 8E <>45@=870F8N >:>;> 750 <;= 4>;;0@>2. 4=0:> 70 ?5@2K5 <5AOFK 2009 3>40 >1J5< 70:07>2 =0 ?@54?@8OB8OE C?0; ?>GB8 =0 30%.15:3616:0016:1216:2316:30 =B5@0:B82=K9 2K?CA:.16:3617:0017:1217:2417:30' !?5F2K?CA:. B:@KB85 B>@3>2 2 <5@8:5.17:3717:5118:0018:1318:2318:30!D5@0 8=B5@5A>2. !>B>2K9 @K=>:: ?@>4068 8 <>45;8.!>F8>;>38 ?>4AG8B0;8, GB> 1>;LH5 ?>;>28=K @>AA8O= A>18@0NBAO ?><5=OBL B5;5D>= 2 1;8609H85 <5AOFK. -B> 3>2>@8B > B><, GB> :@878A ?>:0 =5 A;8H:>< ?>2;8O; =0 MB>B A5:B>@ @8B59;0. @8 2K1>@5 <>45;8 ?>:C?0B5;8 ?@5645 2A53> >1@0I0NB 2=8<0=85 =0 48709=, <5=N 8 =0;8G85 :0<5@K. !>18@0NBAO ;8 ?@>872>48B5;8 8 A>B>2K5 @8B59;5@K ?@54;>68BL ?>:C?0B5;O< 1>;55 M:>=><8G=K5 <>45;8 2 :@878A=>< 3>4C?18:3619:00# !?5F2K?CA:. B>38 B< >@3>2 2 >AA88.19:1319:2319:30 @5<O 10=:>2.>A;54=85 =545;8 ?@>H;8 =0 @>AA89A:>< D>=4>2>< @K=:5 ?>4 7=0:>< 10=:>2A:>3> A5:B>@0. !15@10=: 2KH5; 2 ;845@K :0: ?> >1J5<C B>@3>2, B0: 8 ?> B5<?0< 87<5=5=8O :>B8@>2>:. -B><C A?>A>1AB2>20; 8 =>2>AB=>9 D>=. $8=0=A>20O >BG5B=>ABL, 87<5=5=8O 2 A>AB025 <8=>@8B0@=KE 0:F8>=5@>2, A>1@0=85 0:F8>=5@>2. 0: 87<5=OBAO 4>E>4K @>AA89A:8E 10=:>2 2> 2B>@>9 ?>;>28=5 B5:CI53> 3>40? 5@2CN 2>;=CD8=0=A>2>3> :@878A0 C40;>AL A ?><>ILN 3>AC40@AB20 ?>30A8BL. C45B ;8 2B>@0O? 0: A:>@> 2>AAB0=>28BAO :@548B>20=85 M:>=><8:8?19:3620:0020:1320:2320:300H8 45=L38. =B5@0:B82.0 0H8 2>?@>AK 2 ?@O<>< MD8@5 >B25BOB: 8@8;; "@5<0A>2, 48@5:B>@ 0=0;8B8G5A:>3> 45?0@B0<5=B0 0=:0 >A:2K,;048<8@ !>;>4CE8=, C?@02;ONI89 48@5:B>@ # !. 45< 72>=:>2 ?> B5;5D>=C 2 ABC488 925-54-93.  B0: 65 ?8A5< =0 M;5:B@>==K9 04@5A markets@rbctv.ru.20:36=B@830 4=O, 8=B@830 =545;8.21:0021:1721:24 8@ A53>4=O.21:3021:4821:5422:0022:1722:2322:3022:4722:5323:0023:1323:2123:3023:3600:00$ !?5F2K?CA:. B>38 B>@3>2 2 <5@8:5.00:1200:30 C@=0;L=K9 @K=>:: <5=LH5 3;O=F0.!53>4=O 6C@=0;L=K9 3;0<C@ 2K=C645= ACI5AB2>20BL 2 =5?@82KG=>< M:>=><=>< @568<5. >;8G5AB2> @5:;0<K @57:> C<5=LH8;>AL, 0 8<5==> >=0 405B 3;O=F52K< 6C@=0;0< 4> 80% ?@81K;8. 740B5;LA:85 4><0 >?B8<878@CNB @0AE>4K 8 ?>=5<=>3C A>:@0I0NB A2>8 8740B5;LA:85 ?@>D8;8. 0: 2K68205B 3;O=5F A53>4=O? 0:85 8740=8O =081>;55 2>AB@51>20=K G8B0B5;O<8? B> 87 83@>:>2 B5@O5B A2>8 ?>78F88, 0 :B> B25@4> AB>8B =0 =>30E?00:3601:0001:1301:30 IT-187=5A: B5=45=F88 @K=:0.> ?>4AG5B0< M:A?5@B>2, 87-70 :@878A0 B5<?K @>AB0 ;845@>2 IT-@K=:0 >?CAB8;8AL 4> C@>2=O 2004 3>40. 5A<>B@O =0 AB>;L ?5AA8<8AB8G=K5 ?>:070B5;8, EC4H55 4;O @>AA89A:>9 >B@0A;8 5I5 2?5@548.  MB>< 3>4C ?045=85 @K=:0 <>65B A>AB028BL >:>;> 50%.  ?5@2CN >G5@54L >B ?@8>1@5B5=8O 2KA>:8E B5E=>;>389 1K;8 2K=C645=K >B:070BLAO 10=:8, @8B59;5@K 8 4525;>?5@K. !>:@0I0NBAO 8 3>AC40@AB25==K5 2;>65=8O 2 IT-?@>5:BK. 01:3602:0002:1302:3002:3603:0003:1303:30 !: 8AB>@8O A ?@>4>;65=85<.Q! 1 <0@B0 2 >AA88 =0G0;0 459AB2>20BL A8AB5<0 ?@O<>3> C@53C;8@>20=8O C1KB:>2 ?> !. >48B5;8, ?>AB@0402H85 2 ", B5?5@L <>3CB >1@0I0BLAO A B@51>20=85< > 2>7<5I5=88 C1KB:>2 =5?>A@54AB25==> 2 A2>N AB@0E>2CN :><?0=8N, 0 =5 2 :><?0=8N, 2 :>B>@>9 1K; 70AB@0E>20= 28=>2=8: 020@88. "5>@5B8G5A:8 =>2K5 ?@028;0 4>;6=K 1K;8 >1;53G8BL 687=L 02B>2;045;LF0<. > =0 ?@0:B8:5 2>A?>;L7>20BLAO 8<8 =5 B0:-B> ?@>AB>. 59AB2CNI89 70:>= ?>425@3AO :@8B8:5 8=D8=0. @><5 B>3>, 45OB5;L=>ABLN AB@0E>2KE :><?0=89 =0 @K=:5 ! 708=B5@5A>20;0AL 0=B8<>=>?>;L=0O A;C610: $! =0H;0 ?@87=0:8 >3@0=8G5=8O :>=:C@5=F88. 03:3604:0004:1204:3004:4704:5305:0005:1305:3005:3705:4105:4705:51B>@=8:, 30.06.2009 3.06:13=45:AK @072820NI8EAO AB@0=.5DBL 4;O 8=>AB@0=F52? >AA89A:>9 "=5DBO=:5" =5>1E>48<K 8=>AB@0==K5 8=25AB8F88. > >F5=:0< 83@>:>2 @K=:0, A59G0A 4;O @0728B8O >B@0A;8 =5>1E>48<> >:>;> 300 <8;;80@4>2 4>;;0@>2.  5A;8 A@54AB20 =5 1C4CB =0945=K, 2 1;8609H85 3>4K 4>1KG0 "G5@=>3>" 7>;>B0 <>65B @57:> C?0ABL. 564C B5< ?@8E>4 70@C156=KE 8=25AB>@>2 2> <=>3>< 1C45B 7028A5BL >B :0G5AB20 ?@54;0305<KE ?@>5:B>2, 30@0=B89 8 2>7<>6=>AB59 2 =0H59 AB@0=5. @><5 B>3>, 4;O ?>;CG5=8O 4>ABC?0 8=>AB@0==KE :><?0=89 : =5DB5307>2K< <5AB>@>645=8O< =5>1E>48<> A>3;0A85 2;0AB59.@>3=>7 :C@A0 @C1;O. >AA89A:85 2;0AB8 C25@5=K 2 40;L=59H5< @>AB5 =0F8>=0;L=>9 20;NBK. 0=: >AA88 AG8B05B, GB> :C@A @C1;O 1C45B C:@5?;OBLAO 4> :>=F0 3>40, 20;NB=K9 @K=>: AB018;878@C5BAO 8 =8:0:>3> M:AB@5==>3> 2<5H0B5;LAB20 3>AC40@AB20 =5 B@51C5BAO. 5 2A5 M:>=><8ABK @0745;ONB B0:>9 ?@>3=>7, CG8BK20O, GB> @>AA89A:0O M:>=><8:0 <>65B >AB0BLAO 2 ?>;>A5 @5F5AA88 4> :>=F0 3>40. !B>8B ;8 >6840BL 2B>@>3> 28B:0 4520;L20F88 =0F8>=0;L=>9 20;NBK, 8;8 @C1;L A<>65B >B2>520BL CB@0G5==K5 ?>78F88?>@?>@0F8O Mars.>@?>@0F8O Mars ?>4B25@48;0 ?;0=K 225AB8 D01@8:C ?> ?@>872>4AB2C :>@<>2 4;O 682>B=KE 2 #;LO=>2A:>9 >1;0AB8 2 A5=BO1@5 2009 3>40. 0==K9 8=25AB?@>5:B =0E>48BAO 2 AB0488 7025@H5=8O. 0?><=8<, GB> 2 2007 3>4C ?@028B5;LAB2> #;LO=>2A:>9 >1;0AB8 8 "0@A" ?>4?8A0;8 A>3;0H5< =85 > AB@>8B5;LAB25 D01@8: ?> ?@>872>4AB2C :>=48B5@A:8E 8745;89 8 :>@<>2 4;O 682>B=KE. 1I89 >1J5< 8=25AB8F89 A>AB02;O5B ?>@O4:0 4,6 <;@4 @C1;59.!5B8 AB@>9<0B5@80;>2. ?>A;54=85 3>4K @K=>: B>20@>2 4;O @5<>=B0 8 AB@>8B5;LAB20 C25@5==> @072820;AO 2 ?>;L7C A5B52>3> @8B59;0.  =0H59 AB@0=5 @01>B0NB ?@0:B8G5A:8 2A5 :@C?=K5 70?04=K5 A5B8 4;O AB@>8B5;LAB20, @5<>=B0 8 4><0 2 D>@<0B5 DIY-38?5@<0@:5B>2. E 3;02=K<8 :>=:C@5=B0<8 ?> >1J5<C ?@>406 O2;ONBAO AB@>8B5;L=K5 @K=:8. "5< =5 <5=55, F828;87>20==K5 D>@<K B>@3>2;8, ;>38AB8G5A:>5 C4>1AB2> 8 >A>1K9 ?>4E>4 : ?>:C?0B5;O< AB0;8 A5@L57=K< D0:B>@>< 2 ?@82;5G5=88 :;85=B>2 2 DIY-A5B8. 7<5=8B ;8 :@878A MBC B5=45=F8N?0 0H8 2>?@>AK 2 ?@O<>< MD8@5 >B25BOB: ;5:A0=4@ @0?82:>, C?@02;ONI89 3@C??K 0:F89 " 5=5AA0=A #?@02;5=85 8=25AB8F8O<8". 45< 72>=:>2 ?> B5;5D>=C 2 ABC488 925-54-93.  B0: 65 ?8A5< =0 M;5:B@>==K9 04@5A markets@rbctv.ru.#!?5F2K?CA:. B>38 B>@3>2 2 <5@8:5.! !>B>2K9 @K=>:: ?@>4068 8 <>45;8.!>F8>;>38 ?>4AG8B0;8, GB> 1>;LH5 ?>;>28=K @>AA8O= A>18@0NBAO ?><5=OBL B5;5D>= 2 1;8609H85 <5AOFK. -B> 3>2>@8B > B><, GB> :@878A ?>:0 =5 A;8H:>< ?>2;8O; =0 MB>B A5:B>@ @8B59;0. @8 2K1>@5 <>45;8 ?>:C?0B5;8 ?@5645 2A53> >1@0I0NB 2=8<0=85 =0 48709=, <5=N 8 =0;8G85 :0<5@K. !>18@0NBAO ;8 ?@>872>48B5;8 8 A>B>2K5 @8B59;5@K ?@54;>68BL ?>:C?0B5;O< 1>;55 M:>=><8G=K5 <>45;8 2 :@878A=>< 3>4C?  5DBL 4;O 8=>AB@0=F52? >AA89A:>9 "=5DBO=:5" =5>1E>48<K 8=>AB@0==K5 8=25AB8F88. > >F5=:0< 83@>:>2 @K=:0, A59G0A 4;O @0728B8O >B@0A;8 =5>1E>48<> >:>;> 300 <8;;80@4>2 4>;;0@>2.  5A;8 A@54AB20 =5 1C4CB =0945=K, 2 1;8609H85 3>4K 4>1KG0 "G5@=>3>" 7>;>B0 <>65B @57:> C?0ABL. 564C B5< ?@8E>4 70@C156=KE 8=25AB>@>2 2> <=>3>< 1C45B 7028A5BL >B :0G5AB20 ?@54;0305<KE ?@>5:B>2, 30@0=B89 8 2>7<>6=>AB59 2 =0H59 AB@0=5. @><5 B>3>, 4;O ?>;CG5=8O 4>ABC?0 8=>AB@0==KE :><?0=89 : =5DB5307>2K< <5AB>@>645=8O< =5>1E>48<> A>3;0A85 2;0AB59.  @>3=>7 :C@A0 @C1;O. >AA89A:85 2;0AB8 C25@5=K 2 40;L=59H5< @>AB5 =0F8>=0;L=>9 20;NBK. 0=: >AA88 AG8B05B, GB> :C@A @C1;O 1C45B C:@5?;OBLAO 4> :>=F0 3>40, 20;NB=K9 @K=>: AB018;878@C5BAO 8 =8:0:>3> M:AB@5==>3> 2<5H0B5;LAB20 3>AC40@AB20 =5 B@51C5BAO. 5 2A5 M:>=><8ABK @0745;ONB B0:>9 ?@>3=>7, CG8BK20O, GB> @>AA89A:0O M:>=><8:0 <>65B >AB0BLAO 2 ?>;>A5 @5F5AA88 4> :>=F0 3>40. !B>8B ;8 >6840BL 2B>@>3> 28B:0 4520;L20F88 =0F8>=0;L=>9 20;NBK, 8;8 @C1;L A<>65B >B2>520BL CB@0G5==K5 ?>78F88? !@540, 01.07.2009 3.=45:AK "! 8 .- @>4C:B>2K9 @8B59;: <564C=0@>4=0O M:A?0=A8O.> >F5=:0< 0=0;8B8:>2, >AA8O =0E>48BAO =0 A54L<>< <5AB5 2 @59B8=35 =081>;55 ?@82;5:0B5;L=KE 4;O <564C=0@>4=>3> @8B59;0 AB@0=. 5A<>B@O =0 :@878A, 8=>AB@0==K5 A5B528:8 ?@>4>;60NB @072820BL A2>9 187=5A 2 =0H59 AB@0=5. >;55 B>3>, =5:>B>@K5 :><?0=88 ?>4C<K20NB > B><, GB>1K ?@>40BL G0ABL 187=5A0 2 2@>?5, GB>1K ?@8ABC?8BL : M:A?0=A88 =0 @>AA89A:89 @K=>:. #40ABAO ;8 8< 2>?;>B8BL AB>;L 0<18F8>7=K5 ?;0=K?  0=:8: B5AB =0 AB@5AA./;85=B 2?@025 7=0BL AB5?5=L =0456=>AB8 A2>53> 10=:0. "5< 1>;55 2 CA;>28OE :@878A0. !53>4=O =0 A?0A5=85 :@548B=KE >@30=870F89 2K45;ONBAO <8;;80@4=K5 1N465B=K5 @5AC@AK. > 8 B0:85 15H5=K5 45=L38 =5@0482K5 B>?-<5=5465@K <>3CB ?CAB8BL =0 25B5@. <5@8:0=A:85 2;0AB8 225;8 2 :@C?=59H8E 10=:0E >1O70B5;L=CN ?@>F54C@C B5AB8@>20=8O =0 AB@5AA, GB>1K ?@>25@8BL 8E C@>25=L A>?@>B82;O5<>AB8 :@878AC. >G5<C 65 @>AA89A:85 10=:8 <>3CB MB> 45;0BL 8;8 =5 45;0BL =0 A2>5 CA<>B@5=85? >;6=K ;8 @57C;LB0BK B5AB>2 AB0BL ?C1;8G=K<8, GB>1K ?><>GL :;85=B0< 2 2K1>@5 =0456=>3> 10=:0? $ ">A:>2A:>5 8?>B5G=>5 035=BAB2>".& $ 70@538AB@8@>20; 5H5=85 > 4>?>;=8B5;L=>< 2K?CA:5 >1K:=>25==KE 8<5==KE 1574>:C<5=B0@=KE 0:F89 ><<5@G5A:>3> 10=:0 ">A:>2A:>5 8?>B5G=>5 035=BAB2>". ;O C25;8G5=8O CAB02=>3> :0?8B0;0 548=AB25==K9 0:F8>=5@  "" - 3>@>4 >A:20 - =0?@028B 2,4 <;@4 @C1;59.  =0AB>OI55 2@5<O :0?8B0; 10=:0 @025= 1 209,504 <;= @C1;59. >A;5 @07<5I5=8O F5==KE 1C<03 4>;O 0:F8>=5@0 =5 87<5=8BAO 8 ?>-?@56=5<C A>AB028B 100%. >@>3>9 D8B=5A.=0 ?>A;54=85 ?OBL ;5B >1J5< @>AA89A:>3> D8B=5A-@K=:0 2K@>A 2 B@8 @070, >4=0:> D8B=5A B0: 8 =5 AB0; 4>ABC?=K< 4;O :064>3>.  5A;8 A?>@B82=K5 70;K 2 >=4>=5 ?>A5I05B :064K9 G5B25@BK9 68B5;L 3>@>40, B> 2 < >A:25 - ;8HL :064K9 B@84F0BK9.  >AA88, ?> A@02=5=8N A 0?04><, F5=K =0 D8B=5A-CA;C38 ?>-?@56=5<C A;8H:>< 2KA>:8& 0 0H8 2>?@>AK 2 ?@O<>< MD8@5 >B25BOB: <8B@89 ;5:A0=4@>2, 254CI89 0=0;8B8:  "$(  ".<br>45< 72>=:>2 ?> B5;5D>=C 2 ABC488 925-54-93.  B0: 65 ?8A5< =0 M;5:B@>==K9 04@5A markets@rbctv.ru !5B8 AB@>9<0B5@80;>2. ?>A;54=85 3>4K @K=>: B>20@>2 4;O @5<>=B0 8 AB@>8B5;LAB20 C25@5==> @072820;AO 2 ?>;L7C A5B52>3> @8B59;0.  =0H59 AB@0=5 @01>B0NB ?@0:B8G5A:8 2A5 :@C?=K5 70?04=K5 A5B8 4;O AB@>8B5;LAB20, @5<>=B0 8 4><0 2 D>@<0B5 DIY-38?5@<0@:5B>2. E 3;02=K<8 :>=:C@5=B0<8 ?> >1J5<C ?@>406 O2;ONBAO AB@>8B5;L=K5 @K=:8. "5< =5 <5=55, F828;87>20==K5 D>@<K B>@3>2;8, ;>38AB8G5A:>5 C4>1AB2> 8 >A>1K9 ?>4E>4 : ?>:C?0B5;O< AB0;8 A5@L57=K< D0:B>@>< 2 ?@82;5G5=88 :;85=B>2 2 DIY-A5B8. 7<5=8B ;8 :@878A MBC B5=45=F8N? '5B25@3, 02.07.2009 3. >AA89A:85 >B@0A;52K5 8=45:AK. >18;L=0O :>=:C@5=F8O. CA;>28OE D8=0=A>2>3> :@878A0 =5:>B>@K5 A>B>2K5 >?5@0B>@K =0<5@5=K ?>2KA8BL F5=K =0 CA;C38 A2O78. >4>1=K5 <5@K ?@54AB028B5;8 :><?0=89 =07K20NB >?B8<870F859. > 8E <=5=8N, =5A<>B@O =0 @>AB B0@8D>2, 1>;LH8=AB2> =>2KE ?@54;>65=89 ?>-?@56=5<C >AB0=CBAO ?@82;5:0B5;L=K<8. 564C B5<, 0=0;8B8:8 ?@845@6820NBAO 8=>3> <=5=8O: ?>4>1=K5 <5@K <>3CB 2K720BL @57:89 >BB>: 01>=5=B>2, GB> ?@8=5A5B 1>;LH5 C1KB:>2, G5< ?@81K;8.  #:@08=0: @0AAB0=>2:0 A8;.045=85 ?@><KH;5==>3> ?@>872>4AB20 2 #:@08=5 70 1 :20@B0; B5:CI53> 3>40 ?@52KA8;> 30%. -B> =08EC4H89 @57C;LB0B A@548 AB@0= !. 0:A8<0;L=0O 8=D;OF8O B0:65 1K;0 70D8:A8@>20=0 2 #:@08=5. =45:A ?>B@518B5;LA:8E F5= 2K@>A =0 20%. , ?> 2A5< ?@>3=>70<, #:@08=0 AB0=5B ;845@>< ?> B5<?0< ?045=8O . > 4065 ?;0G52=>5 A>AB>O=85 =0F8>=0;L=>9 M:>=><8:8 =5 AB8<C;8@C5B ?>;8B8G5A:85 A8;K AB@0=K : >1J548=5=8N 2 1>@L15 A :@878A><. !E20B:0 70 2;0ABL ?@>4>;605BAO. 0:>5 1C4CI55 645B #:@08=C? "5;54>:C<5=B0;8AB8:0 2 >AA88.> >F5=:0< M:A?5@B>2, @59B8=38 <=>3>G8A;5==KE B5;52878>==KE B>:-H>C ?040NB, ?@8 MB>< @0AB5B 7@8B5;LA:89 8=B5@5A : 4>:C<5=B0;L=K< D8;L<0<. 4=0:> ?@8 @>AB5 ?>?C;O@=>AB8 8 C25;8G5=88 >1J5<>2 ?@>872>4AB20 :0G5AB2> <=>38E 4>:C<5=B0;L=KE ?@>5:B>2 >AB02;O5B 65;0BL ;CGH53>. > :0:8< 70:>=0< @0728205BAO @>AA89A:0O B5;54>:C<5=B0;8AB8:0? 0:85 60=@K =081>;55 2>AB@51>20=K A53>4=O? 'B> =C6=>, GB>1K A45;0BL ?>-=0AB>OI5<C :0G5AB25==K9 4>:C<5=B0;L=K9 D8;L<?OB=8F0, 03.07.2009 3. =45:A FTSE. !@>G=> " $>:CA".!53>4=O 2 ?@>3@0<<5 A0<K5 ?>A;54=85 B5=45=F88 @>AA89A:8E >B@0A;59. 0: ?>2;8ONB =0 @K=>: ?@>87>H54H85 2 AB@0=5 A>1KB8O? 0:85 ?@>F5AAK =0 MB>9 =545;5 1K;8 2 F5=B@5 2=8<0=8O? B> 709<5B ;848@CNI85 ?>78F88 =0 @K=:5, 0 :><C ?@845BAO C9B8?  !>1KB85 =545;8. ?>;8B8G5A:>9, >1I5AB25==>9 8 M:>=><8G5A:>9 687=8 AB@0=K 2@5<O >B 2@5<5=8 ?@>8AE>4OB A>1KB8O, :>B>@K5 A@07C ?>;CG0NB <>I=K9 @57>=0=A, ;><0NB A;>682H85AO B5=45=F88, AB@5<8B5;L=> <5=ONB =0AB@>5=8O 8 273;O4K <8;;8>=>2 ;N459. > G0I5 ?@>8AE>4OB <5=55 7=0G8<K5 A>1KB8O, :>B>@K5 B>;L:> ?> ?@>H5AB288 :0:>3>-B> 2@5<5=8 ?>;CG0NB 4>;6=CN >F5=:C. > 8 B5, 8 4@C385 70A;C6820NB 2=8<0=8O, ?>B><C GB> 40NB 2>7<>6=>ABL ?@028;L=> ?>=OBL B>, GB> ?@>8AE>48B 2>:@C3 =0A.  K=:8. ;>10;L=K9 273;O4. #K: <564C=0@>4=>5 ?0@B=5@AB2>.@!53>4=O :064K9 ABC45=B AB@5<8BAO ?>;CG8BL 48?;><, :>B>@K9 1K ?@87=020;8 70 @C156><.  >4=0 87 ?@8>@8B5B=KE 7040G 4;O @>AA89A:8E 2C7>2 - 2;8BLAO 2 <8@>2CN A8AB5<C >1@07>20=8O. 0:85 ACI5AB2CNB D>@<K A>B@C4=8G5AB20 A 70@C156=K<8 C=825@A8B5B0<8? 'B> B0:>5 <564C=0@>4=0O 0::@548B0F8O 2C70? 'B> 405B ABC45=BC 42>9=>9 48?;><?04:4504:5105:08CC11>B0, 04.07.2009 3.!0;>=. >< ?>3@0=8G=KE >ICI5=89.q5;>5 8 G5@=>5. 8=8<0;87< 8 MB=8G5A:89 AB8;L. CAB>B0 8 <0:A8<0;L=0O =0AKI5==>ABL 45B0;O<8. 3@0O =0 :>=B@0AB0E 8 10;0=A8@CO =0 3@0=8 4>72>;5==>3>, EC4>6=8: ?> 8=B5@L5@0< M4 ;852 A>740; :20@B8@C 4;O 687=8 8 >D8F80;L=KE ?@85<>2. 0640O 87 2-E ?>;>28=>: MB>3> 68;LO  ?@820B 7>=0 8 show-room  2K7K205B A2>8 O@:85 >ICI5=8O 8 ?@54;0305B A2>5 ?>=8<0=85 CNB0 8 :><D>@B0. 06:2307:0807:4007:4607:5208:0808:3709:0809:37  @078;8O.10:0810:3711:0811:3712:0813:0813:37 >< 4;O >B4KE0.>< 4;O 687=8. 5 4;O 4@C759, =5 4;O HC<=KE 25G5@8=<>:, 0 4;O A51O.  2004-< MB>B ?@>5:B 0@E8B5:B>@0 ;5=K =4@552>9 AB0; ;0C@50B>< ?@5AB86=>9 0@E8B5:BC@=>9 ?@5<88  % 8 ?>;CG8; 2KAHCN =03@04C 2 :>=:C@A5 450;L=>5 ?@>AB@0=AB2> 4;O 687=8. @>ABK5 8=BC8B82=> ?>=OB=K5 D>@<K 8 =0BC@0;L=K5 <0B5@80;K. B:@KBK5 ?@>AB@0=AB20 8 <0;5=L:85 70:CB:8, :C40 ?@8OB=> A?@OB0BLAO. B4KE 4;O 3;07, 4;O @07C<0 8 4;O 4CH8. 14:0814:2214:3715:0815:3716:0816:3717:0818:0818:3719:0819:4019:4619:5220:0820:3721:0821:2221:3722:0822:3723:0800:0800:3701:0702:0702:3703:0703:3704:0704:3705:07>A:@5A5=85, 05.07.2009 3.06:0806:3607:3708:2310:0910:2211:0911:1511:2118:0919:3723:1023:1623:2200:2302:0602:1902:2504:1104:1504:2104:4105:1205:1805:22( A 29 8N=O ?> 05 8N;O )  A*+&?'?(?)?"dXX??U} } } } # ]<P-    A EPACD,13,13,13,13 %(noo %(,13,13,13,13,13,13,13,13,13,13,139:;ACDNNOuvv %(,13,13,13 %(ghi %(,13,13,13 %(NNO,13  \^_            QSTnoo  NNO  JJK,13 `bd \^_QST! QSTprt  9:;  VXY,13 `bd  ,13 prt  ,13  noo,139:;VXYQSTQSTQSTVXY`bduvv`bd  9:;  prt   %(  ,13         !NNO`bd  ԯVXYprt ACDghi! ,13,13JJKJJKACDACD,13   noo`bd !!!!!!    prt    ,13 񯰱9:; VXY`bd       !9:;prtprt ,139:; 9:;,13  `bd  prtprt  JJK   %(,13   `bd  prt  QST  ,13,13 noo˹VXY! NNO`bd  nooprt  VXY  !QSTghi\^_,13      QST`bd            ACDprt  \^_    `bd %(             ,13VXYVXY                 ACDghi      %(     `bdᯰzr7zBzBzBzBzBzBzBzBzBzBzBzBzBzBzBzBzBr7ίvr7zBzBzBzBzBzBzBzBzBzBzBzBzBzBzBzBzBr7Üvr7zBzBzBzBzBzBzBzBzBzBzBzBzBzBzBzBzBr7v=WD]D]D]D]D]D]D]D]D]D]D]D]D]D]D]D]D]=WÜzB=WD]D]D]D]D]D]D]D]D]D]D]D]D]D]D]D]D]=WvzB=WD]D]D]D]D]D]D]D]D]D]D]D]D]D]D]D]D]=WzBTkLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~Hr7D]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HTkLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~H~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HÜD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~Hbw"D]LdLdLdLdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~H uvvWD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~H,139:;ÜD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HVXY`bdr7D]LdLdLdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HQSTNNOD]LdLdLdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKNNObw"D]LdLdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKD]LdLdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJK`bdbw"D]LdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKprtD]LdLdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKACDbw"D]LdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKuvvD]LdLdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJK9:;bw"D]LdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJK{{{prtίD]LdLdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKNNOvD]LdLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKuvvbw"D]LdLdLdLdLdLdLdD]Ü~HD]LdLdLdL< dLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJK9:;=WLdLdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKNNOr7D]LdLdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKghiuvvbw"D]LdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJK9:;ÜD]LdLdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKghivD]LdLdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnoor7D]LdLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJK9:;TkD]LdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJK9:;ÜD]D]LdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKVXYÜLdLdD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKprtz=WD]Ü~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJK9:;zB=WÜ~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJK9:;r7~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJK,13~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKQSTzBD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKVXYD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKVXYLdD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKuvvuvvÜLdD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooίTkD]LdLdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnoo9:;JJKbw"D]LdLdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooVXY{{{9:;~HD]LdLdLdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooNNOԹ,13zLdD]LdLdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooNNO9:;ÜTkD]LdLdLdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooNNO\^_\^_ىWD]D]LdLdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooNNO{{{9:;ÜTkD]LdLdLdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooNNO9:;r7D]D]LdLdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooNNO`bd`bdÜr7D]LdLdD]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooNNO,13랫vTkD]D]v~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooNNOJJK\^_҉W=Wv~HD]LdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooNNOACDÜÜ~H=WLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooNNO󆈉9:;ٞvTkD]D]LdLdLdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooNNO\^_ACD垫vbw"D]D]LdLdLdLdLdLdLdLdLdLdLdLdD]~HJJKnooNNO9:;Ür7LdD]D]LdLdLdLdLdLdLdLdLdD]~HJJKnooNNOghiQST9:;{{{ҺÜ~HTkD]D]LdLdLdLdLdLdD]~HJJKnooNNOnooQSTACDnooٯ~HTkD]D]D]LdLdD]~HJJKnooNNOnooQST玏ACDNNOÜvr7TkD]=WzBJJKnooNNOnooQSTghighi{{{ٺÜvJJKnooNNOnooQSTԆNNOJJKuvvACDnooJJKnooQSTˎVXYACDNNO9:;ACDJJKJJKJJKJJKJJKJJKJJKJJKJJKJJKJJKJJKJJKJJKJJKJJKNNO,13VXY9:;NNOJJKJJKJJKJJKJJKJJKJJKJJKJJKJJKJJKJJKJJKJJKJJKNNO,13\^_9:;NNOJJKJJKJJKJJKJJKJJKJJKJJKNNOQSTVXYVXYACD <!uvv                                                                          !   "   #    $   %   & ! ! ! !' " " " "( # # #) $ $ $* % % %+ & & & &, ' ' '- ( ( (. ) ) )/ * * * *0 + + +1 , ,2 , ,3 - - - -4 . . .5 /6 / /7 0 0 0 08 19 1: 1; 1< 2 2 2 2= 3 3 3> 4? 4 4@ 5 5 5 5A 6B 6C 6D 6E 7 7 7 7F 8 8 8G 96 9 9H : : : :I ;J ;K ;L ;M < < < <N = = =O >? > >P ? ? ? ?Q @9 @R @S @T A A A AU B B BV C6 C CW D D D DX EB EY EZ E[ F F F F\ G G G] H? H H^ I I I I_ J J` J Ja K K K Kb L L Lc M6 M Md N N N Ne O Of O Og P P P Ph Q Q Q Qi R R Rj S? S Sk T T T Tl Um Un Uo Up V V V Vq W Wr W Ws X6 X Xt Y Y Y Yu ZJ Zv Zw Zx [ [ [ [y \ \ \ \z ]? ] ] ]{ ^ ^ ^ ^| _} _ _~ _ ` ` ` ` a a a b6 b b c c c c d d d e e e f f f g g g h? h h i i i i j j j k k k l l l l m m m n n n o o o o p} p p q q q q r r r r s s s s tm t t t u u u v v v w w w x9 x x x y y y z z z { { { |B |Y |Z | } } } ~ ~ ~    J                                                                                                              !   "   #    $   %   &    '   (   )   *   +    ,   -   .   /   0   1  2  3    4   5 6  7    8 9   <    =   > ?  @    A B Y Z E    F   G 6  H    I J   M    N   O ?  P    Q 9   T   U   V 6  W    X B   [    \   ] ?  ^    _  `  a    b   c 6  d    e  f  g    h    i   j ?  k    l m   p    q  r  s 6  t    u J   x    y   z ?  {    | }           6                   ?                          }               m             9             B             J                                                                    ! ! ! " " " " # # # # $ $ $ $ % % % & & & & ' ' ' ( ( ( ( ) ) ) * * * * + + + + , , , , - - -! . . . ." / / /# 0 0 0 0$ 1 1 1% 2 2 2& 3 3 3 3' 4 4 4( 5 5 5) 6 6 6* 7 7 7+ 8 8 8 8, 9 9 9- : : :. ; ; ;/ < < < <0 = = =1 > >2 > >3 ? ? ? ?4 @ @ @5 A6 A A7 B B B8 C9 C C C< D D D= E E E> F? F F@ G G GA HB H H HE I I IF J J JG K6 K KH L L LI MJ M M MM N N NN O O OO P? P PP Q Q QQ R9 R R RT S S SU T T TV U6 U UW V V VX WB W W W[ X X X\ Y Y Y] Z? Z Z^ [ [ [_ \ \` \ \a ] ] ]b ^ ^ ^c _6 _ _d ` ` `e a af a ag b b b bh c c ci d d dj e? e ek f f fl gm g g gp h h hq i ir i is j6 j jt k k ku lJ l l lx m m my n n nz o? o o{ p p p| q} q q q r r r s s s t6 t t u u u v v v w w w x x x y y y z? z z { { { | | | } } } ~ ~ ~          }             m             9             B             J                                                                                                          !   "   #   $   %   &    '   (   )   *   +   ,   -   .   /   0   1  2  3   4   5 6  7   8 9   <   =   > ?  @   A B   E   F   G 6  H   I J   M   N   O ?  P   Q 9   T   U   V 6  W   X B  [   \   ] ?  ^   _  `  a   b   c 6  d   e  f  g    h   i   j ?  k   l m   p   q  r  s 6  t   u J   x   y   z ?  {   | }         6                  ?                        }             m             9             B            J                   ! ! ! " " " # # # $ $ $ % % % & & & ' ' ' ' ) + + + , , , - - - . . . / / / 0 0 0 1 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9 : : : ; ; ; < < < = = = > > > ? ? ?! @ @ @" A A A# B B B$ C C C% D D D& E E E E' F F F( G G G) H H H* I I I+ J J J, K K K- L L L. M M M/ N N N0 O O O1 P P2 P P3 Q Q Q4 R R R5 S6 S S7 T T T8 U9 U U U< V V V= W W W> X? X X@ Y Y YA ZB Z ZE [ [ [F \ \ \G ]6 ] ]H ^ ^ ^I _J _ _ _M ` ` `N a a aO b? b bP c c cQ d9 d d dT e e eU f f fV g6 g gW h h hX iB i i[ j j j\ k k k] l? l l^ m m m_ n n na o o ob p p pc q6 q qd r r re s s sg t t t th u u ui v v vj w? w wk x x xl ym y y yp z z zq { {r { {s |6 | |t } } }u ~J ~ ~ ~x   y   z ?  {   | }         6               ?                     }             m             9             B            J                       9       m                            B Y Z                       J      $ 9      ( B      ,       0 m      4 J      8 9      =       @   A B  E   F m      I J      N          Q 9      U m      X B      \      _ J      b 9      e m   g   i        l 9       q J       u              y       {   | m                 9       B Y Z     J       m       9             B       J       m              9       J       m             B      9        m    !       "      #    !B ! ! ! " " " #m # # #$ $ $ $ % % % % & & &% ' ' ' (J ( ( ( ) ) )$ *9 * * * + + +( ,B , , - - -, . . . .& / / /' 0 0 00 1m 1 1 1 2 2 24 3 3 3( 4 4 4) 5 5 5* 6 6 68 79 7 7 7 8 8 8= 9 9 9 : : :A ;B ;Y ;Z ;E < < <F =m = = = > > >I ?J ? ? ? @ @ @N A A A A B B BQ C9 C C C D D DU Em E E E F F FX GB G G G H H H\ I I I I J J J^ K K K_ LJ L L L M M Mb N9 N N N O O Oe Pm P P Pg Q Q Qi R R R R+ S S Sk T T Tl U9 U U U  V V Vq WJ W W W  X X Xu YB Y Y, Z Z Zy [ [ [ \ \ \| ]m ] ] ] ^ ^ ^ _ _ _ _ ` ` ` a9 a a a b b b cB c c c d d d eJ e e e f f f g g g- h h h. i i i/ j j j k9 k k k l l l m m m m n n n0 o o o pB p p q q q rJ r r r s s s tm t t t u u u v v v v1 w w w2 x x x3 y y y z9 z z z { { { |J | | | } } } ~m ~ ~ ~         4   5   6         7             8   9   :                ;>@ Root EntryBook m  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~spreadsheet-0.9.0/test/data/test_empty.xls0000644000004100000410000027600012216006760020701 0ustar www-datawww-dataࡱ;   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~Root Entry  !"#$%&(+  \pCalc Ba=@=@ 8"1Arial1Arial1Arial1Arial GENERAL                + ) , *  `rSheet14Sheet2Sheet3jb( 3  @@     dMbP?_%*+# &C&"Times New Roman,Normal"&12&A(%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }  PH0(  >@   dMbP?_%*+# &C&"Times New Roman,Normal"&12&A(%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }  PH 0(  >@<d   dMbP?_%*+# &C&"Times New Roman,Normal"&12&A(%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }  PH0 0(   >@<d  FMicrosoft Excel 97-TabelleBiff8՜.+,D՜.+,\Oh+'0`HP d p |  Hannes Wyss0@@@(F5@G(`(uRoot EntryF Workbook CompObj'IOle )SummaryInformation( aDocumentSummaryInformation8*tspreadsheet-0.9.0/test/data/test_long_sst_record.xls0000644000004100000410000014700012216006760022726 0ustar www-datawww-dataࡱ> bd  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`aeRoot Entry F<E uc@WorkbookSummaryInformation(DocumentSummaryInformation8 \p Chinese User Ba==iGK-8X@"1b[SO1b[SO1b[SO1b[SO1b[SO1b[SO1 b[SO1h8b[SO1,8b[SO18b[SO18b[SO1b[SO1b[SO1b[SO14b[SO1 b[SO1b[SO1 b[SO14b[SO1<b[SO1?b[SO1>b[SO1bArial+""#,##0;""\-#,##05""#,##0;[Red]""\-#,##07""#,##0.00;""\-#,##0.00A""#,##0.00;[Red]""\-#,##0.00i*2_ ""* #,##0_ ;_ ""* \-#,##0_ ;_ ""* "-"_ ;_ @_ .))_ * #,##0_ ;_ * \-#,##0_ ;_ * "-"_ ;_ @_ y,:_ ""* #,##0.00_ ;_ ""* \-#,##0.00_ ;_ ""* "-"??_ ;_ @_ 6+1_ * #,##0.00_ ;_ * \-#,##0.00_ ;_ * "-"??_ ;_ @_ \$#,##0_);\(\$#,##0\)\$#,##0_);[Red]\(\$#,##0\) \$#,##0.00_);\(\$#,##0.00\)% \$#,##0.00_);[Red]\(\$#,##0.00\)                                      P  P          a> , *   ff   ` + )               ||? }-} .00\)_ *}-} .00\)_ *}-} .00\)_ *}-} .00\)_ *}-} .00\)_ *}-} .00\)_ *}-} .00\)_ *}-} .00\)_ *}-} .00\)_ *}-}  .00\)_ *}-}  .00\)_ *}-}  .00\)_ *}-}  .00\)_ *}-}  .00\)_ *}-} .00\)_ *}-} .00\)_ *}A} .00\)_ *ef-#,#}A} .00\)_ *ef-#,#}A} .00\)_ *ef-#,#}A} .00\)_ *ef-#,#}A} .00\)_ *ef-#,#}A} .00\)_ *ef -#,#}A} .00\)_ *L-#,#}A} .00\)_ *L-#,#}A} .00\)_ *L-#,#}A} .00\)_ *L-#,#}A} .00\)_ *L-#,#}A} .00\)_ *L -#,#}A} .00\)_ *23-#,#}A} .00\)_ *23-#,#}A} .00\)_ *23-#,#}A} .00\)_ *23-#,#}A}  .00\)_ *23-#,#}A}! .00\)_ *23 -#,#}-}" .00\)_ *}-}# .00\)_ *}A}$ .00\)_ *-#,#}A}% .00\)_ *?-#,#}A}& .00\)_ *23-#,#}-}' .00\)_ *}A}( .00\)_ *-#,#}A}) a.00\)_ *-#,#}U}* .00\)_ *-#,# ;_ }-}+ .00\)_ *}-}, .00\)_ *}}- }.00\)_ *-#,# ;_ -"?? _ }}. .00\)_ *-#,#??? ;_ ???-"?? ???_ ???}-}/ .00\)_ *}-}0 .00\)_ *}A}1 }.00\)_ *-#,#}-}2 .00\)_ *}-}3 .00\)_ *}A}4 .00\)_ *-#,#}A}5 .00\)_ *-#,#}A}6 .00\)_ *-#,#}A}7 .00\)_ *-#,#}A}8 .00\)_ *-#,#}A}9 .00\)_ * -#,#}A}: e.00\)_ *-#,#}}; ???.00\)_ *-#,#??? ;_ ???-"?? ???_ ???}}< ??v.00\)_ *̙-#,# ;_ -"?? _ }}= .00\)_ *-#,# ;_ -"?? _ }(}> .00\)_ *!20% - :_eW[r 1O20% - :_eW[r 1 ef %!20% - :_eW[r 2O"20% - :_eW[r 2 ef %!20% - :_eW[r 3O&20% - :_eW[r 3 ef %!20% - :_eW[r 4O*20% - :_eW[r 4 ef %!20% - :_eW[r 5O.20% - :_eW[r 5 ef %!20% - :_eW[r 6O220% - :_eW[r 6  ef %!40% - :_eW[r 1O40% - :_eW[r 1 L %!40% - :_eW[r 2O#40% - :_eW[r 2 L湸 %!40% - :_eW[r 3O'40% - :_eW[r 3 L %!40% - :_eW[r 4O+40% - :_eW[r 4 L %!40% - :_eW[r 5O/40% - :_eW[r 5 L %!40% - :_eW[r 6O340% - :_eW[r 6  Lմ %!60% - :_eW[r 1O 60% - :_eW[r 1 23 %!60% - :_eW[r 2O$60% - :_eW[r 2 23ٗ %!60% - :_eW[r 3O(60% - :_eW[r 3 23֚ %!60% - :_eW[r 4O,60% - :_eW[r 4 23 %! 60% - :_eW[r 5O060% - :_eW[r 5 23 %!!60% - :_eW[r 6O460% - :_eW[r 6  23 %"~vRk #h+h I}% $h 1=h 1 I}%O %h 2=h 2 I}%? &h 3=h 3 I}%23 'h 4/h 4 I}%(]5]  %+8^ĉ %)}Y5}Y  a% *Gl;`GGl;` %OO+'^, '^[0] -{o{  }% .hgUSCQ > > > > > > > >2>@7ggD Oh+'0p08 P \hChinese User@@mt՜.+,0HP X`hp x  test  F#Microsoft Office Excel 2003 CompObjoBiff8Excel.Sheet.89qspreadsheet-0.9.0/test/data/test_merged_cells.xls0000644000004100000410000001500012216006760022157 0ustar www-datawww-dataࡱ;   Root Entry  !"#$%&'()*+,.124  \pCalc Ba==@ 8@"1Arial1Arial1Arial1Arial GENERAL                + ) , *   `Sheet1JSheet2) Sheet3Tjb( 3  @@  (merged cells test spreadsheet  cc   dMbP?_%*+$!&C&"Times New Roman,Regular"&12&A)&&C&"Times New Roman,Regular"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }   ## ~ ~  PH0(  >@gg   dMbP?_% *+$!&C&"Times New Roman,Regular"&12&A)&&C&"Times New Roman,Regular"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }  PH 0(  >@gg   dMbP?_% *+$!&C&"Times New Roman,Regular"&12&A)&&C&"Times New Roman,Regular"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }  PH0 0(   >@gg  FMicrosoft Excel 97-TabelleBiff8Oh+'0|8 @ L X d p1@@@i@՜.+,D՜.+,\Root EntryF@ Workbook CompObj-IOle /SummaryInformation(0DocumentSummaryInformation83tspreadsheet-0.9.0/test/data/test_merged_and_protected.xls0000644000004100000410000004500012216006760023673 0ustar www-datawww-dataࡱ> #  !"R F com.apple.print.PageFormat.PMHorizontalRes com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMHorizontalRes 720 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMOrientation com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMOrientation 1 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMScaling com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMScaling 1 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalRes com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalRes 720 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalScaling com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalScaling 1 com.apple.print.ticket.stateFlag 0 com.apple.print.subTicket.paper_info_ticket PMPPDPaperCodeName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray PMPPDPaperCodeName A4 com.apple.print.ticket.stateFlag 0 PMTiogaPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray PMTiogaPaperName iso-a4 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMAdjustedPageRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPageRect 0 0 7830 5590 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMAdjustedPaperRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPaperRect -180 -180 8240 5770 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMPaperName iso-a4 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMUnadjustedPageRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPageRect 0 0 783 559 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMUnadjustedPaperRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPaperRect -18 -18 824 577 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.ppd.PMPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.ppd.PMPaperName A4 com.apple.print.ticket.stateFlag 0 com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.type com.apple.print.PaperInfoTicket com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.type com.apple.print.PageFormatTicket MzLL 0g{HH(dh "d??U      v(P>@dd@  7  ՜.+,0 PXh px 'Home Sheet1  Worksheets FMicrosoft Excel Sheet8FIBExcel.Sheet.8 Oh+'0LHPdx ' Apple Apple Apple AppleMicrosoft Excel@p '@p?\B'GPICTJ HHJ JHH sJJQo{sgZwsgZwk[swsco{wo|k{wc_wsgZwscg:wgZw\swkZJt_wgZ{cB2o|wo|FSRwo|Zc9wg[wNZcgZ` swwNtB2oR>owgZ{JtB2c9o|wo|NJtwo|Ro|g[wRgZZgZb swwZ5gZB2JtwwgZ{_Ro|ws>Nwo|w>Jtg[{>B3{gZ4swk[>_wgZ{o|wwo|g[gZ)k[k{gZwswsswswo{wssJt ssJRcVskZ{o{o{{o{kZZ{o{o{o{c^Vg9kZo{{{CkZZcg9{Zo{{kZkZswo{o{{wwo{{Z{o{o{^so{o{{wskZo{w{Nso{sso{{wwo{o{kZ^wwsw^^swwsJtRsRJRNsVVRZZNso{RVNsZVVF1ZZRkZVNsNsZV9VVNsccRNsZZNsRJRVNso{{{RRJRRo{NsRJRNsNssNswJRZRJRNso{RRNsRNsNssVF1Nsg9Ns JRo{VNssBRVVJR^wswVwsg9kZI^kZkZZo{^kZckZ^kZ^kZZkZwkZkZZo{g9sg9kZcg9wkZcZg9Zg9^g9o{Zw{F1kZkZwZo{Vg9^kZ^g9kZkZZg9kZkZo{^g9kZkZcVwskZ ^g9V=kZ^kZ^kZ^kZZkZcg9kZw+g:c:g:wso{wswckZwo{wBs{g:Z{s{wwwwwwwswRo|9s5JRcwo{skZs{wwkZ{ssg9RRo{kZwwwg9wVss{g9Vwo{scNsF1{kZ{g9Nso{g9g9wsg9VwwssV8s=^RJRJR^F1cF1JRRZRJRVo{RRsRRVwNsVRVNsNsRsF1Rg9VJRw{RJRNsVNsRwVRRNsNswws^Zw1sg9Vccg9^kZ^g9{Zg9kZwZg9o{kZZg9o{Zg9skZkZc^g9ckZcskZZwVg9^kZZkZo{kZV=ww-k[k{k[{ww{wkZo{{s{s{.so|Jtws{{ww.ssB2{s{{ww.s{ZkZs{{ww,sk{_s{{ww+k[k{k[{ww{ww{s{s{s{>{ sRVg9VVkZVZ{{ZZo{o{{kZwF1kZws^{R{o{o{{{o{{o{{w{o{sc{so{Zwo{{sZ^wwsNB2o7ss^sBJRNsNskZVw{RNsVNs^Nso{ZVBNsVRR{RF1kZVVR^NscRF1Ro{F1ZF1JRRsNscNsVVRZwwskZ7sZVo{wwsZZ{VkZo{g9o{Z{o{kZcckZcVg9^o{ZZkZwVRo{kZsZo{Zg9^kZ^g9kZZo{Zcsww3k[k{k[{ww{sw{ww{s{s{.sZZws{{ww.so{FSws{{ww.s{Vk{s{{ww,so|cs{{ww+k[k{k[{sw{ws{s{s{.sV_ws{{ww.sB2Vg:s{{ww.sk[^ws{{ww+g:c:g:wsswsswo{wo{w.s{k[Zk{{s{{ww.ssZg9s{{ww*sRs{{ww,soss{{ww+k[k{k[{sw{ws{s{s{.sZNo|s{{ww.sZ5ss{{ww.sJtk[^s{{ww,ss_s{{ww+k[k{k[{sw{ws{s{s{.sRVk[s{{ww.scVZs{{ww.so|^{s{{ww+g:c:g:wsswsswo{wo{w2 s{{kZswVw{{s{{ww2 sRgZVg:Rs{{ww2 sc9gZJt{Jts{{ww2 scc9sZss{{ww+k[k{k[{sw{ws{s{s{0sVgZwB3s{{ww0sc9gZJts{{ww2 s_cFS{s{{ww2 sg9g:wcss{{ww+k[k{k[{sw{ws{s{s{2 sVgZsRc9s{{ww2 sc9gZ{V{s{{ww2 scc9g:Zss{{ww+k[k{k[{sw{ws{s{s{2 sVgZsRgZs{{ww2 sc9gZ{B2os{{ww2 s_c{kZVs{{ww2 sg9g:s^{s{{ww+k[k{k[{sw{ws{s{s{2 sVgZJtk[s{{ww2 sc9gZZJt_s{{ww2 scc9sws{{ww+g:c:g:wsswsswo{wo{w2 s{{kZssZo{{{s{{ww2 sRgZgZ_ws{{ww2 sc9gZ{Vc9s{{wwCompObjTspreadsheet-0.9.0/test/data/test_margin.xls0000644000004100000410000007300012216006760021013 0ustar www-datawww-dataࡱ> .9  !"#$%&'()*+,-12345678R F7̥/WorkbookKVSummaryInformation(0DocumentSummaryInformation8 O\pPavel Evstigneev Ba==d98@"1Calibri1Calibri1Calibri1Calibri1Calibri1h8Cambria1,8Calibri18Calibri18Calibri1Calibri1Calibri1<Calibri1>Calibri1?Calibri14Calibri14Calibri1 Calibri1 Calibri1Calibri1Calibri1 Calibri1Calibri"Rp"#,##0_);\("Rp"#,##0\)#"Rp"#,##0_);[Red]\("Rp"#,##0\)$"Rp"#,##0.00_);\("Rp"#,##0.00\))$"Rp"#,##0.00_);[Red]\("Rp"#,##0.00\):*5_("Rp"* #,##0_);_("Rp"* \(#,##0\);_("Rp"* "-"_);_(@_).))_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)B,=_("Rp"* #,##0.00_);_("Rp"* \(#,##0.00\);_("Rp"* "-"??_);_(@_)6+1_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)                                                                       ff + ) , *     P  P        `            a>  " ||?گ}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-} 00\);_(*}-}  00\);_(*}-}  00\);_(*}-}  00\);_(*}-}  00\);_(*}-}  00\);_(*}-} 00\);_(*}-} 00\);_(*}-}+ 00\);_(*}-}, 00\);_(*}-}- 00\);_(*}-}. 00\);_(*}-}: 00\);_(*}-}; 00\);_(*}A}1 00\);_(*?_);_(@_}A}2 00\);_(*??_);_(@_}A}3 00\);_(*23?_);_(@_}-}4 00\);_(*}A}0 a00\);_(*?_);_(@_}A}( 00\);_(*?_);_(@_}A}7 e00\);_(*?_);_(@_}}5 ??v00\);_(*̙?_);_(@_   @|8VQ}}9 ???00\);_(*?_);_(@_??? ??? ??? ???@|8VQ}}) }00\);_(*?_);_(@_   @|8VQ}A}6 }00\);_(*?_);_(@_}}* 00\);_(*?_);_(@_??? ??? ??? ???@|8VQ}-}= 00\);_(*}}8 00\);_(*?_);_(@_   @|8VQ}-}/ 00\);_(*}U}< 00\);_(*?_);_(@_ }A}" 00\);_(*?_);_(@_}A} 00\);_(*ef?_);_(@_}A} 00\);_(*L?_);_(@_}A} 00\);_(*23?_);_(@_}A}# 00\);_(*?_);_(@_}A} 00\);_(*ef?_);_(@_}A} 00\);_(*L?_);_(@_}A} 00\);_(*23?_);_(@_}A}$ 00\);_(*?_);_(@_}A} 00\);_(*ef?_);_(@_}A} 00\);_(*L?_);_(@_}A} 00\);_(*23?_);_(@_}A}% 00\);_(*?_);_(@_}A} 00\);_(*ef?_);_(@_}A} 00\);_(*L?_);_(@_}A} 00\);_(*23?_);_(@_}A}& 00\);_(*?_);_(@_}A} 00\);_(*ef?_);_(@_}A} 00\);_(*L?_);_(@_}A}  00\);_(*23?_);_(@_}A}' 00\);_(* ?_);_(@_}A} 00\);_(*ef ?_);_(@_}A} 00\);_(*L ?_);_(@_}A}! 00\);_(*23 ?_);_(@_}-}> 00\);_(* 20% - Accent1M 20% - Accent1 ef % 20% - Accent2M" 20% - Accent2 ef % 20% - Accent3M& 20% - Accent3 ef % 20% - Accent4M* 20% - Accent4 ef % 20% - Accent5M. 20% - Accent5 ef % 20% - Accent6M2 20% - Accent6  ef % 40% - Accent1M 40% - Accent1 L % 40% - Accent2M# 40% - Accent2 L渷 % 40% - Accent3M' 40% - Accent3 L % 40% - Accent4M+ 40% - Accent4 L % 40% - Accent5M/ 40% - Accent5 L % 40% - Accent6M3 40% - Accent6  Lմ % 60% - Accent1M 60% - Accent1 23 % 60% - Accent2M$ 60% - Accent2 23ږ % 60% - Accent3M( 60% - Accent3 23כ % 60% - Accent4M, 60% - Accent4 23 % 60% - Accent5M0 60% - Accent5 23 %! 60% - Accent6M4 60% - Accent6  23 % "Accent1AAccent1 O % #Accent2A!Accent2 PM % $Accent3A%Accent3 Y % %Accent4A)Accent4 d % &Accent5A-Accent5 K % 'Accent6A1Accent6  F %(Bad9Bad  %) Calculation Calculation  }% * Check Cell Check Cell  %????????? ???+ Comma,( Comma [0]-&Currency.. Currency [0]/Explanatory TextG5Explanatory Text % 0Good;Good  a%1 Heading 1G Heading 1 I}%O2 Heading 2G Heading 2 I}%?3 Heading 3G Heading 3 I}%234 Heading 49 Heading 4 I}% 5InputuInput ̙ ??v% 6 Linked CellK Linked Cell }% 7NeutralANeutral  e%3Normal % 8Noteb Note   9OutputwOutput  ???%????????? ???:$Percent ;Title1Title I}% <TotalMTotal %OO= Warning Text? Warning Text %XTableStyleMedium9PivotStyleMedium48dq:Fc-2NWgFSWc-2NWgFSW̙̙3f3fff3f3f33333f33333\`;Sheet1"2 inch left margin $/  PK!pO[Content_Types].xmlj0Eжr(΢]yl#!MB;.n̨̽\A1&ҫ QWKvUbOX#&1`RT9<l#$>r `С-;c=1gMԯNDJ++2a,/$nECA6٥D-ʵ? dXiJF8,nx (MKoP(\HbWϿ})zg'8yV#x'˯?oOz3?^?O?~B,z_=yǿ~xPiL$M>7Ck9I#L nꎊ)f>\<|HL|3.ŅzI2O.&e>Ƈ8qBۙ5toG1sD1IB? }J^wi(#SKID ݠ1eBp{8yC]$f94^c>Y[XE>#{Sq c8 >;-&~ ..R(zy s^Fvԇ$*cߓqrB3' }'g7t4Kf"߇ފAV_] 2H7Hk;hIf;ZX_Fڲe}NM;SIvưõ[H5Dt(?]oQ|fNL{d׀O&kNa4%d8?L_H-Ak1h fx-jWBxlB -6j>},khxd׺rXg([x?eޓϲكkS1'|^=aѱnRvPK! ѐ'theme/theme/_rels/themeManager.xml.relsM 0wooӺ&݈Э5 6?$Q ,.aic21h:qm@RN;d`o7gK(M&$R(.1r'JЊT8V"AȻHu}|$b{P8g/]QAsم(#L[PK-!pO[Content_Types].xmlPK-!֧6 -_rels/.relsPK-!kytheme/theme/themeManager.xmlPK-!0ktheme/theme/theme1.xmlPK-! ѐ' theme/theme/_rels/themeManager.xml.relsPK] O UU  dMbP?_*+%,&@'?MM com.apple.print.PageFormat.PMHorizontalRes com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMHorizontalRes 300 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMOrientation com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMOrientation 1 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMScaling com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMScaling 1 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalRes com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalRes 300 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalScaling com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalScaling 1 com.apple.print.ticket.stateFlag 0 com.apple.print.subTicket.paper_info_ticket PMPPDPaperCodeName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray PMPPDPaperCodeName A4 com.apple.print.ticket.stateFlag 0 PMTiogaPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray PMTiogaPaperName iso-a4 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMAdjustedPageRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPageRect 0 0 3262.5000000000005 2329.166666666667 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMAdjustedPaperRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPaperRect -75 -75 3433.3333333333335 2404.166666666667 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMPaperName iso-a4 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMUnadjustedPageRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPageRect 0 0 783 559 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMUnadjustedPaperRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPaperRect -18 -18 824 577 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.ppd.PMPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.ppd.PMPaperName A4 com.apple.print.ticket.stateFlag 0 com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.type com.apple.print.PaperInfoTicket com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.type com.apple.print.PageFormatTicket Mz,,  j eg{HH(dh " d??U , >>,>@dd@  7ggD  ՜.+,0 PXt | 'Pavel production Sheet1  Worksheets F$Microsoft Excel 97 - 2004 Worksheet8FIBExcel.Sheet.8 Oh+'0HPl 'Pavel EvstigneevPavel EvstigneevMicrosoft Macintosh Excel@8@̥GPICTR HHR RHH|RR3kZo{kZkZwg9wg9wsswo{o{wg9wPk[ssco{w9kZF1JRkZg9^V^swBZRkZPkZo{wVo{ZVckZF1JRsg9F1swJRF1kZUkZo{{Vo{VkZJRkZF1R^g9ZZcswB^NskZNg9kZccg9g9Ro{{kZ{{g9wwsw{{kZ)^Zo{wkZwg9wo{o{wkZw<s{{w{{{ww{{wpwZZwsJRc^wg9kZsF1o{V{kZ{9ZVwkZsswkZ{kZwckZsw{{wpwo{Zw!RsNsF1^RRo{=JRV^BF1F1g9VNscF1JRkZ5g9NsNsJRcZF1JRw{{wtwZJR{w"c5kZNsNssRBg9NsNsVg9JRkZNssBNswRNsF1=g9cJRBcZRNsw{{w*wwVRkZw{{w&g9swswo{wsswo{w0wg9o{{www{{w.wwBwww{{w.wg9cwww{{w0ww=Rwww{{w&wwww{{w)kZo{swswo{wsswsw(o{wswssw{s.wZVwww{{w.wwF1www{{w.wkZJRwww{{w.w^Nswww{{w&wwww{{w&g9swo{wo{wsswo{w,w^www{{w.wZJRwww{{w2wo{Ns=wwww{{w,w^www{{w&wwww{{w&g9swo{wo{wsswo{w.wVVwww{{w.wNsswww{{w.wB{www{{w.w^cwww{{w&s{wss{{s$kZswswo{wswsw.wZ^www{{w0wo{JRZwww{{w2wsZVswww{{w.wg9g9www{{w+o{wsw{s{s{ww{s{+kZso{w{s{s{ww{s{0wZNso{www{{w.w{Vwww{{w.wVwwww{{w,wo{www{{w'kZo{swswo{wswsw#o{wss{{s0wNsJR{www{{w.wF1Bwww{{w2wwVVo{www{{w.wcg9www{{w&wwww{{w)c^co{so{so{so{so{s.wkZkZwww{{w2wwVRwwww{{w2w{NsJRkZwww{{w0wkZJR{www{{w,wwwww{{w&g9swo{wo{wsswo{w4ss{o{{www{{w6wsF1JRccwww{{w4wJRBwRwww{{w6wsF1sVVkZwww{{w&wwww{{w&g9swo{wo{wsswo{w4w{o{{o{www{{w4wwJRsJRwww{{w2wJRJRwww{{w6wsJRso{JRswww{{w&wwww{{w&g9swo{wo{wsswo{w4wwg9kZ^www{{w6w{JR^kZwww{{w2wJRo{Zwww{{w6wsRsNsVswww{{w&wwww{{w&g9swo{wo{wsswo{w6wwg9g9Z{www{{w6w{JR{Zo{www{{wCompObjbspreadsheet-0.9.0/test/data/test_copy.xls0000644000004100000410000027600012216006760020515 0ustar www-datawww-dataࡱ;   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~Root Entry  !"#$%&(+  \pCalc Ba=@=@ 8"1Arial1Arial1Arial1Arial GENERAL                + ) , *  `rSheet16Sheet2Sheet3aajb( 3  @@     dMbP?_%*+$!&C&"Times New Roman,Regular"&12&A)&&C&"Times New Roman,Regular"&12Page &P&333333?'333333?(-؂-?)-؂-?" d,,333333?333333?U } y PH0(  >@   dMbP?_%*+$!&C&"Times New Roman,Regular"&12&A)&&C&"Times New Roman,Regular"&12Page &P&333333?'333333?(-؂-?)-؂-?" d,,333333?333333?U } y PH 0(  >@<d   dMbP?_%*+$!&C&"Times New Roman,Regular"&12&A)&&C&"Times New Roman,Regular"&12Page &P&333333?'333333?(-؂-?)-؂-?" d,,333333?333333?U } y PH0 0(   >@<d  FMicrosoft Excel 97-TabelleBiff8՜.+,D՜.+,\Oh+'0`HP d p |  Hannes Wyss1@@f@i @fG(`(uRoot EntryF Workbook CompObj'IOle )SummaryInformation( aDocumentSummaryInformation8*tspreadsheet-0.9.0/test/data/test_adding_data_to_existing_file.xls0000644000004100000410000000700012216006760025365 0ustar www-datawww-dataࡱ;   Ba==@ 8"1Arial GENERAL                                  83ffff̙̙3f3fff3f3f33333f33333XMichael Rob Arjun   d_%U}   >   d_%U>   d_%U> Root EntryWorkbook>jT}>jT} spreadsheet-0.9.0/test/data/test_datetime.xls0000644000004100000410000001500012216006760021326 0ustar www-datawww-dataࡱ;   Root Entry  !"#$%&'()+./02  \pCalc Ba=@=@ 8"1Arial1Arial1Arial1Arial GENERAL HH:MM [H]:MM                + ) , *      `Sheet1Sheet2Sheet3aajb( 3  @@     dMbP?_%*+# &C&"Times New Roman,Normal"&12&A(%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }  UUUUUU?"""""?DDDDD?PH0(  >@K   dMbP?_%*+# &C&"Times New Roman,Normal"&12&A(%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }  PH 0(  >@   dMbP?_%*+# &C&"Times New Roman,Normal"&12&A(%&C&"Times New Roman,Normal"&12Page &P&333333?'333333?(-؂-?)-؂-?"d,,333333?333333?U }  PH0 0(   >@  FMicrosoft Excel 97-TabelleBiff8Oh+'0HPh |  Bjorn Hjelle Hannes Wyss3@:Q@@L@@H5՜.+,D՜.+,\Root EntryF Workbookk CompObj*IOle ,SummaryInformation(-DocumentSummaryInformation81tspreadsheet-0.9.0/test/format.rb0000644000004100000410000000655512216006760016666 0ustar www-datawww-data#!/usr/bin/env ruby # TestFormat -- Spreadsheet -- 06.11.2012 -- mina.git@naguib.ca $: << File.expand_path('../lib', File.dirname(__FILE__)) require 'test/unit' require 'spreadsheet' module Spreadsheet class TestFormat < Test::Unit::TestCase def setup @format = Format.new end def test_date? assert_equal false, @format.date? @format.number_format = "hms" assert_equal false, @format.date? @format.number_format = "Y" assert_equal true, @format.date? @format.number_format = "YMD" assert_equal true, @format.date? @format.number_format = "\\$#,##0.00_);[RED]\"($\"#,##0.00\\)" assert_equal false, @format.date? end def test_date_or_time? assert_equal false, @format.date_or_time? @format.number_format = "hms" assert_equal true, @format.date_or_time? @format.number_format = "YMD" assert_equal true, @format.date_or_time? @format.number_format = "hmsYMD" assert_equal true, @format.date_or_time? @format.number_format = "\\$#,##0.00_);[RED]\"($\"#,##0.00\\)" assert_equal false, @format.date? end def test_datetime? assert_equal false, @format.datetime? @format.number_format = "H" assert_equal false, @format.datetime? @format.number_format = "S" assert_equal false, @format.datetime? @format.number_format = "Y" assert_equal false, @format.datetime? @format.number_format = "HSYMD" assert_equal true, @format.datetime? @format.number_format = "\\$#,##0.00_);[RED]\"($\"#,##0.00\\)" assert_equal false, @format.date? end def test_time? assert_equal false, @format.time? @format.number_format = "YMD" assert_equal false, @format.time? @format.number_format = "hmsYMD" assert_equal true, @format.time? @format.number_format = "h" assert_equal true, @format.time? @format.number_format = "hm" assert_equal true, @format.time? @format.number_format = "hms" assert_equal true, @format.time? @format.number_format = "\\$#,##0.00_);[RED]\"($\"#,##0.00\\)" assert_equal false, @format.date? end def test_borders? assert_equal [:none, :none, :none, :none], @format.border @format.border = :thick assert_equal [:thick, :thick, :thick, :thick], @format.border @format.left = :hair assert_equal [:thick, :thick, :thick, :hair], @format.border @format.right = :hair assert_equal [:thick, :thick, :hair, :hair], @format.border @format.top = :hair assert_equal [:thick, :hair, :hair, :hair], @format.border @format.bottom = :hair assert_equal [:hair, :hair, :hair, :hair], @format.border assert_raises(ArgumentError) do @format.bottom = :bogus end assert_equal [:black, :black, :black, :black], @format.border_color @format.border_color = :green assert_equal [:green, :green, :green, :green], @format.border_color @format.left_color = :red assert_equal [:green, :green, :green, :red], @format.border_color @format.right_color = :red assert_equal [:green, :green, :red, :red], @format.border_color @format.top_color = :red assert_equal [:green, :red, :red, :red], @format.border_color @format.bottom_color = :red assert_equal [:red, :red, :red, :red], @format.border_color assert_raises(ArgumentError) do @format.bottom_color = :bogus end end end end spreadsheet-0.9.0/README.md0000644000004100000410000000560512216006760015344 0ustar www-datawww-dataLast Update: 31.05.2013 - Zeno R.R. Davatz # Spreadsheet https://github.com/zdavatz/spreadsheet The Mailing List can be found here: http://groups.google.com/group/rubyspreadsheet The code can be found here: https://github.com/zdavatz/spreadsheet For a viewable directory of all recent changes, please see: http://scm.ywesee.com/?p=spreadsheet/.git;a=summary To get a graphical overview of the Library please see http://spreadsheet.rubyforge.org/spreadsheet.jpeg For Non-GPLv3 commercial licencing, please see: http://www.spreadsheet.ch ## Description The Spreadsheet Library is designed to read and write Spreadsheet Documents. As of version 0.6.0, only Microsoft Excel compatible spreadsheets are supported. Spreadsheet is a combination/complete rewrite of the Spreadsheet::Excel Library by Daniel J. Berger and the ParseExcel Library by Hannes Wyss. Spreadsheet can read, write and modify Spreadsheet Documents. ## Notes from Users Alfred: a@boxbot.org: I think it should be noted in the README file that the library doesn't recognize cell formats in Excel created documents, which results in Floats returned for any number. ## What's new? * Supported outline (grouping) functions * Significantly improved memory-efficiency when reading large Excel Files * Limited Spreadsheet modification support * Improved handling of String Encodings ## On the Roadmap * Improved Format support/Styles * Document Modification: Formats/Styles * Formula Support * Document Modification: Formulas * Write-Support: BIFF5 * Ruby 1.9 Support; * Remove backward compatibility code Note: Spreadsheet supports Ruby 1.8.6, 1.8.7, 1.9.2, 1.9.3 You will get a deprecated warning about iconv when using spreadsheet with Ruby 1.9.3. So replacing iconv is on the Roadmap as well ;). ## Dependencies * ruby 1.8 * ruby-ole [ http://code.google.com/p/ruby-ole/ ] ## Examples * Have a look at the GUIDE: https://github.com/zdavatz/spreadsheet/blob/master/GUIDE.txt * Also look at: https://gist.github.com/phollyer/1214475 ## Installation Using RubyGems [ http://www.rubygems.org ]: * `sudo gem install spreadsheet` If you don't like RubyGems[http://www.rubygems.org], let me know which installation solution you prefer and I'll include it in the future. If you can use 'rake' and 'hoe' library is also installed, you can build a gem package as follows: * `rake gem` The gem package is built in pkg directory. ## Testing Bundler support added. Running tests: * `bundle install` * ./test/suite.rb ## TravisCI * https://travis-ci.org/zdavatz/spreadsheet ## Authors Original Code: Spreadsheet::Excel: Copyright (c) 2005 by Daniel J. Berger (djberg96@gmail.com) ParseExcel: Copyright (c) 2003 by Hannes Wyss (hannes.wyss@gmail.com) New Code: Copyright (c) 2010 ywesee GmbH (mhatakeyama@ywesee.com, zdavatz@ywesee.com) ## License This library is distributed under the GPLv3. Please see the LICENSE[link://files/LICENSE_txt.html] file. spreadsheet-0.9.0/Rakefile0000644000004100000410000000066312216006760015531 0ustar www-datawww-data# -*- ruby -*- $: << File.expand_path("./lib", File.dirname(__FILE__)) require 'rubygems' require 'bundler' require 'hoe' require './lib/spreadsheet.rb' ENV['RDOCOPT'] = '-c utf8' Hoe.plugin :git Hoe.spec('spreadsheet') do |p| p.developer('Masaomi Hatakeyama, Zeno R.R. Davatz','mhatakeyama@ywesee.com, zdavatz@ywesee.com') p.remote_rdoc_dir = 'spreadsheet' p.extra_deps << ['ruby-ole', '>=1.0'] end # vim: syntax=Ruby spreadsheet-0.9.0/GUIDE.md0000644000004100000410000002156212216006760015244 0ustar www-datawww-data# Getting Started with Spreadsheet This guide is meant to get you started using Spreadsheet. By the end of it, you should be able to read and write Spreadsheets. ## Reading is easy! First, make sure all that code is loaded: ```ruby require 'spreadsheet' ``` Worksheets come in various Encodings. You need to tell Spreadsheet which Encoding you want to deal with. The Default is UTF-8 ```ruby Spreadsheet.client_encoding = 'UTF-8' ``` Let's open a workbook: ```ruby book = Spreadsheet.open '/path/to/an/excel-file.xls' ``` We can either access all the Worksheets in a Workbook... ```ruby book.worksheets ``` ...or access them by index or name (encoded in your client_encoding) ```ruby sheet1 = book.worksheet 0 sheet2 = book.worksheet 'Sheet1' ``` Now you can either iterate over all rows that contain some data. A call to Worksheet.each without argument will omit empty rows at the beginning of the Worksheet: ```ruby sheet1.each do |row| # do something interesting with a row end ``` Or you can tell Worksheet how many rows should be omitted at the beginning. The following starts at the 3rd row, regardless of whether or not it or the preceding rows contain any data: ```ruby sheet2.each 2 do |row| # do something interesting with a row end ``` Or you can access rows directly, by their index (0-based): ```ruby row = sheet1.row(3) ``` To access the values stored in a Row, treat the Row like an Array. ```ruby row[0] ``` -> this will return a String, a Float, an Integer, a Formula, a Link or a Date or DateTime object - or nil if the cell is empty. More information about the formatting of a cell can be found in the Format with the equivalent index ```ruby row.format 2 ``` ## Writing is easy As before, make sure you have Spreadsheet required and the client_encoding set. Then make a new Workbook: ```ruby book = Spreadsheet::Workbook.new ``` Add a Worksheet and you're good to go: ```ruby sheet1 = book.create_worksheet ``` This will create a Worksheet with the Name "Worksheet1". If you prefer another name, you may do either of the following: ```ruby sheet2 = book.create_worksheet :name => 'My Second Worksheet' sheet1.name = 'My First Worksheet' ``` Now, add data to the Worksheet, using either Worksheet#[]=, Worksheet#update_row, or work directly on Row using any of the Array-Methods that modify an Array in place: ```ruby sheet1.row(0).concat %w{Name Country Acknowlegement} sheet1[1,0] = 'Japan' row = sheet1.row(1) row.push 'Creator of Ruby' row.unshift 'Yukihiro Matsumoto' sheet1.row(2).replace [ 'Daniel J. Berger', 'U.S.A.', 'Author of original code for Spreadsheet::Excel' ] sheet1.row(3).push 'Charles Lowe', 'Author of the ruby-ole Library' sheet1.row(3).insert 1, 'Unknown' sheet1.update_row 4, 'Hannes Wyss', 'Switzerland', 'Author' ``` Add some Formatting for flavour: ```ruby sheet1.row(0).height = 18 format = Spreadsheet::Format.new :color => :blue, :weight => :bold, :size => 18 sheet1.row(0).default_format = format bold = Spreadsheet::Format.new :weight => :bold 4.times do |x| sheet1.row(x + 1).set_format(0, bold) end ``` And finally, write the Excel File: ```ruby book.write '/path/to/output/excel-file.xls' ``` ## Modifying an existing Document Spreadsheet has some limited support for modifying an existing Document. This is done by copying verbatim those parts of an Excel-document which Spreadsheet can't modify (yet), recalculating relevant offsets, and writing the data that can be changed. Here's what should work: * Adding, changing and deleting cells. * You should be able to fill in Data to be evaluated by predefined Formulas Limitations: * Spreadsheet can only write BIFF8 (Excel97 and higher). The results of modifying an earlier version of Excel are undefined. * Spreadsheet does not modify Formatting at present. That means in particular that if you set the Value of a Cell to a Date, it can only be read as a Date if its Format was set correctly prior to the change. * Although it is theoretically possible, it is not recommended to write the resulting Document back to the same File/IO that it was read from. And here's how it works: ```ruby book = Spreadsheet.open '/path/to/an/excel-file.xls' sheet = book.worksheet 0 sheet.each do |row| row[0] *= 2 end book.write '/path/to/output/excel-file.xls' ``` ## Date and DateTime Excel does not know a separate Datatype for Dates. Instead it encodes Dates into standard floating-point numbers and recognizes a Date-Cell by its formatting-string: ```ruby row.format(3).number_format ``` Whenever a Cell's Format describes a Date or Time, Spreadsheet will give you the decoded Date or DateTime value. Should you need to access the underlying Float, you may do the following: ```ruby row.at(3) ``` If for some reason the Date-recognition fails, you may force Date-decoding: ```ruby row.date(3) row.datetime(3) ``` When you set the value of a Cell to a Date, Time or DateTime, Spreadsheet will try to set the cell's number-format to a corresponding value (one of Excel's builtin formats). If you have already defined a Date- or DateTime-format, Spreadsheet will use that instead. If a format has already been applied to a particular Cell, Spreadsheet will leave it untouched: ```ruby row[4] = Date.new 1975, 8, 21 # -> assigns the builtin Date-Format: 'M/D/YY' book.add_format Format.new(:number_format => 'DD.MM.YYYY hh:mm:ss') row[5] = DateTime.new 2008, 10, 12, 11, 59 # -> assigns the added DateTime-Format: 'DD.MM.YYYY hh:mm:ss' row.set_format 6, Format.new(:number_format => 'D-MMM-YYYY') row[6] = Time.new 2008, 10, 12 # -> the Format of cell 6 is left unchanged. ``` ## Outline (Grouping) and Hiding Spreadsheet supports outline (grouping) and hiding functions from version 0.6.5. In order to hide rows or columns, you can use 'hidden' property. As for outline, 'outline_level' property is also available. You can use both 'hidden' and 'outline_level' at the same time. You can create a new file with outline and hiding rows and columns as follows: ```ruby require 'spreadsheet' # create a new book and sheet book = Spreadsheet::Workbook.new sheet = book.create_worksheet 5.times {|j| 5.times {|i| sheet[j,i] = (i+1)*10**j}} # column sheet.column(2).hidden = true sheet.column(3).hidden = true sheet.column(2).outline_level = 1 sheet.column(3).outline_level = 1 # row sheet.row(2).hidden = true sheet.row(3).hidden = true sheet.row(2).outline_level = 1 sheet.row(3).outline_level = 1 # save file book.write 'out.xls' ``` Also you can read an existing file and change the hidden and outline properties. Here is the example below: ```ruby require 'spreadsheet' # read an existing file file = ARGV[0] book = Spreadsheet.open(file, 'rb') sheet= book.worksheet(0) # column sheet.column(2).hidden = true sheet.column(3).hidden = true sheet.column(2).outline_level = 1 sheet.column(3).outline_level = 1 # row sheet.row(2).hidden = true sheet.row(3).hidden = true sheet.row(2).outline_level = 1 sheet.row(3).outline_level = 1 # save file book.write "out.xls" ``` Notes * The outline_level should be under 8, which is due to the Excel data format. ## More about Encodings Spreadsheet assumes it's running on Ruby 1.8 with Iconv-support. It is your responsibility to handle Conversion Errors, or to prevent them e.g. by using the Iconv Transliteration and Ignore flags: Spreadsheet.client_encoding = 'LATIN1//TRANSLIT//IGNORE' ## Page setup (for printing) ```ruby sheet.pagesetup[:orientation] = :landscape # or :portrait (default) sheet.pagesetup[:adjust_to] = 85 # default 100 ``` ## Backward Compatibility Spreadsheet is designed to be a drop-in replacement for both ParseExcel and Spreadsheet::Excel. It provides a number of require-paths for backward compatibility with its predecessors. If you have been working with ParseExcel, you have probably used one or more of the following: ```ruby require 'parseexcel' require 'parseexcel/parseexcel' require 'parseexcel/parser' ``` Either of the above will define the ParseExcel.parse method as a facade to Spreadsheet.open. Additionally, this will alter Spreadsheets behavior to define the ParseExcel::Worksheet::Cell class and fill each parsed Row with instances thereof, which in turn provide ParseExcel's Cell#to_s(encoding) and Cell#date methods. You will have to manually uninstall the parseexcel library. If you are upgrading from Spreadsheet::Excel, you were probably using Workbook#add_worksheet and Worksheet#write, write_row or write_column. Use the following to load the code which provides them: ```ruby require 'spreadsheet/excel' ``` Again, you will have to manually uninstall the spreadsheet-excel library. If you perform fancy formatting, you may run into trouble as the Format implementation has changed considerably. If that is the case, please drop me a line at hannes.wyss@gmail.com and I will try to help you. Don't forget to include the offending code-snippet! All compatibility code is deprecated and will be removed in version 1.0.0 spreadsheet-0.9.0/History.md0000644000004100000410000004226012216006760016046 0ustar www-datawww-data### 0.9.0 / 16.09.2013 * Author: Pavel Date: Mon Sep 16 14:02:49 2013 +0700 * Test cases for Worksheet#margins, Worksheet#pagesetup, Workbook#delete_worksheet. Fix bugs related to it. * Page margins reader/writter * Markdownify GUIDE * Add page setup options (landscape or portrait and adjust_to) ### 0.8.9 / 24.08.2013 Author: Doug Renn Date: Fri Aug 23 17:10:24 2013 -0600 * Work around to handle number formats that are being mistaken time formats ### 0.8.8 / 02.08.2013 Author: Nathan Colgate Date: Thu Aug 1 15:01:57 2013 -0500 * Update excel/internals.rb to reference a valid Encoding type * Encoding.find("MACINTOSH") was throwing an error. Encoding.find("MACROMAN") does not. ### 0.8.7 / 24.07.2013 Author: Yasuhiro Asaka Date: Wed Jul 24 11:31:12 2013 +0900 * Remove warnings for test suite * warning: mismatched indentations at 'end' with 'class' at xxx * warning: method redefined; discarding old xxx * warning: assigned but unused variable xxx * warning: previous definition of xxx was here * The source :rubygems is deprecated because HTTP * requests are insecure. (Gemfile) ### 0.8.6 / 11.07.2013 Author: Arjun Anand and Robert Stern Date: Wed Jul 10 13:45:30 2013 -0400 * Allow editing of an existing worksheet. ### 0.8.5 / 24.04.2013 * Applied Patch by Joao Almeida: When editing an existing sheet, cells merge was not working. * https://github.com/voraz/spreadsheet/pull/14.patch ### 0.8.4 / 20.04.2013 * Applied Patch by boss@airbladesoftware.com * https://groups.google.com/d/msg/rubyspreadsheet/73IoEwSx69w/barE7uVnIzwJ ### 0.8.3 / 12.03.2013 Author: Keith Walsh Date: Mon Mar 11 16:48:25 2013 -0400 * Typo correction in guide example. ### 0.8.2 / 28.02.2013 Author: Roque Pinel Date: Wed Feb 27 12:10:29 2013 -0500 * Requiring BigDecimal for checking. * Made API friendly to BigDecimal precision. * Changes introduced by the user 'valeriusjames'. ### 0.8.1 / 18.02.2013 * Updated Manifest.txt to include lib/spreadsheet/excel/rgb.rb ### 0.8.0 / 18.02.2013 * Adding support for converting color palette values to RGB values (not vice-versa..yet) * by https://github.com/dancaugherty/spreadsheet/compare/master...rgb ### 0.7.9 / 06.02.2013 Author: Eugeniy Belyaev (zhekanax) * You can merge if you are interested in perl-like Workbook.set_custom_color implementation. I know it is not really a proper way to deal with custom colors, but nevertheless it makes it possible. * https://github.com/zdavatz/spreadsheet/pull/27 ### 0.7.8 / 06.02.2013 Author: Kenichi Kamiya Date: Wed Feb 6 11:23:35 2013 +0900 * Link to Travis CI on README * Remove warnings "assigned but unused variable" in test * Remove warnings "assigned but unused variable" * Enable $VERBOSE flag when running test ### 0.7.7 / 22.01.2013 Author: DeTeam Date: Tue Jan 22 19:11:52 2013 +0400 * Make tests pass * Readme updated * RuntimeError when file is empty * Hoe in dev deps * Finish with bundler * Add a Gemfile also see: https://github.com/zdavatz/spreadsheet/pull/24 ### 0.7.6 / 15.01.2013 Author: Kenichi Kamiya Date: Tue Jan 15 15:52:58 2013 +0900 * Remove warnings "method redefined; discarding old default_format" * Remove warnings "`*' interpreted as argument prefix" * Remove warnings "instance variable @{ivar} not initialized" * Remove warnings "assigned but unused variable" also see: https://github.com/zdavatz/spreadsheet/pull/21 ### 0.7.5 / 06.12.2012 * Add error tolerant values for Iconv when writing spreadsheet * by andrea@spaghetticode.it ### 0.7.4 / 06.10.2012 * Adds Spreadsheet::Excel::Row#to_a method to properly decode Date and DateTime data. * patches by https://github.com/mdgreenfield/spreadsheet ### 0.7.3 / 26.06.2012 * Fix Format borders * see https://github.com/zdavatz/spreadsheet/pull/6 for full details. * patches by uraki66@gmail.com ### 0.7.2 / 14.06.2012 * many changes by Mina Naguib * see git log for full details ### 0.7.1 / 08.05.2012 * Author: Artem Ignatiev * remove require and rake altogether * gem build and rake gem both work fine without those requires, * and requiring 'rake' broke bundler * add rake as development dependency * Somehow it broken rake on my other project ### 0.7.0 / 07.05.2012 * Author: Artem Ignatiev * use both ruby 1.8 and 1.9 compatible way of getting character code when hashing * Fix syntax for ruby-1.9 * return gemspec so that bundler can find it When bundler loads gemspec, it evaluates it, and if the return value is not a gem specification built, refuses to load the gem. * Testing worksheet protection ### 0.6.9 / 28.04.2012 * Yield is more simple here too. * No need to capture the block in Spreadsheet.open * Rather than extending a core class, let's just use #rcompact from a helper module ### 0.6.8 / 20.01.2012 * adds the fix to allow the writing of empty rows, by ClemensP. * Test also by ClemensP. ### 0.6.7 / 18.01.2012 * http://dev.ywesee.com/wiki.php/Gem/Spreadsheet points point 2. * Tests by Michal * Patches by Timon ### 0.6.6 / 18.01.2012 * http://dev.ywesee.com/wiki.php/Gem/Spreadsheet points 8 and 9. * Fixes byjsaak@napalm.hu * Patches by Vitaly Klimov ### 0.6.5.9 / 7.9.2011 * Fixed a frozen string bug thanks to dblock (Daniel Doubrovkine), * dblock@dblock.org * https://github.com/dblock/spreadsheet/commit/164dcfbb24097728f1a7453702c270107e725b7c ### 0.6.5.8 / 30.8.2011 * This patch is about adding a sheet_count method to workbook so that it returns * the total no of worksheets for easy access. Please check. By * tamizhgeek@gmail.com * https://gist.github.com/1180625 ### 0.6.5.7 / 20.7.2011 * Fixed the bug introduced by Roel van der Hoorn and updated the test cases. * https://github.com/vanderhoorn/spreadsheet/commit/c79ab14dcf40dee1d6d5ad2b174f3fe31414ca28 ### 0.6.5.6 / 20.7.2011 * Added a fix from Roel van der Hoorn to sanitize_worksheets if 'sheets' is empty. * https://github.com/vanderhoorn/spreadsheet/commit/c109f2ac5486f9a38a6d93267daf560ab4b9473e ### 0.6.5.5 / 24.6.2011 * updated the color code for orange to 0x0034 => :orange, thanks to the hint of Jonty * https://gist.github.com/1044700 ### 0.6.5.4 / 18.4.2011 * Updated worksheet.rb according to the Patch of Björn Andersson. * https://gist.github.com/925007#file_test.patch * http://url.ba/09p9 ### 0.6.5.3 / 23.3.2011 * Updated Txt lib/spreadsheet/excel/writer/biff8.rb with a Patch from Alexandre Bini * See this for full detail: http://url.ba/6r1z ### 0.6.5.2 / 14.2.2011 * Updated test/integration.rb to work with Ruby ruby 1.9.2p136 (2010-12-25 revision 30365) [i686-linux] * Thanks for the hint tomiacannondale@gmail.com ### 0.6.5.1 / 17.1.2011 * One enhancement thanks to Qiong Peng, Moo Yu, and Thierry Thelliez * http://dev.ywesee.com/wiki.php/Gem/Spreadsheet ### 0.6.5 / 07.12.2010 * 2 Enhancements courtesy to ISS AG. * Outlining (Grouping) of lines and columns is now possible. The outlining maximum is 8. This means you can do 8 subgroups in a group. * Hiding and Unhiding of lines and columns is now possible. * Both of above two points is now possible by creating a new Excel File from scratch or editing an existing XLS and adding groups or hiding lines to it. ### 0.6.4.1 / 2009-09-17 * 3 Bugfixes * Fixes the issue reported by Thomas Preymesser and tracked down most of the way by Hugh McGowan in http://rubyforge.org/tracker/index.php?func=detail&aid=26647&group_id=678&atid=2677 where reading the value of the first occurrence of a shared formula failed. * Fixes the issue reported by Anonymous in http://rubyforge.org/tracker/index.php?func=detail&aid=26546&group_id=678&atid=2677 where InvalidDate was raised for some Dates. * Fixes the issue reported by John Lee in http://rubyforge.org/tracker/index.php?func=detail&aid=27110&group_id=678&atid=2677 which is probably a duplicate of the Bug previously reported by Kadvin XJ in http://rubyforge.org/tracker/index.php?func=detail&aid=26182&group_id=678&atid=2677 where unchanged rows were marked as changed in the Excel-Writer while the File was being written, triggering an Error. * 1 minor enhancement * Detect row offsets from Cell data if Row-Ops are missing This fixes a bug reported by Alexander Logvinov in http://rubyforge.org/tracker/index.php?func=detail&aid=26513&group_id=678&atid=2677 ### 0.6.4 / 2009-07-03 * 5 Bugfixes * Fixes the issue reported by Harley Mackenzie in http://rubyforge.org/tracker/index.php?func=detail&aid=24119&group_id=678&atid=2677 where in some edge-cases numbers were stored incorrectly * Fixes the issue reported and fixed by someone23 in http://rubyforge.org/tracker/index.php?func=detail&aid=25732&group_id=678&atid=2677 where using Row-updater methods with blocks caused LocalJumpErrors * Fixes the issue reported and fixed by Corey Burrows in http://rubyforge.org/tracker/index.php?func=detail&aid=25784&group_id=678&atid=2677 where "Setting the height of a row, either in Excel directly, or via the Spreadsheet::Row#height= method results in a row that Excel displays with the maximum row height (409)." * Fixes the issue reported by Don Park in http://rubyforge.org/tracker/index.php?func=detail&aid=25968&group_id=678&atid=2677 where some Workbooks could not be parsed due to the OLE-entry being all uppercase * Fixes the issue reported by Iwan Buetti in http://rubyforge.org/tracker/index.php?func=detail&aid=24414&group_id=678&atid=2677 where parsing some Workbooks failed with an Invalid date error. * 1 major enhancement * Spreadsheet now runs on Ruby 1.9 ### 0.6.3.1 / 2009-02-13 * 3 Bugfixes * Only selects the First Worksheet by default This deals with an issue reported by Biörn Andersson in http://rubyforge.org/tracker/?func=detail&atid=2677&aid=23736&group_id=678 where data-edits in OpenOffice were propagated through all selected sheets. * Honors Row, Column, Worksheet and Workbook-formats and thus fixes a Bug introduced in http://scm.ywesee.com/?p=spreadsheet;a=commit;h=52755ad76fdda151564b689107ca2fbb80af3b78 and reported in http://rubyforge.org/tracker/index.php?func=detail&aid=23875&group_id=678&atid=2678 and by Joachim Schneider in http://rubyforge.org/forum/forum.php?thread_id=31056&forum_id=2920 * Fixes a bug reported by Alexander Skwar in http://rubyforge.org/forum/forum.php?thread_id=31403&forum_id=2920 where the user-defined formatting of Dates and Times was overwritten with a default format, and other issues connected with writing Dates and Times into Spreadsheets. * 1 minor enhancements * Spreadsheet shold now be completely warning-free, as requested by Eric Peterson in http://rubyforge.org/forum/forum.php?thread_id=31346&forum_id=2920 ### 0.6.3 / 2009-01-14 * 1 Bugfix * Fixes the issue reported by Corey Martella in http://rubyforge.org/forum/message.php?msg_id=63651 as well as other issues engendered by the decision to always shorten Rows to the last non-nil value. * 2 minor enhancements * Added bin/xlsopcodes, a tool for examining Excel files * Documents created by Spreadsheet can now be Printed in Excel and Excel-Viewer. This issue was reported by Spencer Turner in http://rubyforge.org/tracker/index.php?func=detail&aid=23287&group_id=678&atid=2677 ### 0.6.2.1 / 2008-12-18 * 1 Bugfix * Using Spreadsheet together with 'jcode' could lead to broken Excel-Files Thanks to Eugene Mikhailov for tracking this one down in: http://rubyforge.org/tracker/index.php?func=detail&aid=23085&group_id=678&atid=2677 ### 0.6.2 / 2008-12-11 * 14 Bugfixes * Fixed a bug where #! methods did not trigger a call to #row_updated * Corrected the Row-Format in both Reader and Writer (was Biff5 for some reason) * Populates Row-instances with @default_format, @height, @outline_level and @hidden attributes * Fixed a Bug where Workbooks deriving from a Template-Workbook without SST could not be saved Reported in http://rubyforge.org/tracker/index.php?func=detail&aid=22863&group_id=678&atid=2678 * Improved handling of Numeric Values (writes a RK-Entry for a Float only if it can be encoded with 4 leading zeroes, and a Number-Entry for an Integer only if it cannot be encoded as an RK) * Fixes a bug where changes to a Row were ignored if they were outside of an existing Row-Block. * Fixes a bug where MULRK-Entries sometimes only contained a single RK * Fixes a bug where formatting was ignored if it was applied to empty Rows Reported by Zomba Lumix in http://rubyforge.org/forum/message.php?msg_id=61985 * Fixes a bug where modifying a Row in a loaded Workbook could lead to Rows with smaller indices being set to nil. Reported by Ivan Samsonov in http://rubyforge.org/forum/message.php?msg_id=62816 * Deals with rounding-problems when calculating Time Reported by Bughunter extraordinaire Bjørn Hjelle * Correct splitting of wide characters in SST Reported by Michel Ziegler and by Eugene Mikhailov in http://rubyforge.org/tracker/index.php?func=detail&aid=23085&group_id=678&atid=2677 * Fix an off-by-one error in write_mulrk that caused Excel to complain that 'Data may be lost', reported by Emma in http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/321979 and by Chris Lowis in http://rubyforge.org/tracker/index.php?func=detail&aid=22892&group_id=678&atid=2677 * Read formats correctly in read_mulrk Reported by Ivan Samsonov Fixes that part of http://rubyforge.org/forum/message.php?msg_id=62821 which is a bug. Does nothing for the disappearance of Rich-Text formatting, which will not be addressed until 0.7.0 * Fixes a (benign?) bug, where adding text to a template-file resulted in a duplicate extsst-record. * 2 minor enhancements * Improved recognition of Time-Formats * Improvement to Robustness: allow Spreadsheet::Workbook.new Takes care of http://rubyforge.org/forum/message.php?msg_id=62941 Reported by David Chamberlain ### 0.6.1.9 / 2008-11-07 * 1 Bugfix * Fixes a precision-issue in Excel::Row#datetime: Excel records Time-Values with more significant bits (but not necessarily more precise) than DateTime can handle. (Thanks to Bjørn Hjelle for the Bugreport) * 1 minor enhancement * Added support for appending Worksheets to a Workbook (Thanks to Mohammed Rabbani for the report) ### 0.6.1.8 / 2008-10-31 * 1 Bugfix * Fixes a bug where out-of-sequence reading of multiple Worksheets could lead to data from the wrong Sheet being returned. (Thanks to Bugreporter extraordinaire Bjørn Hjelle) ### 0.6.1.7 / 2008-10-30 * 1 Bugfix * Fixes a bug where all Formulas were ignored. (Thanks to Bjørn Hjelle for the report) * 1 minor enhancement * Allow the substitution of an IO object with a StringIO. (Thanks to luxor for the report) ### 0.6.1.6 / 2008-10-28 * 2 Bugfixes * Fixed encoding and decoding of BigNums, negative and other large Numbers http://rubyforge.org/tracker/index.php?func=detail&aid=22581&group_id=678&atid=2677 * Fix a bug where modifications to default columns weren't stored http://rubyforge.org/forum/message.php?msg_id=61567 * 1 minor enhancement * Row#enriched_data won't return a Bogus-Date if the data isn't a Numeric value (Thanks to Bjørn Hjelle for the report) ### 0.6.1.5 / 2008-10-24 * 2 Bugfixes * Removed obsolete code which triggered Iconv::InvalidEncoding on Systems with non-gnu Iconv: http://rubyforge.org/tracker/index.php?func=detail&aid=22541&group_id=678&atid=2677 * Handle empty Worksheets (Thanks to Charles Lowe for the Patches) ### 0.6.1.4 / 2008-10-23 * 1 Bugfix * Biff8#wide now works properly even if $KCODE=='UTF-8' (Thanks to Bjørn Hjelle for the Bugreport) * 1 minor enhancement * Read/Write functionality for Links (only URLs can be written as of now) ### 0.6.1.3 / 2008-10-21 * 2 Bugfixes * Renamed UTF8 to UTF-8 to support freebsd (Thanks to Jacob Atzen for the Patch) * Fixes a Bug where only the first Rowblock was read correctly if there were no DBCELL records terminating the Rowblocks. (Thanks to Bjørn Hjelle for the Bugreport) ### 0.6.1.2 / 2008-10-20 * 2 Bugfixes * Corrected the Font-Encoding values in Excel::Internals (Thanks to Bjørn Hjelle for the Bugreport) * Spreadsheet now skips Richtext-Formatting runs and Asian Phonetic Settings when reading the SST, fixing a problem where the presence of Richtext could lead to an incomplete SST. ### 0.6.1.1 / 2008-10-20 * 1 Bugfix * Corrected the Manifest - included column.rb ### 0.6.1 / 2008-10-17 * 3 minor enhancements * Adds Column formatting and Worksheet#format_column * Reads and writes correct Fonts (Font-indices > 3 appear to be 1-based) * Reads xf data ### 0.6.0 / 2008-10-13 * 1 major enhancement * Initial upload of the shiny new Spreadsheet Gem after three weeks of grueling labor in the dark binary mines of Little-Endian Biff and long hours spent polishing the surfaces of documentation. spreadsheet-0.9.0/LICENSE.txt0000644000004100000410000007724612216006760015722 0ustar www-datawww-data GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. spreadsheet-0.9.0/.gemtest0000644000004100000410000000000012216006760015516 0ustar www-datawww-dataspreadsheet-0.9.0/spreadsheet.gemspec0000644000004100000410000000136512216006760017740 0ustar www-datawww-dataspec = Gem::Specification.new do |s| s.name = "spreadsheet" s.version = "0.8.9" s.summary = "The Spreadsheet Library is designed to read and write Spreadsheet Documents" s.description = "As of version 0.6.0, only Microsoft Excel compatible spreadsheets are supported" s.author = "Masaomi Hatakeyama, Zeno R.R. Davatz" s.email = "mhatakeyama@ywesee.com, zdavatz@ywesee.com" s.platform = Gem::Platform::RUBY s.license = "GPLv3" s.files = Dir.glob("{bin,lib,test}/**/*") + Dir.glob("*.txt") s.test_file = "test/suite.rb" s.executables << "xlsopcodes" s.add_dependency "ruby-ole" s.add_development_dependency "hoe" s.homepage = "https://github.com/zdavatz/spreadsheet/" end spreadsheet-0.9.0/bin/0000755000004100000410000000000012216006760014627 5ustar www-datawww-dataspreadsheet-0.9.0/bin/xlsopcodes0000755000004100000410000000057712216006760016751 0ustar www-datawww-data#!/usr/bin/env ruby # # This file was generated by Bundler. # # The application 'xlsopcodes' is installed as part of a gem, and # this file is here to facilitate running it. # require 'pathname' ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath) require 'rubygems' require 'bundler/setup' load Gem.bin_path('spreadsheet', 'xlsopcodes') spreadsheet-0.9.0/metadata.yml0000644000004100000410000001146612216006760016372 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: spreadsheet version: !ruby/object:Gem::Version hash: 59 prerelease: segments: - 0 - 9 - 0 version: 0.9.0 platform: ruby authors: - Masaomi Hatakeyama, Zeno R.R. Davatz autorequire: bindir: bin cert_chain: [] date: 2013-09-16 00:00:00 Z dependencies: - !ruby/object:Gem::Dependency type: :runtime version_requirements: &id001 !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 15 segments: - 1 - 0 version: "1.0" requirement: *id001 prerelease: false name: ruby-ole - !ruby/object:Gem::Dependency type: :development version_requirements: &id002 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 27 segments: - 4 - 0 version: "4.0" requirement: *id002 prerelease: false name: rdoc - !ruby/object:Gem::Dependency type: :development version_requirements: &id003 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 11 segments: - 3 - 6 version: "3.6" requirement: *id003 prerelease: false name: hoe description: |- The Spreadsheet Library is designed to read and write Spreadsheet Documents. As of version 0.6.0, only Microsoft Excel compatible spreadsheets are supported. Spreadsheet is a combination/complete rewrite of the Spreadsheet::Excel Library by Daniel J. Berger and the ParseExcel Library by Hannes Wyss. Spreadsheet can read, write and modify Spreadsheet Documents. email: - mhatakeyama@ywesee.com, zdavatz@ywesee.com executables: - xlsopcodes extensions: [] extra_rdoc_files: - GUIDE.md - History.md - LICENSE.txt - Manifest.txt - README.md files: - .gitignore - .travis.yml - GUIDE.md - Gemfile - Gemfile.lock - History.md - LICENSE.txt - Manifest.txt - README.md - Rakefile - bin/xlsopcodes - lib/parseexcel.rb - lib/parseexcel/parseexcel.rb - lib/parseexcel/parser.rb - lib/spreadsheet.rb - lib/spreadsheet/column.rb - lib/spreadsheet/compatibility.rb - lib/spreadsheet/datatypes.rb - lib/spreadsheet/encodings.rb - lib/spreadsheet/excel.rb - lib/spreadsheet/excel/error.rb - lib/spreadsheet/excel/internals.rb - lib/spreadsheet/excel/internals/biff5.rb - lib/spreadsheet/excel/internals/biff8.rb - lib/spreadsheet/excel/offset.rb - lib/spreadsheet/excel/password_hash.rb - lib/spreadsheet/excel/reader.rb - lib/spreadsheet/excel/reader/biff5.rb - lib/spreadsheet/excel/reader/biff8.rb - lib/spreadsheet/excel/rgb.rb - lib/spreadsheet/excel/row.rb - lib/spreadsheet/excel/sst_entry.rb - lib/spreadsheet/excel/workbook.rb - lib/spreadsheet/excel/worksheet.rb - lib/spreadsheet/excel/writer.rb - lib/spreadsheet/excel/writer/biff8.rb - lib/spreadsheet/excel/writer/format.rb - lib/spreadsheet/excel/writer/n_worksheet.rb - lib/spreadsheet/excel/writer/workbook.rb - lib/spreadsheet/excel/writer/worksheet.rb - lib/spreadsheet/font.rb - lib/spreadsheet/format.rb - lib/spreadsheet/formula.rb - lib/spreadsheet/helpers.rb - lib/spreadsheet/link.rb - lib/spreadsheet/row.rb - lib/spreadsheet/workbook.rb - lib/spreadsheet/worksheet.rb - lib/spreadsheet/writer.rb - spreadsheet.gemspec - test/data/test_adding_data_to_existing_file.xls - test/data/test_borders.xls - test/data/test_changes.xls - test/data/test_copy.xls - test/data/test_datetime.xls - test/data/test_empty.xls - test/data/test_formula.xls - test/data/test_long_sst_record.xls - test/data/test_margin.xls - test/data/test_merged_and_protected.xls - test/data/test_merged_cells.xls - test/data/test_missing_row.xls - test/data/test_pagesetup.xls - test/data/test_version_excel5.xls - test/data/test_version_excel95.xls - test/data/test_version_excel97.xls - test/data/test_version_excel97_2010.xls - test/excel/reader.rb - test/excel/row.rb - test/excel/writer/workbook.rb - test/excel/writer/worksheet.rb - test/font.rb - test/format.rb - test/integration.rb - test/row.rb - test/suite.rb - test/workbook.rb - test/workbook_protection.rb - test/worksheet.rb - .gemtest homepage: https://github.com/zdavatz/spreadsheet licenses: [] post_install_message: rdoc_options: - --main - README.md require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" requirements: [] rubyforge_project: spreadsheet rubygems_version: 1.8.25 signing_key: specification_version: 3 summary: The Spreadsheet Library is designed to read and write Spreadsheet Documents test_files: [] spreadsheet-0.9.0/Gemfile0000644000004100000410000000022512216006760015351 0ustar www-datawww-datasource "https://rubygems.org" gem 'ruby-ole' gem 'spreadsheet' group :development do gem 'ruby-ole' gem 'hoe', '>= 3.4' gem 'spreadsheet' end spreadsheet-0.9.0/Manifest.txt0000644000004100000410000000410312216006760016364 0ustar www-datawww-data.gitignore .travis.yml GUIDE.md Gemfile Gemfile.lock History.md LICENSE.txt Manifest.txt README.md Rakefile bin/xlsopcodes lib/parseexcel.rb lib/parseexcel/parseexcel.rb lib/parseexcel/parser.rb lib/spreadsheet.rb lib/spreadsheet/column.rb lib/spreadsheet/compatibility.rb lib/spreadsheet/datatypes.rb lib/spreadsheet/encodings.rb lib/spreadsheet/excel.rb lib/spreadsheet/excel/error.rb lib/spreadsheet/excel/internals.rb lib/spreadsheet/excel/internals/biff5.rb lib/spreadsheet/excel/internals/biff8.rb lib/spreadsheet/excel/offset.rb lib/spreadsheet/excel/password_hash.rb lib/spreadsheet/excel/reader.rb lib/spreadsheet/excel/reader/biff5.rb lib/spreadsheet/excel/reader/biff8.rb lib/spreadsheet/excel/rgb.rb lib/spreadsheet/excel/row.rb lib/spreadsheet/excel/sst_entry.rb lib/spreadsheet/excel/workbook.rb lib/spreadsheet/excel/worksheet.rb lib/spreadsheet/excel/writer.rb lib/spreadsheet/excel/writer/biff8.rb lib/spreadsheet/excel/writer/format.rb lib/spreadsheet/excel/writer/n_worksheet.rb lib/spreadsheet/excel/writer/workbook.rb lib/spreadsheet/excel/writer/worksheet.rb lib/spreadsheet/font.rb lib/spreadsheet/format.rb lib/spreadsheet/formula.rb lib/spreadsheet/helpers.rb lib/spreadsheet/link.rb lib/spreadsheet/row.rb lib/spreadsheet/workbook.rb lib/spreadsheet/worksheet.rb lib/spreadsheet/writer.rb spreadsheet.gemspec test/data/test_adding_data_to_existing_file.xls test/data/test_borders.xls test/data/test_changes.xls test/data/test_copy.xls test/data/test_datetime.xls test/data/test_empty.xls test/data/test_formula.xls test/data/test_long_sst_record.xls test/data/test_margin.xls test/data/test_merged_and_protected.xls test/data/test_merged_cells.xls test/data/test_missing_row.xls test/data/test_pagesetup.xls test/data/test_version_excel5.xls test/data/test_version_excel95.xls test/data/test_version_excel97.xls test/data/test_version_excel97_2010.xls test/excel/reader.rb test/excel/row.rb test/excel/writer/workbook.rb test/excel/writer/worksheet.rb test/font.rb test/format.rb test/integration.rb test/row.rb test/suite.rb test/workbook.rb test/workbook_protection.rb test/worksheet.rb spreadsheet-0.9.0/.gitignore0000644000004100000410000000001312216006760016041 0ustar www-datawww-datatags *.swp spreadsheet-0.9.0/lib/0000755000004100000410000000000012216006760014625 5ustar www-datawww-dataspreadsheet-0.9.0/lib/spreadsheet/0000755000004100000410000000000012216006760017134 5ustar www-datawww-dataspreadsheet-0.9.0/lib/spreadsheet/excel/0000755000004100000410000000000012216006760020234 5ustar www-datawww-dataspreadsheet-0.9.0/lib/spreadsheet/excel/rgb.rb0000644000004100000410000001000212216006760021324 0ustar www-datawww-data# A quick and dirty class for converting color palette values to RGB values. # The values below have the form 0xRRGGBB, where RR is the red level, GG the # green level, and BB the blue level. Each level is a value from 0 to 255, # just as one would expect in HTML markup. # Future directions may include: # - support for mapping RGB values to "best fit" palette values # # by Dan Caugherty https://github.com/dancaugherty/spreadsheet/compare/master...rgb module Spreadsheet module Excel class Rgb attr_accessor :r, :g, :b @@RGB_MAP = { :xls_color_0 => 0x000000, :xls_color_1 => 0xffffff, :xls_color_2 => 0xff0000, :xls_color_3 => 0x00ff00, :xls_color_4 => 0x0000ff, :xls_color_5 => 0xffff00, :xls_color_6 => 0xff00ff, :xls_color_7 => 0x00ffff, :xls_color_8 => 0x800000, :xls_color_9 => 0x008000, :xls_color_10 => 0x008000, :xls_color_11 => 0x000080, :xls_color_12 => 0x808080, :xls_color_13 => 0x008080, :xls_color_14 => 0xc0c0c0, :xls_color_15 => 0x808080, :xls_color_16 => 0x9999ff, :xls_color_17 => 0x993366, :xls_color_18 => 0xffffcc, :xls_color_19 => 0xccffff, :xls_color_20 => 0x660066, :xls_color_21 => 0xff8080, :xls_color_22 => 0x0066cc, :xls_color_23 => 0xccccff, :xls_color_24 => 0x000080, :xls_color_25 => 0xff00ff, :xls_color_26 => 0xffff00, :xls_color_27 => 0x00ffff, :xls_color_28 => 0x800080, :xls_color_29 => 0x800000, :xls_color_30 => 0x008080, :xls_color_31 => 0x0000ff, :xls_color_32 => 0x00ccff, :xls_color_33 => 0xccffff, :xls_color_34 => 0xccffcc, :xls_color_35 => 0xffff99, :xls_color_36 => 0x99ccff, :xls_color_37 => 0xff99cc, :xls_color_38 => 0xcc99ff, :xls_color_39 => 0xffcc99, :xls_color_40 => 0x3366ff, :xls_color_41 => 0x33cccc, :xls_color_42 => 0x99cc00, :xls_color_43 => 0xffcc00, :xls_color_44 => 0xff9900, :xls_color_45 => 0xff6600, :xls_color_46 => 0x666699, :xls_color_47 => 0x969696, :xls_color_48 => 0x003366, :xls_color_49 => 0x339966, :xls_color_50 => 0x003300, :xls_color_51 => 0x333300, :xls_color_52 => 0x993300, :xls_color_53 => 0x993366, :xls_color_54 => 0x333399, :xls_color_55 => 0x333333, :builtin_black => 0x000000, :builtin_white => 0xffffff, :builtin_red => 0xff0000, :builtin_green => 0x00ff00, :builtin_blue => 0x0000ff, :builtin_yellow => 0xffff00, :builtin_magenta => 0xff00ff, :builtin_cyan => 0x00ffff, :aqua => 0x00ffff, :black => 0x000000, :blue => 0x0000ff, :cyan => 0x00ffff, :brown => 0x800000, :fuchsia => 0xff00ff, :gray => 0x808080, :grey => 0x808080, :green => 0x008000, :lime => 0x00ff00, :magenta => 0xff00ff, :navy => 0x000080, :orange => 0xff9900, :purple => 0x800080, :red => 0xff0000, :silver => 0xc0c0c0, :white => 0xffffff, :yellow => 0xffff00 } def self.to_rgb color_symbol col = @@RGB_MAP[color_symbol] return Rgb.new(col >> 16, (col & 0xff00) >> 8, col & 0xff) if col nil end def initialize(r,g,b) @r = r & 0xff @g = g & 0xff @b = b & 0xff end def to_i (r * (256 * 256)) + (g * 256) + b end def as_hex to_i.to_s(16) end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/reader/0000755000004100000410000000000012216006760021476 5ustar www-datawww-dataspreadsheet-0.9.0/lib/spreadsheet/excel/reader/biff8.rb0000644000004100000410000002024012216006760023017 0ustar www-datawww-datamodule Spreadsheet module Excel class Reader ## # This Module collects reader methods such as read_string that are specific to # Biff8. This Module is likely to be expanded as Support for older Versions # of Excel grows and methods get moved here for disambiguation. module Biff8 include Spreadsheet::Excel::Internals ## # When a String is too long for one Opcode, it is continued in a Continue # Opcode. Excel may reconsider compressing the remainder of the string. # This method appends the available remainder (decompressed if necessary) to # the incomplete string. def continue_string work, incomplete_string=@incomplete_string opts, _ = work.unpack 'C' wide = opts & 1 head, chars = incomplete_string owing = chars - head.size / 2 size = owing * (wide + 1) string = work[1, size] if wide == 0 string = wide string end head << string if head.size >= chars * 2 @incomplete_string = nil end size + 1 end ## # When a String is too long for one Opcode, it is continued in a Continue # Opcode. Excel may reconsider compressing the remainder of the string. # This method only evaluates the header and registers the address of the # continuation with the previous SstEntry. def continue_string_header work, oppos opts, _ = work.unpack 'C' wide = opts & 1 owing = @incomplete_sst.continued_chars size = [work.size, owing * (1 + wide) + 1].min chars = (size - 1) / (1 + wide) skip = size @incomplete_sst.continue oppos + OPCODE_SIZE, size, chars unless @incomplete_sst.continued? @workbook.add_shared_string @incomplete_sst skip += @incomplete_skip @incomplete_sst = nil @incomplete_skip = nil end skip end ## # Read more data into the Shared String Table. (see also: #read_sst) # This method only evaluates the header, the actual work is done in #_read_sst def continue_sst work, oppos, len pos = 0 if @incomplete_sst pos = continue_string_header work, oppos elsif !@incomplete_skip.nil? pos = @incomplete_skip @incomplete_skip = nil end @sst_offset[1] += len _read_sst work, oppos, pos end def postread_workbook # :nodoc: super @incomplete_string, @sst_size, @sst_offset, @incomplete_sst = nil, @incomplete_skip = nil end ## # Store the offset of extsst, so we can write a new extsst when the # sst has changed def read_extsst work, pos, len @workbook.offsets.store :extsst, [pos, len] end ## # Read the Shared String Table present in all Biff8 Files. # This method only evaluates the header, the actual work is done in #_read_sst def read_sst work, pos, len # Offset Size Contents # 0 4 Total number of strings in the workbook (see below) # 4 4 Number of following strings (nm) # 8 var. List of nm Unicode strings, 16-bit string length (➜ 3.4) _, @sst_size = work.unpack 'V2' @sst_offset = [pos, len] @workbook.offsets.store :sst, @sst_offset _read_sst work, pos, 8 end ## # Read a string from the Spreadsheet, such as a Worksheet- or Font-Name, or a # Number-Format. See also #read_string_header and #read_string_body def read_string work, count_length=1 # Offset Size Contents # 0 1 or 2 Length of the string (character count, ln) # 1 or 2 1 Option flags: # Bit Mask Contents # 0 0x01 Character compression (ccompr): # 0 = Compressed (8-bit characters) # 1 = Uncompressed (16-bit characters) # 2 0x04 Asian phonetic settings (phonetic): # 0 = Does not contain Asian phonetic settings # 1 = Contains Asian phonetic settings # 3 0x08 Rich-Text settings (richtext): # 0 = Does not contain Rich-Text settings # 1 = Contains Rich-Text settings # [2 or 3] 2 (optional, only if richtext=1) # Number of Rich-Text formatting runs (rt) # [var.] 4 (optional, only if phonetic=1) # Size of Asian phonetic settings block (in bytes, sz) # var. ln Character array (8-bit characters # or 2∙ln or 16-bit characters, dependent on ccompr) # [var.] 4∙rt (optional, only if richtext=1) # List of rt formatting runs (➜ 3.2) # [var.] sz (optional, only if phonetic=1) # Asian Phonetic Settings Block (➜ 3.4.2) chars, offset, wide, _, _, available, owing, _ = read_string_header work, count_length string, _ = read_string_body work, offset, available, wide > 0 if owing > 0 @incomplete_string = [string, chars] end string end ## # Read the body of a string. Returns the String (decompressed if necessary) and # the available data (unchanged). def read_string_body work, offset, available, wide data = work[offset, available] string = wide ? data : wide(data) [string, data] end ## # Read the header of a string. Returns the following information in an Array: # * The total number of characters in the string # * The offset of the actual string data (= the length of this header in bytes) # * Whether or not the string was compressed (0/1) # * Whether or not the string contains asian phonetic settings (0/1) # * Whether or not the string contains richtext formatting (0/1) # * The number of bytes containing characters in this chunk of data # * The number of characters missing from this chunk of data and expected to # follow in a Continue Opcode def read_string_header work, count_length=1, offset=0 fmt = count_length == 1 ? 'C2' : 'vC' chars, opts = work[offset, 1 + count_length].unpack fmt wide = opts & 1 phonetic = (opts >> 2) & 1 richtext = (opts >> 3) & 1 size = chars * (wide + 1) skip = 0 if richtext > 0 runs, = work[offset + 1 + count_length, 2].unpack 'v' skip = 4 * runs end if phonetic > 0 psize, = work[offset + 1 + count_length + richtext * 2, 4].unpack 'V' skip += psize end flagsize = 1 + count_length + richtext * 2 + phonetic * 4 avbl = [work.size - offset, flagsize + size].min have_chrs = (avbl - flagsize) / (1 + wide) 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 data = '' string.each_byte do |byte| data << byte.chr << 0.chr end data end private ## # Read the Shared String Table present in all Biff8 Files. def _read_sst work, oppos, pos worksize = work.size while @workbook.sst_size < @sst_size && pos < worksize do sst = SstEntry.new :offset => oppos + OPCODE_SIZE + pos, :ole => @data, :reader => self sst.chars, sst.flags, wide, sst.phonetic, sst.richtext, sst.available, sst.continued_chars, skip = read_string_header work, 2, pos sst.wide = wide > 0 if sst.continued? @incomplete_sst = sst @incomplete_skip = skip pos += sst.available else @workbook.add_shared_string sst pos += sst.available + skip if pos > worksize @incomplete_skip = pos - worksize end end end end end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/reader/biff5.rb0000644000004100000410000000213712216006760023021 0ustar www-datawww-datamodule Spreadsheet module Excel class Reader ## # This Module collects reader methods such as read_string that are specific to # Biff5. This Module is likely to be expanded as Support for older Versions # of Excel grows. module Biff5 ## # Read a String of 8-bit Characters def read_string work, count_length=1 # Offset Size Contents # 0 1 or 2 Length of the string (character count, ln) # 1 or 2 ln Character array (8-bit characters) fmt = count_length == 1 ? 'C' : 'v' 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 end spreadsheet-0.9.0/lib/spreadsheet/excel/internals.rb0000644000004100000410000004076512216006760022574 0ustar www-datawww-datarequire 'date' module Spreadsheet module Excel ## # Binary Formats and other configurations internal to Excel. This Module is # likely to shrink as Support for older Versions of Excel grows and more Binary # formats are moved away from here for disambiguation. # If you need to work with constants defined in this module and are confused by # names like SEDOC_ROLOC, try reading them backwards. (The reason for this weird # naming convention is that according to my ri, Ruby 1.9 renames Hash#index to # Hash#key without backward compatibility. Since I did not want to pepper my code # with RUBY_VERSION-checks, I settled on this strategy to make the transition to # Ruby 1.9 as simple as possible. module Internals EIGHT_BYTE_DOUBLE = [0.1].pack('E').size == 8 ? 'E' : 'e' CODEPAGES = { 367 => "ASCII", 437 => "IBM437", #(US) 720 => "IBM720", #(OEM Arabic) 737 => "IBM737", #(Greek) 775 => "IBM775", #(Baltic) 850 => "IBM850", #(Latin I) 852 => "IBM852", #(Latin II (Central European)) 855 => "IBM855", #(Cyrillic) 857 => "IBM857", #(Turkish) 858 => "IBM858", #(Multilingual Latin I with Euro) 860 => "IBM860", #(Portuguese) 861 => "IBM861", #(Icelandic) 862 => "IBM862", #(Hebrew) 863 => "IBM863", #(Canadian (French)) 864 => "IBM864", #(Arabic) 865 => "IBM865", #(Nordic) 866 => "IBM866", #(Cyrillic (Russian)) 869 => "IBM869", #(Greek (Modern)) 874 => "WINDOWS-874", #(Thai) 932 => "WINDOWS-932", #(Japanese Shift-JIS) 936 => "WINDOWS-936", #(Chinese Simplified GBK) 949 => "WINDOWS-949", #(Korean (Wansung)) 950 => "WINDOWS-950", #(Chinese Traditional BIG5) 1200 => "UTF-16LE", #(BIFF8) 1250 => "WINDOWS-1250", #(Latin II) (Central European) 1251 => "WINDOWS-1251", #(Cyrillic) 1252 => "WINDOWS-1252", #(Latin I) (BIFF4-BIFF7) 1253 => "WINDOWS-1253", #(Greek) 1254 => "WINDOWS-1254", #(Turkish) 1255 => "WINDOWS-1255", #(Hebrew) 1256 => "WINDOWS-1256", #(Arabic) 1257 => "WINDOWS-1257", #(Baltic) 1258 => "WINDOWS-1258", #(Vietnamese) 1361 => "WINDOWS-1361", #(Korean (Johab)) 10000 => "MACROMAN", 32768 => "MACROMAN", 32769 => "WINDOWS-1252", #(Latin I) (BIFF2-BIFF3) } SEGAPEDOC = CODEPAGES.invert # color_codes according to http://support.softartisans.com/kbview_1205.aspx # synonyms are in comments when reverse lookup COLOR_CODES = { 0x0000 => :builtin_black, 0x0001 => :builtin_white, 0x0002 => :builtin_red, 0x0003 => :builtin_green, 0x0004 => :builtin_blue, 0x0005 => :builtin_yellow, 0x0006 => :builtin_magenta, 0x0007 => :builtin_cyan, 0x0008 => :black, #xls_color_0 0x0009 => :white, #xls_color_1 0x000a => :red, #xls_color_2 0x000b => :lime, #xls_color_3 0x000c => :blue, #xls_color_4 0x000d => :yellow, #xls_color_5 0x000e => :magenta, #xls_color_6, fuchsia 0x000f => :cyan, #xls_color_7, aqua 0x0010 => :brown, #xls_color_8 0x0011 => :green, #xls_color_9 0x0012 => :navy, #xls_color_10 0x0013 => :xls_color_11, 0x0014 => :xls_color_12, 0x0015 => :xls_color_13, 0x0016 => :silver, #xls_color_14 0x0017 => :gray, #xls_color_15, grey 0x0018 => :xls_color_16, 0x0019 => :xls_color_17, 0x001a => :xls_color_18, 0x001b => :xls_color_19, 0x001c => :xls_color_20, 0x001d => :xls_color_21, 0x001e => :xls_color_22, 0x001f => :xls_color_23, 0x0020 => :xls_color_24, 0x0021 => :xls_color_25, 0x0022 => :xls_color_26, 0x0023 => :xls_color_27, 0x0024 => :purple, #xls_color_28 0x0025 => :xls_color_29, 0x0026 => :xls_color_30, 0x0027 => :xls_color_31, 0x0028 => :xls_color_32, 0x0029 => :xls_color_33, 0x002a => :xls_color_34, 0x002b => :xls_color_35, 0x002c => :xls_color_36, 0x002d => :xls_color_37, 0x002e => :xls_color_38, 0x002f => :xls_color_39, 0x0030 => :xls_color_40, 0x0031 => :xls_color_41, 0x0032 => :xls_color_42, 0x0033 => :xls_color_43, 0x0034 => :orange, #xls_color_44 0x0035 => :xls_color_45, 0x0036 => :xls_color_46, 0x0037 => :xls_color_47, 0x0038 => :xls_color_48, 0x0039 => :xls_color_49, 0x003a => :xls_color_50, 0x003b => :xls_color_51, 0x003c => :xls_color_52, 0x003d => :xls_color_53, 0x003e => :xls_color_54, 0x003f => :xls_color_55, 0x0040 => :border, 0x0041 => :pattern_bg, 0x0043 => :dialog_bg, 0x004d => :chart_text, 0x004e => :chart_bg, 0x004f => :chart_border, 0x0050 => :tooltip_bg, 0x0051 => :tooltip_text, 0x7fff => :text } SEDOC_ROLOC = COLOR_CODES.invert.update( :xls_color_0 => 0x0008, :xls_color_1 => 0x0009, :xls_color_2 => 0x000a, :xls_color_3 => 0x000b, :xls_color_4 => 0x000c, :xls_color_5 => 0x000d, :xls_color_6 => 0x000e, :fuchsia => 0x000e, :xls_color_7 => 0x000f, :aqua => 0x000f, :xls_color_8 => 0x0010, :xls_color_9 => 0x0011, :xls_color_10 => 0x0012, :xls_color_14 => 0x0016, :xls_color_15 => 0x0017, :grey => 0x0017, :xls_color_28 => 0x0024, :xls_color_44 => 0x0034 ) BINARY_FORMATS = { :blank => 'v3', :boolerr => 'v3C2', :colinfo => 'v5x2', :font => 'v5C3x', :labelsst => 'v3V', :number => "v3#{EIGHT_BYTE_DOUBLE}", :pagesetup => "v8#{EIGHT_BYTE_DOUBLE}2v", :margin => "#{EIGHT_BYTE_DOUBLE}", :rk => 'v3V', :row => 'v4x4V', :window2 => 'v4x2v2x4', :xf => 'v3C4V2v', } # From BIFF5 on, the built-in number formats will be omitted. The built-in # formats are dependent on the current regional settings of the operating # system. The following table shows which number formats are used by # default in a US-English environment. All indexes from 0 to 163 are # reserved for built-in formats. BUILTIN_FORMATS = { # TODO: locale support 0 => 'GENERAL', 1 => '0', 2 => '0.00', 3 => '#,##0', 4 => '#,##0.00', 5 => '"$"#,##0_);("$"#,##0)', 6 => '"$"#,##0_);[Red]("$"#,##0)', 7 => '"$"#,##0.00_);("$"#,##0.00)', 8 => '"$"#,##0.00_);[Red]("$"#,##0.00)', 9 => '0%', 10 => '0.00%', 11 => '0.00E+00', 12 => '# ?/?', 13 => '# ??/??', 14 => 'M/D/YY', 15 => 'D-MMM-YY', 16 => 'D-MMM', 17 => 'MMM-YY', 18 => 'h:mm AM/PM', 19 => 'h:mm:ss AM/PM', 20 => 'h:mm', 21 => 'h:mm:ss', 22 => 'M/D/YY h:mm', 37 => '_(#,##0_);(#,##0)', 38 => '_(#,##0_);[Red](#,##0)', 39 => '_(#,##0.00_);(#,##0.00)', 40 => '_(#,##0.00_);[Red](#,##0.00)', 41 => '_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)', 42 => '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)', 43 => '_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)', 44 => '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)', 45 => 'mm:ss', 46 => '[h]:mm:ss', 47 => 'mm:ss.0', 48 => '##0.0E+0', 49 => '@', } BUILTIN_STYLES = { 0x00 => 'Normal', 0x01 => 'RowLevel_lv', 0x02 => 'ColLevel_lv', 0x03 => 'Comma', 0x04 => 'Currency', 0x05 => 'Percent', 0x06 => 'Comma', 0x07 => 'Currency', 0x08 => 'Hyperlink', 0x09 => 'Followed Hyperlink', } ESCAPEMENT_TYPES = { 0x0001 => :superscript, 0x0002 => :subscript, } SEPYT_TNEMEPACSE = ESCAPEMENT_TYPES.invert FONT_ENCODINGS = { 0x00 => :iso_latin1, 0x01 => :default, 0x02 => :symbol, 0x4d => :apple_roman, 0x80 => :shift_jis, 0x81 => :korean_hangul, 0x82 => :korean_johab, 0x86 => :chinese_simplified, 0x88 => :chinese_traditional, 0xa1 => :greek, 0xa2 => :turkish, 0xa3 => :vietnamese, 0xb1 => :hebrew, 0xb2 => :arabic, 0xba => :baltic, 0xcc => :cyrillic, 0xde => :thai, 0xee => :iso_latin2, 0xff => :oem_latin1, } SGNIDOCNE_TNOF = FONT_ENCODINGS.invert FONT_FAMILIES = { 0x01 => :roman, 0x02 => :swiss, 0x03 => :modern, 0x04 => :script, 0x05 => :decorative, } SEILIMAF_TNOF = FONT_FAMILIES.invert FONT_WEIGHTS = { :bold => 700, :normal => 400, } LEAP_ERROR = Date.new 1900, 2, 28 OPCODES = { :blank => 0x0201, # BLANK ➜ 6.7 :boolerr => 0x0205, # BOOLERR ➜ 6.10 :boundsheet => 0x0085, # ●● BOUNDSHEET ➜ 6.12 :codepage => 0x0042, # ○ CODEPAGE ➜ 6.17 :colinfo => 0x007d, # ○○ COLINFO ➜ 6.18 :continue => 0x003c, # ○ CONTINUE ➜ 6.22 :datemode => 0x0022, # ○ DATEMODE ➜ 6.25 :dbcell => 0x0a0b, # ○ DBCELL :dimensions => 0x0200, # ● DIMENSIONS ➜ 6.31 :eof => 0x000a, # ● EOF ➜ 6.36 :font => 0x0031, # ●● FONT ➜ 6.43 :format => 0x041e, # ○○ FORMAT (Number Format) ➜ 6.45 :formula => 0x0006, # FORMULA ➜ 6.46 :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 :rk => 0x027e, # RK ➜ 6.82 (BIFF3-BIFF8) :row => 0x0208, # ● ROW ➜ 6.83 :rstring => 0x00d6, # RSTRING ➜ 6.84 (BIFF5/BIFF7) :sst => 0x00fc, # ● SST ➜ 6.96 :string => 0x0207, # STRING ➜ 6.98 :style => 0x0293, # ●● STYLE ➜ 6.99 :xf => 0x00e0, # ●● XF ➜ 6.115 :sharedfmla => 0x04bc, # SHAREDFMLA ➜ 5.94 ########################## Unhandled Opcodes ################################ :extsst => 0x00ff, # ● EXTSST ➜ 6.40 :index => 0x020b, # ○ INDEX ➜ 5.7 (Row Blocks), ➜ 6.55 :uncalced => 0x005e, # ○ UNCALCED ➜ 6.104 ########################## ○ Calculation Settings Block ➜ 5.3 :calccount => 0x000c, # ○ CALCCOUNT ➜ 6.14 :calcmode => 0x000d, # ○ CALCMODE ➜ 6.15 :precision => 0x000e, # ○ PRECISION ➜ 6.74 (moved to Workbook Globals # Substream in BIFF5-BIFF8) :refmode => 0x000f, # ○ REFMODE ➜ 6.80 :delta => 0x0010, # ○ DELTA ➜ 6.30 :iteration => 0x0011, # ○ ITERATION ➜ 6.57 :saverecalc => 0x005f, # ○ SAVERECALC ➜ 6.85 (BIFF3-BIFF8 only) ########################## ○ Workbook Protection Block ➜ 5.18 :protect => 0x0012, # ○ PROTECT # Worksheet contents: 1 = protected (➜ 6.77) :windowprot => 0x0019, # ○ WINDOWPROTECT Window settings: 1 = protected # (BIFF4W only, ➜ 6.110) :objectprot => 0x0063, # ○ OBJECTPROTECT # Embedded objects: 1 = protected (➜ 6.69) :scenprotect => 0x00dd, # ○ SCENPROTECT # Scenarios: 1 = protected (BIFF5-BIFF8, ➜ 6.86) :password => 0x0013, # ○ PASSWORD Hash value of the password; # 0 = no password (➜ 6.72) ########################## ○ File Protection Block ➜ 5.19 :writeprot => 0x0086, # ○ WRITEPROT File is write protected # (BIFF3-BIFF8, ➜ 6.112), password in FILESHARING :filepass => 0x002f, # ○ FILEPASS File is read/write-protected, # encryption information (➜ 6.41) :writeaccess => 0x005c, # ○ WRITEACCESS User name (BIFF3-BIFF8, ➜ 6.111) :filesharing => 0x005b, # ○ FILESHARING File sharing options # (BIFF3-BIFF8, ➜ 6.42) ########################## ○ Link Table ➜ 5.10.3 # ●● SUPBOOK Block(s) # Settings for a referenced document :supbook => 0x01ae, # ● SUPBOOK ➜ 6.100 :externname => 0x0223, # ○○ EXTERNNAME ➜ 6.38 :xct => 0x0059, # ○○ ● XCT ➜ 6.114 :crn => 0x005a, # ●● CRN ➜ 6.24 :externsheet => 0x0017, # ● EXTERNSHEET ➜ 6.39 :name => 0x0218, # ○○ NAME ➜ 6.66 ########################## :window1 => 0x003d, # ● WINDOW1 ➜ 6.108 (has information on # which Spreadsheet is 'active') :backup => 0x0040, # ○ BACKUP ➜ 6.5 :country => 0x008c, # ○ COUNTRY (Make writeable?) ➜ 6.23 :hideobj => 0x008d, # ○ HIDEOBJ ➜ 6.52 :palette => 0x0092, # ○ PALETTE ➜ 6.70 :fngroupcnt => 0x009c, # ○ FNGROUPCOUNT :bookbool => 0x00da, # ○ BOOKBOOL ➜ 6.9 :tabid => 0x013d, # ○ TABID :useselfs => 0x0160, # ○ USESELFS (Natural Language Formulas) ➜ 6.105 :dsf => 0x0161, # ○ DSF (Double Stream File) ➜ 6.32 :refreshall => 0x01b7, # ○ REFRESHALL ########################## ● Worksheet View Settings Block ➜ 5.5 :window2 => 0x023e, # ● WINDOW2 ➜ 5.110 :scl => 0x00a0, # ○ SCL ➜ 5.92 (BIFF4-BIFF8 only) :pane => 0x0041, # ○ PANE ➜ 5.75 :selection => 0x001d, # ○○ SELECTION ➜ 5.93 ########################## ○ Page Settings Block ➜ 5.4 :hpagebreaks => 0x001b, # ○ HORIZONTALPAGEBREAKS ➜ 6.54 :vpagebreaks => 0x001a, # ○ VERTICALPAGEBREAKS ➜ 6.107 :header => 0x0014, # ○ HEADER ➜ 6.51 :footer => 0x0015, # ○ FOOTER ➜ 6.44 :hcenter => 0x0083, # ○ HCENTER ➜ 6.50 (BIFF3-BIFF8 only) :vcenter => 0x0084, # ○ VCENTER ➜ 6.106 (BIFF3-BIFF8 only) :leftmargin => 0x0026, # ○ LEFTMARGIN ➜ 6.62 :rightmargin => 0x0027, # ○ RIGHTMARGIN ➜ 6.81 :topmargin => 0x0028, # ○ TOPMARGIN ➜ 6.103 :bottommargin => 0x0029, # ○ BOTTOMMARGIN ➜ 6.11 # ○ PLS (opcode unknown) :pagesetup => 0x00a1, # ○ PAGESETUP ➜ 6.89 (BIFF4-BIFF8 only) :bitmap => 0x00e9, # ○ BITMAP ➜ 6.6 (Background-Bitmap, BIFF8 only) ########################## :printheaders => 0x002a, # ○ PRINTHEADERS ➜ 6.76 :printgridlns => 0x002b, # ○ PRINTGRIDLINES ➜ 6.75 :gridset => 0x0082, # ○ GRIDSET ➜ 6.48 :guts => 0x0080, # ○ GUTS ➜ 6.49 :defrowheight => 0x0225, # ○ DEFAULTROWHEIGHT ➜ 6.28 :wsbool => 0x0081, # ○ WSBOOL ➜ 6.113 :defcolwidth => 0x0055, # ○ DEFCOLWIDTH ➜ 6.29 :sort => 0x0090, # ○ SORT ➜ 6.95 } =begin ## unknown opcodes 0x00bf, 0x00c0, 0x00c1, 0x00e1, 0x00e2, 0x00eb, 0x01af, 0x01bc =end SEDOCPO = OPCODES.invert TWIPS = 20 UNDERLINE_TYPES = { 0x0001 => :single, 0x0002 => :double, 0x0021 => :single_accounting, 0x0022 => :double_accounting, } SEPYT_ENILREDNU = UNDERLINE_TYPES.invert XF_H_ALIGN = { :default => 0, :left => 1, :center => 2, :right => 3, :fill => 4, :justify => 5, :merge => 6, :distributed => 7, } NGILA_H_FX = XF_H_ALIGN.invert XF_TEXT_DIRECTION = { :context => 0, :left_to_right => 1, :right_to_left => 2, } NOITCERID_TXET_FX = XF_TEXT_DIRECTION.invert XF_V_ALIGN = { :top => 0, :middle => 1, :bottom => 2, :justify => 3, :distributed => 4, } NGILA_V_FX = XF_V_ALIGN.invert # border line styles taken from http://www.openoffice.org/sc/excelfileformat.pdf XF_BORDER_LINE_STYLES = { 0x00 => :none, 0x01 => :thin, 0x02 => :medium, 0x03 => :dashed, 0x04 => :dotted, 0x05 => :thick, 0x06 => :double, 0x07 => :hair, # the following are only valid for BIFF8 and higher: 0x08 => :medium_dashed, 0x09 => :thin_dash_dotted, 0x0a => :medium_dash_dotted, 0x0b => :thin_dash_dot_dotted, 0x0c => :medium_dash_dot_dotted, 0x0d => :slanted_medium_dash_dotted } # ensure reader always gets a valid line style XF_BORDER_LINE_STYLES.default = :none SELYTS_ENIL_REDROB_FX = XF_BORDER_LINE_STYLES.invert SELYTS_ENIL_REDROB_FX.default = 0x00 OPCODE_SIZE = 4 ROW_HEIGHT = 12.1 SST_CHUNKSIZE = 20 def binfmt key BINARY_FORMATS[key] end def opcode key OPCODES[key] end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/row.rb0000644000004100000410000000424112216006760021371 0ustar www-datawww-datarequire 'date' require 'spreadsheet/row' module Spreadsheet module Excel ## # Excel-specific Row methods class Row < Spreadsheet::Row ## # The Excel date calculation erroneously assumes that 1900 is a leap-year. All # Dates after 28.2.1900 are off by one. LEAP_ERROR = Date.new 1900, 2, 28 ## # Force convert the cell at _idx_ to a Date def date idx _date at(idx) end ## # Force convert the cell at _idx_ to a DateTime def datetime idx _datetime at(idx) end def each size.times do |idx| yield self[idx] end end ## # Access data in this Row like you would in an Array. If a cell is formatted # as a Date or DateTime, the decoded Date or DateTime value is returned. def [] idx, len=nil if len idx = idx...(idx+len) end if idx.is_a? Range data = [] idx.each do |i| data.push enriched_data(i, at(i)) end data else enriched_data idx, at(idx) end end ## # Returns data as an array. If a cell is formatted as a Date or DateTime, the # decoded Date or DateTime value is returned. def to_a self[0...length] end private def _date data # :nodoc: return data if data.is_a?(Date) datetime = _datetime data Date.new datetime.year, datetime.month, datetime.day end def _datetime data # :nodoc: return data if data.is_a?(DateTime) base = @worksheet.date_base date = base + data.to_f hour = (data.to_f % 1) * 24 min = (hour % 1) * 60 sec = ((min % 1) * 60).round min = min.floor hour = hour.floor if sec > 59 sec = 0 min += 1 end if min > 59 min = 0 hour += 1 end if hour > 23 hour = 0 date += 1 end if LEAP_ERROR > base date -= 1 end DateTime.new(date.year, date.month, date.day, hour, min, sec) end def enriched_data idx, data # :nodoc: res = nil if link = @worksheet.links[[@idx, idx]] res = link elsif data.is_a?(Numeric) && fmt = format(idx) res = if fmt.datetime? || fmt.time? _datetime data elsif fmt.date? _date data end end res || data end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/worksheet.rb0000644000004100000410000000556212216006760022604 0ustar www-datawww-datarequire 'spreadsheet/excel/offset' require 'spreadsheet/excel/row' require 'spreadsheet/worksheet' 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 def add_link row, column, link @links.store [row, column], link end def column idx ensure_rows_read super end def date_base @workbook.date_base end def margins ensure_rows_read super end def pagesetup ensure_rows_read super end def each *args ensure_rows_read super end def ensure_rows_read return if @row_addresses @dimensions = nil @row_addresses = [] @reader.read_worksheet self, @offset if @reader end def row idx @rows[idx] or begin ensure_rows_read if addr = @row_addresses[idx] row = @reader.read_row self, addr [:default_format, :height, :outline_level, :hidden, ].each do |key| row.send "unupdated_#{key}=", addr[key] end row.worksheet = self row else Row.new self, idx end end end def rows self.to_a end def row_updated idx, row res = super @workbook.changes.store self, true @workbook.changes.store :boundsheets, true @changes.store idx, true @changes.store :dimensions, true res end def set_row_address idx, opts @offsets.store idx, opts[:row_block] @row_addresses[idx] = opts end def shared_string idx @workbook.shared_string idx end private ## premature optimization? def have_set_dimensions value, pos, len if @row_addresses.size < row_count @row_addresses.concat Array.new(row_count - @row_addresses.size) end end def recalculate_dimensions ensure_rows_read shorten @rows @dimensions = [] @dimensions[0] = [ index_of_first(@rows), index_of_first(@row_addresses) ].compact.min || 0 @dimensions[1] = [ @rows.size, @row_addresses.size ].compact.max || 0 compact = @rows.compact first_rows = compact.collect do |row| row.first_used end.compact.min first_addrs = @row_addresses.compact.collect do |addr| addr[:first_used] end.min @dimensions[2] = [ first_rows, first_addrs ].compact.min || 0 last_rows = compact.collect do |row| row.first_unused end.max last_addrs = @row_addresses.compact.collect do |addr| addr[:first_unused] end.max @dimensions[3] = [last_rows, last_addrs].compact.max || 0 @dimensions end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/offset.rb0000644000004100000410000000214012216006760022044 0ustar www-datawww-datarequire 'spreadsheet/compatibility' module Spreadsheet module Excel ## # This module is used to keep track of offsets in modified Excel documents. # Considered internal and subject to change without notice. module Offset include Compatibility attr_reader :changes, :offsets def initialize *args super @changes = {} @offsets = {} end def Offset.append_features mod super mod.module_eval do class << self include Compatibility def offset *keys keys.each do |key| attr_reader key unless instance_methods.include? method_name(key) define_method "#{key}=" do |value| @changes.store key, true instance_variable_set ivar_name(key), value end define_method "set_#{key}" do |value, pos, len| instance_variable_set ivar_name(key), value @offsets.store key, [pos, len] havename = "have_set_#{key}" send(havename, value, pos, len) if respond_to? havename end end end end end end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/workbook.rb0000644000004100000410000000322712216006760022422 0ustar www-datawww-datarequire 'spreadsheet/workbook' require 'spreadsheet/excel/offset' require 'spreadsheet/excel/writer' require 'ole/storage' module Spreadsheet module Excel ## # Excel-specific Workbook methods. These are mostly pertinent to the Excel # reader. You should have no reason to use any of these. class Workbook < Spreadsheet::Workbook include Spreadsheet::Encodings include Spreadsheet::Excel::Offset BIFF_VERSIONS = { 0x000 => 2, 0x007 => 2, 0x200 => 2, 0x300 => 3, 0x400 => 4, 0x500 => 5, 0x600 => 8, } VERSION_STRINGS = { 0x600 => 'Microsoft Excel 97/2000/XP', 0x500 => 'Microsoft Excel 95', } offset :encoding, :boundsheets, :sst attr_accessor :bof, :ole attr_writer :date_base def Workbook.open io, opts = {} @reader = Reader.new opts @reader.read io end def initialize *args super enc = 'UTF-16LE' if RUBY_VERSION >= '1.9' enc = Encoding.find enc end @encoding = enc @version = 0x600 @sst = [] end def add_shared_string str @sst.push str end def add_worksheet worksheet @changes.store :boundsheets, true super end def biff_version case @bof when 0x009 2 when 0x209 3 when 0x409 4 else BIFF_VERSIONS.fetch(@version) { raise "Unkown BIFF_VERSION '#@version'" } end end def date_base @date_base ||= DateTime.new 1899, 12, 31 end def shared_string idx @sst[idx.to_i].content end def sst_size @sst.size end def uninspect_variables super.push '@sst', '@offsets', '@changes' end def version_string client VERSION_STRINGS.fetch(@version, "Unknown"), 'UTF-8' end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/writer.rb0000644000004100000410000000005412216006760022074 0ustar www-datawww-datarequire 'spreadsheet/excel/writer/workbook' spreadsheet-0.9.0/lib/spreadsheet/excel/error.rb0000644000004100000410000000126212216006760021713 0ustar www-datawww-datamodule Spreadsheet module Excel ## # This class encapsulates Excel Error-Codes class Error attr_reader :code ERROR_VALUES = { 0x00 => '#NULL!', # Intersection of two cell ranges is empty 0x07 => '#DIV/0!', # Division by zero 0x0F => '#VALUE!', # Wrong type of operand 0x17 => '#REF!', # Illegal or deleted cell reference 0x1D => '#NAME?', # Wrong function or range name 0x24 => '#NUM!', # Value range overflow 0x2A => '#N/A!', # Argument or function not available } def initialize code @code = code end ## # The String value Excel associates with an Error code def value ERROR_VALUES.fetch @code, '#UNKNOWN' end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/sst_entry.rb0000644000004100000410000000214012216006760022610 0ustar www-datawww-datarequire 'spreadsheet/encodings' module Spreadsheet module Excel ## # Shared String Table Entry class SstEntry include Spreadsheet::Encodings attr_accessor :chars, :phonetic, :richtext, :flags, :available, :continued_chars, :wide def initialize opts = {} @content = nil @offset = opts[:offset] @ole = opts[:ole] @reader = opts[:reader] @continuations = [] end ## # Access the contents of this Shared String def content @content or begin data = nil data = @ole[@offset, @available] content, _ = @reader.read_string_body data, @flags, @available, @wide @continuations.each do |offset, len| @reader.continue_string(@ole[offset,len], [content, @chars]) end content = client content, 'UTF-16LE' if @reader.memoize? @content = content end content end end ## # Register the offset of a String continuation def continue offset, size, chars @continued_chars -= chars @continuations.push [offset, size] end def continued? # :nodoc: @continued_chars > 0 end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/internals/0000755000004100000410000000000012216006760022233 5ustar www-datawww-dataspreadsheet-0.9.0/lib/spreadsheet/excel/internals/biff8.rb0000644000004100000410000000070712216006760023562 0ustar www-datawww-datamodule Spreadsheet module Excel module Internals ## # Binary Formats and other configurations internal to Biff8. This Module is # likely to be expanded as Support for older Versions of Excel grows and more # Binary formats are moved here for disambiguation. module Biff8 BINARY_FORMATS = { :bof => 'v4V2', :dimensions => 'V2v2x2', } def binfmt key # :nodoc: BINARY_FORMATS.fetch key do super end end end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/internals/biff5.rb0000644000004100000410000000055412216006760023557 0ustar www-datawww-datamodule Spreadsheet module Excel module Internals ## # Binary Formats and other configurations internal to Biff5. This Module is # likely to be expanded as Support for older Versions of Excel grows. module Biff5 BINARY_FORMATS = { :dimensions => 'v5', } def binfmt key # :nodoc: BINARY_FORMATS.fetch key do super end end end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/reader.rb0000644000004100000410000015402712216006760022034 0ustar www-datawww-datarequire 'spreadsheet/encodings' require 'spreadsheet/font' require 'spreadsheet/formula' require 'spreadsheet/link' require 'spreadsheet/excel/error' require 'spreadsheet/excel/internals' require 'spreadsheet/excel/sst_entry' require 'spreadsheet/excel/worksheet' module Spreadsheet module Excel ## # Reader class for Excel Workbooks. Most read_* method correspond to an # Excel-Record/Opcode. You should not need to call any of its methods # directly. If you think you do, look at #read class Reader include Spreadsheet::Encodings include Spreadsheet::Excel::Internals ROW_BLOCK_OPS = { :blank => true, :boolerr => true, :dbcell => true, :formula => true, :label => true, :labelsst => true, :mulblank => true, :mulrk => true, :number => true, :rk => true, :rstring => true, } def initialize opts = {} @pos = 0 @bigendian = opts.fetch(:bigendian) { [1].pack('l') != "\001\000\000\000" } @opts = opts @boundsheets = nil @current_row_block = {} @current_row_block_offset = nil @formats = {} BUILTIN_FORMATS.each do |key, fmt| @formats.store key, client(fmt, 'UTF-8') end end def decode_rk work # Bit Mask Contents # 0 0x00000001 0 = Value not changed 1 = Value is multiplied by 100 # 1 0x00000002 0 = Floating-point value 1 = Signed integer value # 31-2 0xFFFFFFFC Encoded value # # If bit 1 is cleared, the encoded value represents the 30 most significant # bits of an IEEE 754 floating-point value (64-bit double precision). The # 34 least significant bits must be set to zero. If bit 1 is set, the # encoded value represents a signed 30-bit integer value. To get the # correct integer, the encoded value has to be shifted right arithmetically # by 2 bits. If bit 0 is set, the decoded value (both integer and # floating-point) must be divided by 100 to get the final result. flags, = work.unpack 'C' cent = flags & 1 int = flags & 2 value = 0 if int == 0 ## remove two bits integer, = work.unpack 'V' integer &= 0xfffffffc value, = ("\0\0\0\0" << [integer].pack('V')).unpack EIGHT_BYTE_DOUBLE else ## I can't find a format for unpacking a little endian signed integer. # 'V' works for packing, but not for unpacking. But the following works # fine afaics: unsigned, = (@bigendian ? work.reverse : work).unpack 'l' ## remove two bits value = unsigned >> 2 end if cent == 1 value /= 100.0 end value end def encoding codepage_id name = CODEPAGES.fetch(codepage_id) { raise "Unknown Codepage 0x%04x" % codepage_id } if RUBY_VERSION >= '1.9' Encoding.find name else name end end def get_next_chunk pos = @pos if pos < @data.size op, len = @data[@pos,OPCODE_SIZE].unpack('v2') @pos += OPCODE_SIZE if len work = @data[@pos,len] @pos += len code = SEDOCPO.fetch(op, op) if io = @opts[:print_opcodes] io.puts sprintf("0x%04x/%-16s %5i: %s", op, code.inspect, len, work.inspect) end [ pos, code, len + OPCODE_SIZE, work] end end end def in_row_block? op, previous if op == :row previous == op else ROW_BLOCK_OPS.include?(op) end end def memoize? @opts[:memoization] end def postread_workbook sheets = @workbook.worksheets sheets.each_with_index do |sheet, idx| offset = sheet.offset nxt = (nxtsheet = sheets[idx + 1]) ? nxtsheet.offset : @workbook.ole.size @workbook.offsets.store sheet, [offset, nxt - offset] end end def postread_worksheet worksheet end ## # The entry-point for reading Excel-documents. Reads the Biff-Version and # loads additional reader-methods before proceeding with parsing the document. def read io setup io read_workbook @workbook.default_format = @workbook.format 0 @workbook.changes.clear @workbook end def read_blank worksheet, addr, work # Offset Size Contents # 0 2 Index to row # 2 2 Index to column # 4 2 Index to XF record (➜ 6.115) row, column, xf = work.unpack binfmt(:blank) set_cell worksheet, row, column, xf end def read_bof # Offset Size Contents # 0 2 BIFF version (always 0x0600 for BIFF8) # 2 2 Type of the following data: 0x0005 = Workbook globals # 0x0006 = Visual Basic module # 0x0010 = Worksheet # 0x0020 = Chart # 0x0040 = Macro sheet # 0x0100 = Workspace file # 4 2 Build identifier # 6 2 Build year # 8 4 File history flags # 12 4 Lowest Excel version that can read all records in this file _, @bof, _, work = get_next_chunk ## version and datatype are common to all Excel-Versions. Later versions # have additional information such as build-id and -year (from BIFF5). # These are ignored for the time being. version, datatype = work.unpack('v2') if datatype == 0x5 @version = version end end def read_boolerr worksheet, addr, work # Offset Size Contents # 0 2 Index to row # 2 2 Index to column # 4 2 Index to XF record (➜ 6.115) # 6 1 Boolean or error value (type depends on the following byte) # 7 1 0 = Boolean value; 1 = Error code row, column, xf, value, error = work.unpack 'v3C2' set_cell worksheet, row, column, xf, error == 0 ? value > 0 : Error.new(value) end def read_boundsheet work, pos, len # Offset Size Contents # 0 4 Absolute stream position of the BOF record of the sheet # represented by this record. This field is never encrypted # in protected files. # 4 1 Visibility: 0x00 = Visible # 0x01 = Hidden # 0x02 = Strong hidden (see below) # 5 1 Sheet type: 0x00 = Worksheet # 0x02 = Chart # 0x06 = Visual Basic module # 6 var. Sheet name: BIFF5/BIFF7: Byte string, # 8-bit string length (➜ 3.3) # BIFF8: Unicode string, 8-bit string length (➜ 3.4) offset, _, _ = work.unpack("VC2") name = client read_string(work[6..-1]), @workbook.encoding if @boundsheets @boundsheets[0] += 1 @boundsheets[2] += len else @boundsheets = [1, pos, len] end @workbook.set_boundsheets(*@boundsheets) @workbook.add_worksheet Worksheet.new(:name => name, :ole => @book, :offset => offset, :reader => self) end def read_codepage work, pos, len codepage, _ = work.unpack 'v' @workbook.set_encoding encoding(codepage), pos, len end def read_colinfo worksheet, work, pos, len # 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] first.upto last do |col| column = Column.new col, @workbook.format(xf), :width => width.to_f / 256, :hidden => (opts & 0x0001) > 0, :collapsed => (opts & 0x1000) > 0, :outline_level => (opts & 0x0700) / 256 column.worksheet = worksheet worksheet.columns[col] = column end end def read_dimensions worksheet, work, pos, len # Offset Size Contents # 0 4 Index to first used row # 4 4 Index to last used row, increased by 1 # 8 2 Index to first used column # 10 2 Index to last used column, increased by 1 # 12 2 Not used worksheet.set_dimensions work.unpack(binfmt(:dimensions)), pos, len end def read_font work, pos, len # Offset Size Contents # 0 2 Height of the font (in twips = 1/20 of a point) # 2 2 Option flags: # Bit Mask Contents # 0 0x0001 1 = Characters are bold (redundant, see below) # 1 0x0002 1 = Characters are italic # 2 0x0004 1 = Characters are underlined # (redundant, see below) # 3 0x0008 1 = Characters are struck out # 4 0x0010 1 = Characters are outlined (djberger) # 5 0x0020 1 = Characters are shadowed (djberger) # 4 2 Colour index (➜ 6.70) # 6 2 Font weight (100-1000). Standard values are # 0x0190 (400) for normal text and # 0x02bc (700) for bold text. # 8 2 Escapement type: 0x0000 = None # 0x0001 = Superscript # 0x0002 = Subscript # 10 1 Underline type: 0x00 = None # 0x01 = Single # 0x02 = Double # 0x21 = Single accounting # 0x22 = Double accounting # 11 1 Font family: # 0x00 = None (unknown or don't care) # 0x01 = Roman (variable width, serifed) # 0x02 = Swiss (variable width, sans-serifed) # 0x03 = Modern (fixed width, serifed or sans-serifed) # 0x04 = Script (cursive) # 0x05 = Decorative (specialised, # for example Old English, Fraktur) # 12 1 Character set: 0x00 = 0 = ANSI Latin # 0x01 = 1 = System default # 0x02 = 2 = Symbol # 0x4d = 77 = Apple Roman # 0x80 = 128 = ANSI Japanese Shift-JIS # 0x81 = 129 = ANSI Korean (Hangul) # 0x82 = 130 = ANSI Korean (Johab) # 0x86 = 134 = ANSI Chinese Simplified GBK # 0x88 = 136 = ANSI Chinese Traditional BIG5 # 0xa1 = 161 = ANSI Greek # 0xa2 = 162 = ANSI Turkish # 0xa3 = 163 = ANSI Vietnamese # 0xb1 = 177 = ANSI Hebrew # 0xb2 = 178 = ANSI Arabic # 0xba = 186 = ANSI Baltic # 0xcc = 204 = ANSI Cyrillic # 0xde = 222 = ANSI Thai # 0xee = 238 = ANSI Latin II (Central European) # 0xff = 255 = OEM Latin I # 13 1 Not used # 14 var. Font name: # BIFF5/BIFF7: Byte string, 8-bit string length (➜ 3.3) # BIFF8: Unicode string, 8-bit string length (➜ 3.4) name = client read_string(work[14..-1]), @workbook.encoding font = Font.new name size, opts, color, font.weight, escapement, underline, family, encoding = work.unpack binfmt(:font) font.size = size / TWIPS font.italic = opts & 0x0002 font.strikeout = opts & 0x0008 font.outline = opts & 0x0010 font.shadow = opts & 0x0020 font.color = COLOR_CODES[color] || :text font.escapement = ESCAPEMENT_TYPES[escapement] font.underline = UNDERLINE_TYPES[underline] font.family = FONT_FAMILIES[family] font.encoding = FONT_ENCODINGS[encoding] @workbook.add_font font end def read_format work, pos, len # Offset Size Contents # 0 2 Format index used in other records # 2 var. Number format string # (Unicode string, 16-bit string length, ➜ 3.4) idx, = work.unpack 'v' value = read_string work[2..-1], 2 @formats.store idx, client(value, @workbook.encoding) end def read_formula worksheet, addr, work # Offset Size Contents # 0 2 Index to row # 2 2 Index to column # 4 2 Index to XF record (➜ 6.115) # 6 8 Result of the formula. See below for details. # 14 2 Option flags: # Bit Mask Contents # 0 0x0001 1 = Recalculate always # 1 0x0002 1 = Calculate on open # 3 0x0008 1 = Part of a shared formula # 16 4 Not used # 20 var. Formula data (RPN token array, ➜ 4) # Offset Size Contents # 0 2 Size of the following formula data (sz) # 2 sz Formula data (RPN token array) # [2+sz] var. (optional) Additional data for specific tokens # (➜ 4.1.6, for example tArray token, ➜ 4.8.7) # # Result of the Formula # Dependent on the type of value the formula returns, the result field has # the following format: # # Result is a numeric value: # Offset Size Contents # 0 8 IEEE 754 floating-point value (64-bit double precision) # # Result is a string (the string follows in a STRING record, ➜ 6.98): # Offset Size Contents # 0 1 0x00 (identifier for a string value) # 1 5 Not used # 6 2 0xffff # Note: In BIFF8 the string must not be empty. For empty cells there is a # special identifier defined (see below). # # Result is a Boolean value: # Offset Size Contents # 0 1 0x01 (identifier for a Boolean value) # 1 1 Not used # 2 1 0 = FALSE, 1 = TRUE # 3 3 Not used # 6 2 0xffff # # Result is an error value: # Offset Size Contents # 0 1 0x02 (identifier for an error value) # 1 1 Not used # 2 1 Error code (➜ 3.7) # 3 3 Not used # 6 2 0xffff # # Result is an empty cell (BIFF8), for example an empty string: # Offset Size Contents # 0 1 0x03 (identifier for an empty cell) # 1 5 Not used # 6 2 0xffff row, column, xf, rtype, rval, rcheck, opts = work.unpack 'v3CxCx3v2' formula = Formula.new formula.shared = (opts & 0x08) > 0 formula.data = work[20..-1] if rcheck != 0xffff || rtype > 3 value, = work.unpack 'x6E' unless value # on architectures where sizeof(double) > 8 value, = work.unpack 'x6e' end formula.value = value elsif rtype == 0 pos, op, len, work = get_next_chunk if op == :sharedfmla ## TODO: formula-support in 0.8.0 pos, op, len, work = get_next_chunk end if op == :string formula.value = client read_string(work, 2), @workbook.encoding else warn "String Value expected after Formula, but got #{op}" formula.value = Error.new 0x2a @pos = pos end elsif rtype == 1 formula.value = rval > 0 elsif rtype == 2 formula.value = Error.new rval else # leave the Formula value blank end set_cell worksheet, row, column, xf, formula end def read_hlink worksheet, work, pos, len # 6.53.1 Common Record Contents # Offset Size Contents # 0 8 Cell range address of all cells containing this hyperlink # (➜ 3.13.1) # 8 16 GUID of StdLink: # D0 C9 EA 79 F9 BA CE 11 8C 82 00 AA 00 4B A9 0B # (79EAC9D0-BAF9-11CE-8C82-00AA004BA90B) # 24 4 Unknown value: 0x00000002 # 28 4 Option flags (see below) # Bit Mask Contents # 0 0x00000001 0 = No link extant # 1 = File link or URL # 1 0x00000002 0 = Relative file path # 1 = Absolute path or URL # 2 and 4 0x00000014 0 = No description # 1 (both bits) = Description # 3 0x00000008 0 = No text mark # 1 = Text mark # 7 0x00000080 0 = No target frame # 1 = Target frame # 8 0x00000100 0 = File link or URL # 1 = UNC path (incl. server name) #-------------------------------------------------------------------------- # [32] 4 (optional, see option flags) Character count of description # text, including trailing zero word (dl) # [36] 2∙dl (optional, see option flags) Character array of description # text, no Unicode string header, always 16-bit characters, # zero-terminated #-------------------------------------------------------------------------- # [var.] 4 (optional, see option flags) Character count of target # frame, including trailing zero word (fl) # [var.] 2∙fl (optional, see option flags) Character array of target # frame, no Unicode string header, always 16-bit characters, # zero-terminated #-------------------------------------------------------------------------- # var. var. Special data (➜ 6.53.2 and following) #-------------------------------------------------------------------------- # [var.] 4 (optional, see option flags) Character count of the text # mark, including trailing zero word (tl) # [var.] 2∙tl (optional, see option flags) Character array of the text # mark without “#” sign, no Unicode string header, always # 16-bit characters, zero-terminated firstrow, lastrow, firstcol, lastcol, _, opts = work.unpack 'v4H32x4V' has_link = opts & 0x0001 desc = opts & 0x0014 textmark = opts & 0x0008 target = opts & 0x0080 unc = opts & 0x0100 link = Link.new _, description = nil pos = 32 if desc > 0 description, pos = read_hlink_string work, pos link << description end if target > 0 link.target_frame, pos = read_hlink_string work, pos end if unc > 0 # 6.53.4 Hyperlink to a File with UNC (Universal Naming Convention) Path # These data fields are for UNC paths containing a server name (for # instance “\\server\path\file.xls”). The lower 9 bits of the option # flags field must be 1.x00x.xx112. # Offset Size Contents # 0 4 Character count of the UNC, # including trailing zero word (fl) # 4 2∙fl Character array of the UNC, no Unicode string header, # always 16-bit characters, zeroterminated. link.url, pos = read_hlink_string work, pos elsif has_link > 0 uid, = work.unpack "x#{pos}H32" pos += 16 if uid == "e0c9ea79f9bace118c8200aa004ba90b" # 6.53.2 Hyperlink containing a URL (Uniform Resource Locator) # These data fields occur for links which are not local files or files # in the local network (for instance HTTP and FTP links and e-mail # addresses). The lower 9 bits of the option flags field must be # 0.x00x.xx112 (x means optional, depending on hyperlink content). The # GUID could be used to distinguish a URL from a file link. # Offset Size Contents # 0 16 GUID of URL Moniker: # E0 C9 EA 79 F9 BA CE 11 8C 82 00 AA 00 4B A9 0B # (79EAC9E0-BAF9-11CE-8C82-00AA004BA90B) # 16 4 Size of character array of the URL, including trailing # zero word (us). There are us/2-1 characters in the # following string. # 20 us Character array of the URL, no Unicode string header, # always 16-bit characters, zeroterminated size, = work.unpack "x#{pos}V" pos += 4 data = work[pos, size].chomp "\000\000" link.url = client data pos += size else # 6.53.3 Hyperlink to a Local File # These data fields are for links to files on local drives. The path of # the file can be complete with drive letter (absolute) or relative to # the location of the workbook. The lower 9 bits of the option flags # field must be 0.x00x.xxx12. The GUID could be used to distinguish a # URL from a file link. # Offset Size Contents # 0 16 GUID of File Moniker: # 03 03 00 00 00 00 00 00 C0 00 00 00 00 00 00 46 # (00000303-0000-0000-C000-000000000046) # 16 2 Directory up-level count. Each leading “..\” in the # file link is deleted and increases this counter. # 18 4 Character count of the shortened file path and name, # including trailing zero byte (sl) # 22 sl Character array of the shortened file path and name in # 8.3-DOS-format. This field can be filled with a long # file name too. No Unicode string header, always 8-bit # characters, zeroterminated. # 22+sl 24 Unknown byte sequence: # FF FF AD DE 00 00 00 00 # 00 00 00 00 00 00 00 00 # 00 00 00 00 00 00 00 00 # 46+sl 4 Size of the following file link field including string # length field and additional data field (sz). If sz is # zero, nothing will follow (except a text mark). # [50+sl] 4 (optional) Size of character array of the extended file # path and name (xl). There are xl/2 characters in the # following string. # [54+sl] 2 (optional) Unknown byte sequence: 03 00 # [56+sl] xl (optional) Character array of the extended file path # and name (xl), no Unicode string header, always 16-bit # characters, not zero-terminated uplevel, count = work.unpack "x#{pos}vV" pos += 6 # TODO: short file path may have any of the OEM encodings. Find out which # and use the #client method to convert the encoding. prefix = internal('..\\', 'UTF-8') * uplevel link.dos = link.url = prefix << work[pos, count].chomp("\000") pos += count + 24 total, size = work.unpack "x#{pos}V2" pos += 10 if total > 0 link.url = client work[pos, size] pos += size end end else # 6.53.5 Hyperlink to the Current Workbook # In this case only the text mark field is present (optional with # description). # Example: The URL “#Sheet2!B1:C2” refers to the given range in the # current workbook. # The lower 9 bits of the option flags field must be 0.x00x.1x002. end if textmark > 0 link.fragment, _ = read_hlink_string work, pos end if link.empty? link << link.href end firstrow.upto lastrow do |row| firstcol.upto lastcol do |col| worksheet.add_link row, col, link end end end def read_hlink_string work, pos count, = work.unpack "x#{pos}V" len = count * 2 pos += 4 data = work[pos, len].chomp "\000\000" pos += len [client(data, 'UTF-16LE'), pos] end def read_index worksheet, work, pos, len # Offset Size Contents # 0 4 Not used # 4 4 Index to first used row (rf, 0-based) # 8 4 Index to first row of unused tail of sheet # (rl, last used row + 1, 0-based) # 12 4 Absolute stream position of the # DEFCOLWIDTH record (➜ 6.29) of the current sheet. If this # record does not exist, the offset points to the record at # the position where the DEFCOLWIDTH record would occur. # 16 4∙nm Array of nm absolute stream positions to the # DBCELL record (➜ 6.26) of each Row Block # TODO: use the index if it exists # _, first_used, first_unused, defcolwidth, *indices = work.unpack 'V*' end def read_label worksheet, addr, work # Offset Size Contents # 0 2 Index to row # 2 2 Index to column # 4 2 Index to XF record (➜ 6.115) # 6 var. Unicode string, 16-bit string length (➜ 3.4) row, column, xf = work.unpack 'v3' value = client read_string(work[6..-1], 2), @workbook.encoding set_cell worksheet, row, column, xf, value end def read_labelsst worksheet, addr, work # Offset Size Contents # 0 2 Index to row # 2 2 Index to column # 4 2 Index to XF record (➜ 6.115) # 6 4 Index into SST record (➜ 6.96) row, column, xf, index = work.unpack binfmt(:labelsst) set_cell worksheet, row, column, xf, worksheet.shared_string(index) end def read_mulblank worksheet, addr, work # Offset Size Contents # 0 2 Index to row # 2 2 Index to first column (fc) # 4 2∙nc List of nc=lc-fc+1 16-bit indexes to XF records (➜ 6.115) # 4+2∙nc 2 Index to last column (lc) row, column, *xfs = work.unpack 'v*' xfs.pop #=> last_column xfs.each_with_index do |xf, idx| set_cell worksheet, row, column + idx, xf end end def read_mulrk worksheet, addr, work # Offset Size Contents # 0 2 Index to row # 2 2 Index to first column (fc) # 4 6∙nc List of nc=lc-fc+1 XF/RK structures. Each XF/RK contains: # Offset Size Contents # 0 2 Index to XF record (➜ 6.115) # 2 4 RK value (➜ 3.6) # 4+6∙nc 2 Index to last column (lc) row, column = work.unpack 'v2' 4.step(work.size - 6, 6) do |idx| xf, = work.unpack "x#{idx}v" set_cell worksheet, row, column, xf, decode_rk(work[idx + 2, 4]) column += 1 end end def read_number worksheet, addr, work # Offset Size Contents # 0 2 Index to row # 2 2 Index to column # 4 2 Index to XF record (➜ 6.115) # 6 8 IEEE 754 floating-point value (64-bit double precision) row, column, xf, value = work.unpack binfmt(:number) set_cell worksheet, row, column, xf, value end def read_rk worksheet, addr, work # Offset Size Contents # 0 2 Index to row # 2 2 Index to column # 4 2 Index to XF record (➜ 6.115) # 6 4 RK value (➜ 3.6) row, column, xf = work.unpack 'v3' set_cell worksheet, row, column, xf, decode_rk(work[6,4]) end def read_row worksheet, addr row = addr[:index] @current_row_block.fetch [worksheet, row] do @current_row_block.clear cells = @current_row_block[[worksheet, row]] = Row.new(nil, row) @pos = addr[:offset] found = false while tuple = get_next_chunk pos, op, _, work = tuple case op when :eof # ● EOF ➜ 6.36 - we should only get here if there is just # one Row-Block @pos = pos return cells when :dbcell # ○ DBCELL Stream offsets to the cell records of each row return cells when :row # ○○ Row Blocks ➜ 5.7 # ● ROW ➜ 6.83 # ignore, we already did these in read_worksheet return cells if found when :blank # BLANK ➜ 6.7 found = true read_blank worksheet, addr, work when :boolerr # BOOLERR ➜ 6.10 found = true read_boolerr worksheet, addr, work when 0x0002 # INTEGER ➜ 6.56 (BIFF2 only) found = true # TODO: implement for BIFF2 support when :formula # FORMULA ➜ 6.46 found = true read_formula worksheet, addr, work when :label # LABEL ➜ 6.59 (BIFF2-BIFF7) found = true read_label worksheet, addr, work when :labelsst # LABELSST ➜ 6.61 (BIFF8 only) found = true read_labelsst worksheet, addr, work when :mulblank # MULBLANK ➜ 6.64 (BIFF5-BIFF8) found = true read_mulblank worksheet, addr, work when :mulrk # MULRK ➜ 6.65 (BIFF5-BIFF8) found = true read_mulrk worksheet, addr, work when :number # NUMBER ➜ 6.68 found = true read_number worksheet, addr, work when :rk # RK ➜ 6.82 (BIFF3-BIFF8) found = true read_rk worksheet, addr, work when :rstring # RSTRING ➜ 6.84 (BIFF5/BIFF7) found = true read_rstring worksheet, addr, work end end cells end end def read_rstring worksheet, addr, work # Offset Size Contents # 0 2 Index to row # 2 2 Index to column # 4 2 Index to XF record (➜ 6.115) # 6 sz Unformatted Unicode string, 16-bit string length (➜ 3.4) # 6+sz 2 Number of Rich-Text formatting runs (rt) # 8+sz 4·rt List of rt formatting runs (➜ 3.2) row, column, xf = work.unpack 'v3' value = client read_string(work[6..-1], 2), @workbook.encoding set_cell worksheet, row, column, xf, value end def read_window2 worksheet, work, pos, len # This record contains additional settings for the document window # (BIFF2-BIFF4) or for the window of a specific worksheet (BIFF5-BIFF8). # It is part of the Sheet View Settings Block (➜ 4.5). # Offset Size Contents # 0 2 Option flags: # Bits Mask Contents # 0 0x0001 0 = Show formula results # 1 = Show formulas # 1 0x0002 0 = Do not show grid lines # 1 = Show grid lines # 2 0x0004 0 = Do not show sheet headers # 1 = Show sheet headers # 3 0x0008 0 = Panes are not frozen # 1 = Panes are frozen (freeze) # 4 0x0010 0 = Show zero values as empty cells # 1 = Show zero values # 5 0x0020 0 = Manual grid line colour # 1 = Automatic grid line colour # 6 0x0040 0 = Columns from left to right # 1 = Columns from right to left # 7 0x0080 0 = Do not show outline symbols # 1 = Show outline symbols # 8 0x0100 0 = Keep splits if pane freeze is removed # 1 = Remove splits if pane freeze is removed # 9 0x0200 0 = Sheet not selected # 1 = Sheet selected (BIFF5-BIFF8) # 10 0x0400 0 = Sheet not active # 1 = Sheet active (BIFF5-BIFF8) # 11 0x0800 0 = Show in normal view # 1 = Show in page break preview (BIFF8) # 2 2 Index to first visible row # 4 2 Index to first visible column # 6 2 Colour index of grid line colour (➜ 5.74). # Note that in BIFF2-BIFF5 an RGB colour is written instead. # 8 2 Not used # 10 2 Cached magnification factor in page break preview (in percent) # 0 = Default (60%) # 12 2 Cached magnification factor in normal view (in percent) # 0 = Default (100%) # 14 4 Not used 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 previous_op = nil while tuple = get_next_chunk pos, op, len, work = tuple case op when @bof, :bof # ● BOF Type = worksheet (➜ 6.8) return when :eof # ● EOF ➜ 6.36 postread_workbook return when :datemode # ○ DATEMODE ➜ 6.25 flag, _ = work.unpack 'v' if flag == 1 @workbook.date_base = DateTime.new 1904, 1, 1 else @workbook.date_base = DateTime.new 1899, 12, 31 end when :continue # ○ CONTINUE ➜ 6.22 case previous_op when :sst # ● SST ➜ 6.96 continue_sst work, pos, len end when :codepage # ○ CODEPAGE ➜ 6.17 read_codepage work, pos, len when :boundsheet # ●● BOUNDSHEET ➜ 6.12 read_boundsheet work, pos, len when :xf # ●● XF ➜ 6.115 read_xf work, pos, len when :sst # ○ Shared String Table ➜ 5.11 # ● SST ➜ 6.96 read_sst work, pos, len # TODO: implement memory-efficient sst handling, possibly in conjunction # with EXTSST when :extsst # ● EXTSST ➜ 6.40 read_extsst work, pos, len when :style # ●● STYLE ➜ 6.99 read_style work, pos, len when :format # ○○ FORMAT (Number Format) ➜ 6.45 read_format work, pos, len when :font # ●● FONT ➜ 6.43 read_font work, pos, len end previous_op = op unless op == :continue end end 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 :uncalced # ○ UNCALCED ➜ 6.104 # TODO: Formula support. Values were not calculated before saving #warn <<-EOS # Some fields containig formulas were saved without a computed value. # Support Spreadsheet::Excel by implementing formula-calculations! #EOS #when :index # ○ INDEX ➜ 5.7 (Row Blocks), ➜ 6.55 # TODO: if there are changes in rows, omit index when writing #read_index worksheet, work, pos, len when :guts # GUTS 5.53 read_guts worksheet, work, pos, len when :colinfo # ○○ COLINFO ➜ 6.18 read_colinfo worksheet, work, pos, len when :dimensions # ● DIMENSIONS ➜ 6.31 read_dimensions worksheet, work, pos, len when :row # ○○ Row Blocks ➜ 5.7 # ● ROW ➜ 6.83 set_row_address worksheet, work, pos, len when :hlink 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 when :protect, :password read_sheet_protection worksheet, op, work when :pagesetup read_pagesetup(worksheet, work, pos, len) when :leftmargin worksheet.margins[:left] = work.unpack(binfmt(:margin))[0] when :rightmargin worksheet.margins[:right] = work.unpack(binfmt(:margin))[0] when :topmargin worksheet.margins[:top] = work.unpack(binfmt(:margin))[0] when :bottommargin worksheet.margins[:bottom] = work.unpack(binfmt(:margin))[0] else if ROW_BLOCK_OPS.include?(op) set_missing_row_address worksheet, work, pos, len end end previous = op end end def read_pagesetup(worksheet, work, pos, len) worksheet.pagesetup.delete_if { true } data = work.unpack(binfmt(:pagesetup)) worksheet.pagesetup[:orientation] = data[5] == 0 ? :landscape : :portrait worksheet.pagesetup[:adjust_to] = data[1] worksheet.pagesetup[:orig_data] = data # TODO: add options acording to specification 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) 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 def read_style work, pos, len # User-Defined Cell Styles: # Offset Size Contents # 0 2 Bit Mask Contents # 11-0 0x0fff Index to style XF record (➜ 6.115) # 15 0x8000 Always 0 for user-defined styles # 2 var. BIFF2-BIFF7: Non-empty byte string, # 8-bit string length (➜ 3.3) # BIFF8: Non-empty Unicode string, # 16-bit string length (➜ 3.4) # # Built-In Cell Styles # Offset Size Contents # 0 2 Bit Mask Contents # 11-0 0x0FFF Index to style XF record (➜ 6.115) # 15 0x8000 Always 1 for built-in styles # 2 1 Identifier of the built-in cell style: # 0x00 = Normal # 0x01 = RowLevel_lv (see next field) # 0x02 = ColLevel_lv (see next field) # 0x03 = Comma # 0x04 = Currency # 0x05 = Percent # 0x06 = Comma [0] (BIFF4-BIFF8) # 0x07 = Currency [0] (BIFF4-BIFF8) # 0x08 = Hyperlink (BIFF8) # 0x09 = Followed Hyperlink (BIFF8) # 3 1 Level for RowLevel or ColLevel style (zero-based, lv), # FFH otherwise flags, = work.unpack 'v' xf_idx = flags & 0x0fff xf = @workbook.format xf_idx builtin = flags & 0x8000 if builtin == 0 xf.name = client read_string(work[2..-1], 2), @workbook.encoding else id, level = work.unpack 'x2C2' if name = BUILTIN_STYLES[id] name.sub '_lv', "_#{level.to_s}" xf.name = client name, 'UTF-8' end end end def read_xf work, pos, len # Offset Size Contents # 0 2 Index to FONT record (➜ 6.43) # 2 2 Index to FORMAT record (➜ 6.45) # 4 2 Bit Mask Contents # 2-0 0x0007 XF_TYPE_PROT – XF type, cell protection # Bit Mask Contents # 0 0x01 1 = Cell is locked # 1 0x02 1 = Formula is hidden # 2 0x04 0 = Cell XF; 1 = Style XF # 15-4 0xfff0 Index to parent style XF # (always 0xfff in style XFs) # 6 1 Bit Mask Contents # 2-0 0x07 XF_HOR_ALIGN – Horizontal alignment # Value Horizontal alignment # 0x00 General # 0x01 Left # 0x02 Centred # 0x03 Right # 0x04 Filled # 0x05 Justified (BIFF4-BIFF8X) # 0x06 Centred across selection # (BIFF4-BIFF8X) # 0x07 Distributed (BIFF8X) # 3 0x08 1 = Text is wrapped at right border # 6-4 0x70 XF_VERT_ALIGN – Vertical alignment # Value Vertical alignment # 0x00 Top # 0x01 Centred # 0x02 Bottom # 0x03 Justified (BIFF5-BIFF8X) # 0x04 Distributed (BIFF8X) # 7 1 XF_ROTATION: Text rotation angle (see above) # Value Text rotation # 0 Not rotated # 1-90 1 to 90 degrees counterclockwise # 91-180 1 to 90 degrees clockwise # 255 Letters are stacked top-to-bottom, # but not rotated # 8 1 Bit Mask Contents # 3-0 0x0f Indent level # 4 0x10 1 = Shrink content to fit into cell # 5 0x40 1 = Merge Range (djberger) # 7-6 0xc0 Text direction (BIFF8X only) # 0 = According to context # 1 = Left-to-right # 2 = Right-to-left # 9 1 Bit Mask Contents # 7-2 0xfc XF_USED_ATTRIB – Used attributes # Each bit describes the validity of a # specific group of attributes. In cell XFs # a cleared bit means the attributes of the # parent style XF are used (but only if the # attributes are valid there), a set bit # means the attributes of this XF are used. # In style XFs a cleared bit means the # attribute setting is valid, a set bit # means the attribute should be ignored. # Bit Mask Contents # 0 0x01 Flag for number format # 1 0x02 Flag for font # 2 0x04 Flag for horizontal and # vertical alignment, text wrap, # indentation, orientation, # rotation, and text direction # 3 0x08 Flag for border lines # 4 0x10 Flag for background area style # 5 0x20 Flag for cell protection (cell # locked and formula hidden) # 10 4 Cell border lines and background area: # Bit Mask Contents # 3- 0 0x0000000f Left line style (➜ 3.10) # 7- 4 0x000000f0 Right line style (➜ 3.10) # 11- 8 0x00000f00 Top line style (➜ 3.10) # 15-12 0x0000f000 Bottom line style (➜ 3.10) # 22-16 0x007f0000 Colour index (➜ 6.70) # for left line colour # 29-23 0x3f800000 Colour index (➜ 6.70) # for right line colour # 30 0x40000000 1 = Diagonal line # from top left to right bottom # 31 0x80000000 1 = Diagonal line # from bottom left to right top # 14 4 Bit Mask Contents # 6- 0 0x0000007f Colour index (➜ 6.70) # for top line colour # 13- 7 0x00003f80 Colour index (➜ 6.70) # for bottom line colour # 20-14 0x001fc000 Colour index (➜ 6.70) # for diagonal line colour # 24-21 0x01e00000 Diagonal line style (➜ 3.10) # 31-26 0xfc000000 Fill pattern (➜ 3.11) # 18 2 Bit Mask Contents # 6-0 0x007f Colour index (➜ 6.70) # for pattern colour # 13-7 0x3f80 Colour index (➜ 6.70) # for pattern background fmt = Format.new font_idx, numfmt, _, xf_align, xf_rotation, xf_indent, _, xf_borders, xf_brdcolors, xf_pattern = work.unpack binfmt(:xf) fmt.number_format = @formats[numfmt] ## this appears to be undocumented: the first 4 fonts seem to be accessed # with a 0-based index, but all subsequent font indices are 1-based. fmt.font = @workbook.font(font_idx > 3 ? font_idx - 1 : font_idx) fmt.horizontal_align = NGILA_H_FX[xf_align & 0x07] fmt.text_wrap = xf_align & 0x08 > 0 fmt.vertical_align = NGILA_V_FX[xf_align & 0x70] fmt.rotation = if xf_rotation == 255 :stacked elsif xf_rotation > 90 90 - xf_rotation else xf_rotation end fmt.indent_level = xf_indent & 0x0f fmt.shrink = xf_indent & 0x10 > 0 fmt.text_direction = NOITCERID_TXET_FX[xf_indent & 0xc0] fmt.left = XF_BORDER_LINE_STYLES[xf_borders & 0x0000000f] fmt.right = XF_BORDER_LINE_STYLES[(xf_borders & 0x000000f0) >> 4] fmt.top = XF_BORDER_LINE_STYLES[(xf_borders & 0x00000f00) >> 8] fmt.bottom = XF_BORDER_LINE_STYLES[(xf_borders & 0x0000f000) >> 12] fmt.left_color = COLOR_CODES[(xf_borders & 0x007f0000) >> 16] || :black fmt.right_color = COLOR_CODES[(xf_borders & 0x3f800000) >> 23] || :black fmt.cross_down = xf_borders & 0x40000000 > 0 fmt.cross_up = xf_borders & 0x80000000 > 0 if xf_brdcolors fmt.top_color = COLOR_CODES[xf_brdcolors & 0x0000007f] || :black fmt.bottom_color = COLOR_CODES[(xf_brdcolors & 0x00003f80) >> 7] || :black fmt.diagonal_color = COLOR_CODES[(xf_brdcolors & 0x001fc000) >> 14] || :black #fmt.diagonal_style = COLOR_CODES[xf_brdcolors & 0x01e00000] fmt.pattern = (xf_brdcolors & 0xfc000000) >> 26 end fmt.pattern_fg_color = COLOR_CODES[xf_pattern & 0x007f] || :border fmt.pattern_bg_color = COLOR_CODES[(xf_pattern & 0x3f80) >> 7] || :pattern_bg @workbook.add_format fmt end def read_sheet_protection worksheet, op, data case op when :protect worksheet.protect! if data.unpack('v').first == 1 when :password worksheet.password_hash = data.unpack('v').first end end def set_cell worksheet, row, column, xf, value=nil cells = @current_row_block[[worksheet, row]] ||= Row.new(nil, row) cells.formats[column] = @workbook.format(xf) unless xf == 0 cells[column] = value end def set_missing_row_address worksheet, work, pos, len # Offset Size Contents # 0 2 Index of this row # 2 2 Index to this column row_index, _ = work.unpack 'v2' unless worksheet.offsets[row_index] @current_row_block_offset ||= [pos] data = { :index => row_index, :row_block => @current_row_block_offset, :offset => @current_row_block_offset[0], } worksheet.set_row_address row_index, data end end def set_row_address worksheet, work, pos, len # Offset Size Contents # 0 2 Index of this row # 2 2 Index to column of the first cell which # is described by a cell record # 4 2 Index to column of the last cell which is # described by a cell record, increased by 1 # 6 2 Bit Mask Contents # 14-0 0x7fff Height of the row, in twips = 1/20 of a point # 15 0x8000 0 = Row has custom height; # 1 = Row has default height # 8 2 Not used # 10 2 In BIFF3-BIFF4 this field contains a relative offset to # calculate stream position of the first cell record for this # row (➜ 5.7.1). In BIFF5-BIFF8 this field is not used # anymore, but the DBCELL record (➜ 6.26) instead. # 12 4 Option flags and default row formatting: # Bit Mask Contents # 2-0 0x00000007 Outline level of the row # 4 0x00000010 1 = Outline group starts or ends here # (depending on where the outline # buttons are located, see WSBOOL # record, ➜ 6.113), and is collapsed # 5 0x00000020 1 = Row is hidden (manually, or by a # filter or outline group) # 6 0x00000040 1 = Row height and default font height # do not match # 7 0x00000080 1 = Row has explicit default format (fl) # 8 0x00000100 Always 1 # 27-16 0x0fff0000 If fl = 1: Index to default XF record # (➜ 6.115) # 28 0x10000000 1 = Additional space above the row. # This flag is set, if the upper # border of at least one cell in this # row or if the lower border of at # least one cell in the row above is # formatted with a thick line style. # Thin and medium line styles are not # taken into account. # 29 0x20000000 1 = Additional space below the row. # This flag is set, if the lower # border of at least one cell in this # row or if the upper border of at # least one cell in the row below is # formatted with a medium or thick # line style. Thin line styles are # not taken into account. @current_row_block_offset ||= [pos] index, first_used, first_unused, height, flags = work.unpack binfmt(:row) height &= 0x7fff format = nil # TODO: read attributes from work[13,3], read flags attrs = { :default_format => format, :first_used => first_used, :first_unused => first_unused, :index => index, :row_block => @current_row_block_offset, :offset => @current_row_block_offset[0], :outline_level => flags & 0x00000007, :collapsed => (flags & 0x0000010) > 0, :hidden => (flags & 0x0000020) > 0, } if (flags & 0x00000040) > 0 attrs.store :height, height / TWIPS end if (flags & 0x00000080) > 0 xf = (flags & 0x0fff0000) >> 16 attrs.store :default_format, @workbook.format(xf) end # TODO: Row spacing worksheet.set_row_address index, attrs end def setup io ## Reading from StringIO fails without forced encoding if io.respond_to?(:string) && (str = io.string) \ && str.respond_to?(:force_encoding) str.force_encoding 'ASCII-8BIT' end ## io.rewind @ole = Ole::Storage.open io @workbook = Workbook.new io, {} %w{Book Workbook BOOK WORKBOOK book workbook}.any? do |name| @book = @ole.file.open(name) rescue false end raise RuntimeError, "could not locate a workbook, possibly an empty file passed" unless @book @data = @book.read read_bof @workbook.ole = @book @workbook.bof = @bof @workbook.version = @version biff = @workbook.biff_version extend_reader biff extend_internals biff end private def extend_internals version require 'spreadsheet/excel/internals/biff%i' % version extend Internals.const_get('Biff%i' % version) ## spreadsheets may not include a codepage record. @workbook.encoding = encoding 850 if version < 8 rescue LoadError end def extend_reader version require 'spreadsheet/excel/reader/biff%i' % version extend Reader.const_get('Biff%i' % version) rescue LoadError end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/writer/0000755000004100000410000000000012216006760021550 5ustar www-datawww-dataspreadsheet-0.9.0/lib/spreadsheet/excel/writer/biff8.rb0000644000004100000410000000536312216006760023102 0ustar www-datawww-datarequire 'spreadsheet/encodings' module Spreadsheet module Excel module Writer ## # This Module collects writer methods such as unicode_string that are specific # to Biff8. This Module is likely to be expanded as Support for older Versions # of Excel grows and methods get moved here for disambiguation. module Biff8 include Spreadsheet::Encodings ## # Check whether the string _data_ can be compressed (i.e. every second byte # is a Null-byte) and perform compression. # Returns the data and compression_status (0/1) def compress_unicode_string data compressed = internal('') expect_null = false data.each_byte do |byte| if expect_null if byte != 0 return [data, 1] # 1 => Data consists of wide Chars end expect_null = false else compressed << byte expect_null = true end end [compressed, 0] # 0 => Data consists of compressed Chars end ## # Encode _string_ into a Biff8 Unicode String. Header and body are encoded # separately by #_unicode_string. This method simply combines the two. def unicode_string string, count_length=1 header, data, _ = _unicode_string string, count_length header << data end @@bytesize = RUBY_VERSION >= '1.9' ? :bytesize : :size ## # Encode _string_ into a Biff8 Unicode String Header and Body. def _unicode_string string, count_length=1 data = internal string size = data.send(@@bytesize) / 2 fmt = count_length == 1 ? 'C2' : 'vC' data, wide = compress_unicode_string data opts = wide header = [ size, # Length of the string (character count, ln) opts, # Option flags: # Bit Mask Contents # 0 0x01 Character compression (ccompr): # 0 = Compressed (8-bit characters) # 1 = Uncompressed (16-bit characters) # 2 0x04 Asian phonetic settings (phonetic): # 0 = Does not contain Asian phonetic settings # 1 = Contains Asian phonetic settings # 3 0x08 Rich-Text settings (richtext): # 0 = Does not contain Rich-Text settings # 1 = Contains Rich-Text settings #0x00,# (optional, only if richtext=1) Number of Rich-Text # formatting runs (rt) #0x00,# (optional, only if phonetic=1) Size of Asian phonetic # settings block (in bytes, sz) ].pack fmt data << '' # (optional, only if richtext=1) # List of rt formatting runs (➜ 3.2) data << '' # (optional, only if phonetic=1) # Asian Phonetic Settings Block (➜ 3.4.2) [header, data, wide] end end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/writer/worksheet.rb0000644000004100000410000010273612216006760024121 0ustar www-datawww-datarequire 'stringio' require 'spreadsheet/excel/writer/biff8' require 'spreadsheet/excel/internals' require 'spreadsheet/excel/internals/biff8' require 'bigdecimal' module Spreadsheet module Excel module Writer ## # Writer class for Excel Worksheets. Most write_* method correspond to an # Excel-Record/Opcode. You should not need to call any of its methods directly. # If you think you do, look at #write_worksheet class Worksheet include Spreadsheet::Excel::Writer::Biff8 include Spreadsheet::Excel::Internals include Spreadsheet::Excel::Internals::Biff8 attr_reader :worksheet def initialize workbook, worksheet @workbook = workbook @worksheet = worksheet @io = StringIO.new '' @biff_version = 0x0600 @bof = 0x0809 @build_id = 3515 @build_year = 1996 @bof_types = { :globals => 0x0005, :visual_basic => 0x0006, :worksheet => 0x0010, :chart => 0x0020, :macro_sheet => 0x0040, :workspace => 0x0100, } end ## # The number of bytes needed to write a Boundsheet record for this Worksheet # Used by Writer::Worksheet to calculate various offsets. def boundsheet_size name.size + 10 end def data @io.rewind @io.read end def encode_date date return date if date.is_a? Numeric if date.is_a? Time date = DateTime.new date.year, date.month, date.day, date.hour, date.min, date.sec end base = @workbook.date_base value = date - base if LEAP_ERROR > base value += 1 end value end def encode_rk value # Bit Mask Contents # 0 0x00000001 0 = Value not changed 1 = Value is multiplied by 100 # 1 0x00000002 0 = Floating-point value 1 = Signed integer value # 31-2 0xFFFFFFFC Encoded value cent = 0 int = 2 higher = value * 100 if (higher.is_a?(BigDecimal) or higher.is_a?(Float)) && higher < 0xfffffffc cent = 1 if higher == higher.to_i value = higher.to_i else value = higher end end if value.is_a?(Integer) ## although not documented as signed, 'V' appears to correctly pack # negative numbers. value <<= 2 else # FIXME: precision of small numbers int = 0 value, = [value].pack(EIGHT_BYTE_DOUBLE).unpack('x4V') value &= 0xfffffffc end value | cent | int end def name unicode_string @worksheet.name end def need_number? cell if cell.is_a?(Numeric) && cell.abs > 0x1fffffff true elsif (cell.is_a?(BigDecimal) or cell.is_a?(Float)) and not cell.nan? higher = cell * 100 if higher == higher.to_i need_number? higher.to_i else test1, test2 = [cell * 100].pack(EIGHT_BYTE_DOUBLE).unpack('V2') test1 > 0 || need_number?(test2) end else false end end def row_blocks # All cells in an Excel document are divided into blocks of 32 consecutive # rows, called Row Blocks. The first Row Block starts with the first used # row in that sheet. Inside each Row Block there will occur ROW records # describing the properties of the rows, and cell records with all the cell # contents in this Row Block. blocks = [] @worksheet.reject do |row| row.empty? end.each_with_index do |row, idx| blocks << [] if idx % 32 == 0 blocks.last << row end blocks end def size @io.size end def strings @worksheet.inject(Hash.new(0)) do |memo, row| row.each do |cell| memo[cell] += 1 if (cell.is_a?(String) && !cell.empty?) end memo end end ## # Write a blank cell def write_blank row, idx write_cell :blank, row, idx end def write_bof data = [ @biff_version, # BIFF version (always 0x0600 for BIFF8) 0x0010, # Type of the following data: # 0x0005 = Workbook globals # 0x0006 = Visual Basic module # 0x0010 = Worksheet # 0x0020 = Chart # 0x0040 = Macro sheet # 0x0100 = Workspace file @build_id, # Build identifier @build_year, # Build year 0x000, # File history flags 0x006, # Lowest Excel version that can read # all records in this file ] write_op @bof, data.pack("v4V2") end ## # Write a cell with a Boolean or Error value def write_boolerr row, idx value = row[idx] type = 0 numval = 0 if value.is_a? Error type = 1 numval = value.code elsif value numval = 1 end data = [ numval, # Boolean or error value (type depends on the following byte) type # 0 = Boolean value; 1 = Error code ] write_cell :boolerr, row, idx, *data end def write_calccount count = 100 # Maximum number of iterations allowed in circular references write_op 0x000c, [count].pack('v') end def write_cell type, row, idx, *args xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx) data = [ row.idx, # Index to row idx, # Index to column xf_idx, # Index to XF record (➜ 6.115) ].concat args write_op opcode(type), data.pack(binfmt(type)) end def write_cellblocks row # BLANK ➜ 6.7 # BOOLERR ➜ 6.10 # INTEGER ➜ 6.56 (BIFF2 only) # LABEL ➜ 6.59 (BIFF2-BIFF7) # LABELSST ➜ 6.61 (BIFF8 only) # MULBLANK ➜ 6.64 (BIFF5-BIFF8) # MULRK ➜ 6.65 (BIFF5-BIFF8) # NUMBER ➜ 6.68 # RK ➜ 6.82 (BIFF3-BIFF8) # RSTRING ➜ 6.84 (BIFF5/BIFF7) multiples, first_idx = nil row = row.formatted row.each_with_index do |cell, idx| cell = nil if cell == '' ## it appears that there are limitations to RK precision, both for # Integers and Floats, that lie well below 2^30 significant bits, or # Ruby's Bignum threshold. In that case we'll just write a Number # record need_number = need_number? cell if multiples && (!multiples.last.is_a?(cell.class) || need_number) write_multiples row, first_idx, multiples multiples, first_idx = nil end nxt = idx + 1 case cell when NilClass if multiples multiples.push cell elsif nxt < row.size && row[nxt].nil? multiples = [cell] first_idx = idx else write_blank row, idx end when TrueClass, FalseClass, Error write_boolerr row, idx when String write_labelsst row, idx when Numeric ## RK encodes Floats with 30 significant bits, which is a bit more than # 10^9. Not sure what is a good rule of thumb here, but it seems that # Decimal Numbers with more than 4 significant digits are not represented # with sufficient precision by RK if need_number write_number row, idx elsif multiples multiples.push cell elsif nxt < row.size && row[nxt].is_a?(Numeric) multiples = [cell] first_idx = idx else write_rk row, idx end when Formula write_formula row, idx when Date, Time write_number row, idx end end write_multiples row, first_idx, multiples if multiples end def write_changes reader, endpos, sst_status ## FIXME this is not smart solution to update outline_level. # without this process, outlines in row disappear in MS Excel. @worksheet.row_count.times do |i| if @worksheet.row(i).outline_level > 0 @worksheet.row(i).outline_level = @worksheet.row(i).outline_level end end reader.seek @worksheet.offset blocks = row_blocks lastpos = reader.pos offsets = {} row_offsets = [] changes = @worksheet.changes @worksheet.offsets.each do |key, pair| if changes.include?(key) \ || (sst_status == :complete_update && key.is_a?(Integer)) offsets.store pair, key end end ## FIXME it may be smarter to simply write all rowblocks, instead of doing a # song-and-dance routine for every row... work = offsets.invert work.each do |key, (pos, len)| case key when Integer row_offsets.push [key, [pos, len]] when :dimensions row_offsets.push [-1, [pos, len]] end end row_offsets.sort! row_offsets.reverse! control = changes.size @worksheet.each do |row| key = row.idx if changes.include?(key) && !work.include?(key) row, pair = row_offsets.find do |idx, _| idx <= key end work.store key, pair end end if changes.size > control warn <<-EOS Your Worksheet was modified while it was being written. This should not happen. Please contact the author (hannes dot wyss at gmail dot com) with a sample file and minimal code that generates this warning. Thanks! EOS end work = work.sort_by do |key, (pos, _)| [pos, key.is_a?(Integer) ? key : -1] end work.each do |key, (pos, len)| @io.write reader.read(pos - lastpos) if pos > lastpos if key.is_a?(Integer) if block = blocks.find do |rows| rows.any? do |row| row.idx == key end end write_rowblock block blocks.delete block end else send "write_#{key}" end lastpos = pos + len reader.seek lastpos end # Necessary for outline (grouping) and hiding functions # but these below are not necessary to run # if [Row|Column]#hidden? = false and [Row|Column]#outline_level == 0 write_merged_cells write_pagesetup write_margins write_colinfos write_guts @io.write reader.read(endpos - lastpos) end 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 |= 0x1000 if col.collapsed? data = [ col.idx, # Index to first column in the range bunch.last.idx, # Index to last column in the range width.to_i, # Width of the columns in 1/256 of the width of the zero # character, using default font (first FONT record in the # file) xf_idx.to_i, # Index to XF record (➜ 6.115) for default column formatting opts, # 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 ] write_op opcode(:colinfo), data.pack(binfmt(:colinfo)) end def write_colinfos cols = @worksheet.columns bunch = [] cols.each_with_index do |column, idx| if column bunch << column if cols[idx.next] != column write_colinfo bunch bunch.clear end end end end def write_defaultrowheight data = [ 0x00, # Option flags: # Bit Mask Contents # 0 0x01 1 = Row height and default font height do not match # 1 0x02 1 = Row is hidden # 2 0x04 1 = Additional space above the row # 3 0x08 1 = Additional space below the row 0xf2, # Default height for unused rows, in twips = 1/20 of a point ] write_op 0x0225, data.pack('v2') end def write_defcolwidth # Offset Size Contents # 0 2 Column width in characters, using the width of the zero # character from default font (first FONT record in the # file). Excel adds some extra space to the default width, # depending on the default font and default font size. The # algorithm how to exactly calculate the resulting column # width is not known. # # Example: The default width of 8 set in this record results # in a column width of 8.43 using Arial font with a size of # 10 points. write_op 0x0055, [8].pack('v') end def write_dimensions # Offset Size Contents # 0 4 Index to first used row # 4 4 Index to last used row, increased by 1 # 8 2 Index to first used column # 10 2 Index to last used column, increased by 1 # 12 2 Not used write_op 0x0200, @worksheet.dimensions.pack(binfmt(:dimensions)) end def write_eof write_op 0x000a end ## # Write a cell with a Formula. May write an additional String record depending # on the stored result of the Formula. def write_formula row, idx xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx) cell = row[idx] data1 = [ row.idx, # Index to row idx, # Index to column xf_idx, # Index to XF record (➜ 6.115) ].pack 'v3' data2 = nil case value = cell.value when Numeric # IEEE 754 floating-point value (64-bit double precision) data2 = [value].pack EIGHT_BYTE_DOUBLE when String data2 = [ 0x00, # (identifier for a string value) 0xffff, # ].pack 'Cx5v' when true, false value = value ? 1 : 0 data2 = [ 0x01, # (identifier for a Boolean value) value, # 0 = FALSE, 1 = TRUE 0xffff, # ].pack 'CxCx3v' when Error data2 = [ 0x02, # (identifier for an error value) value.code, # Error code 0xffff, # ].pack 'CxCx3v' when nil data2 = [ 0x03, # (identifier for an empty cell) 0xffff, # ].pack 'Cx5v' else data2 = [ 0x02, # (identifier for an error value) 0x2a, # Error code: #N/A! Argument or function not available 0xffff, # ].pack 'CxCx3v' end opts = 0x03 opts |= 0x08 if cell.shared data3 = [ opts # Option flags: # Bit Mask Contents # 0 0x0001 1 = Recalculate always # 1 0x0002 1 = Calculate on open # 3 0x0008 1 = Part of a shared formula ].pack 'vx4' write_op opcode(:formula), data1, data2, data3, cell.data if cell.value.is_a?(String) write_op opcode(:string), unicode_string(cell.value, 2) end end ## # Write a new Worksheet. def write_from_scratch # ● BOF Type = worksheet (➜ 5.8) write_bof # ○ UNCALCED ➜ 5.105 # ○ INDEX ➜ 4.7 (Row Blocks), ➜ 5.59 # ○ Calculation Settings Block ➜ 4.3 write_calccount write_refmode write_iteration write_saverecalc # ○ PRINTHEADERS ➜ 5.81 # ○ PRINTGRIDLINES ➜ 5.80 # ○ GRIDSET ➜ 5.52 # ○ GUTS ➜ 5.53 write_guts # ○ DEFAULTROWHEIGHT ➜ 5.31 write_defaultrowheight # ○ WSBOOL ➜ 5.113 write_wsbool # ○ Page Settings Block ➜ 4.4 # ○ Worksheet Protection Block ➜ 4.18 write_proctection # ○ DEFCOLWIDTH ➜ 5.32 write_defcolwidth # ○○ COLINFO ➜ 5.18 write_colinfos # ○ SORT ➜ 5.99 # ● DIMENSIONS ➜ 5.35 write_dimensions # ○○ Row Blocks ➜ 4.7 write_rows # ● Worksheet View Settings Block ➜ 4.5 # ● WINDOW2 ➜ 5.110 write_window2 # ○ SCL ➜ 5.92 (BIFF4-BIFF8 only) # ○ PANE ➜ 5.75 # ○○ SELECTION ➜ 5.93 # ○ STANDARDWIDTH ➜ 5.101 # ○○ MERGEDCELLS ➜ 5.67 write_merged_cells # ○ LABELRANGES ➜ 5.64 # ○ PHONETIC ➜ 5.77 # ○ Conditional Formatting Table ➜ 4.12 # ○ Hyperlink Table ➜ 4.13 write_pagesetup write_margins write_hyperlink_table # ○ Data Validity Table ➜ 4.14 # ○ SHEETLAYOUT ➜ 5.96 (BIFF8X only) # ○ SHEETPROTECTION Additional protection, ➜ 5.98 (BIFF8X only) # ○ RANGEPROTECTION Additional protection, ➜ 5.84 (BIFF8X only) # ● EOF ➜ 5.36 write_eof end ## # Write record that contains information about the layout of outline symbols. def write_guts # find the maximum outline_level in rows and columns row_outline_level = 0 col_outline_level = 0 if(row = @worksheet.rows.select{|x| x!=nil}.max{|a,b| a.outline_level <=> b.outline_level}) row_outline_level = row.outline_level end if(col = @worksheet.columns.select{|x| x!=nil}.max{|a,b| a.outline_level <=> b.outline_level}) col_outline_level = col.outline_level end # set data data = [ 0, # Width of the area to display row outlines (left of the sheet), in pixel 0, # Height of the area to display column outlines (above the sheet), in pixel row_outline_level+1, # Number of visible row outline levels (used row levels+1; or 0,if not used) col_outline_level+1 # Number of visible column outline levels (used column levels+1; or 0,if not used) ] # write record write_op opcode(:guts), data.pack('v4') end def write_hlink row, col, link # FIXME: only Hyperlinks are supported at present. cell_range = [ row, row, # Cell range address of all cells containing this hyperlink col, col, # (➜ 3.13.1) ].pack 'v4' guid = [ # GUID of StdLink: # D0 C9 EA 79 F9 BA CE 11 8C 82 00 AA 00 4B A9 0B # (79EAC9D0-BAF9-11CE-8C82-00AA004BA90B) "d0c9ea79f9bace118c8200aa004ba90b", ].pack 'H32' opts = 0x01 opts |= 0x02 opts |= 0x14 unless link == link.url opts |= 0x08 if link.fragment opts |= 0x80 if link.target_frame # TODO: UNC support options = [ 2, # Unknown value: 0x00000002 opts, # Option flags # Bit Mask Contents # 0 0x00000001 0 = No link extant # 1 = File link or URL # 1 0x00000002 0 = Relative file path # 1 = Absolute path or URL # 2 and 4 0x00000014 0 = No description # 1 (both bits) = Description # 3 0x00000008 0 = No text mark # 1 = Text mark # 7 0x00000080 0 = No target frame # 1 = Target frame # 8 0x00000100 0 = File link or URL # 1 = UNC path (incl. server name) ].pack('V2') tail = [] ## call internal to get the correct internal encoding in Ruby 1.9 nullstr = internal "\000" unless link == link.url desc = internal(link).dup << nullstr tail.push [desc.size / 2].pack('V'), desc end if link.target_frame frme = internal(link.target_frame).dup << nullstr tail.push [frme.size / 2].pack('V'), frme end url = internal(link.url).dup << nullstr tail.push [ # 6.53.2 Hyperlink containing a URL (Uniform Resource Locator) # These data fields occur for links which are not local files or files # in the local network (for instance HTTP and FTP links and e-mail # addresses). The lower 9 bits of the option flags field must be # 0.x00x.xx112 (x means optional, depending on hyperlink content). The # GUID could be used to distinguish a URL from a file link. # GUID of URL Moniker: # E0 C9 EA 79 F9 BA CE 11 8C 82 00 AA 00 4B A9 0B # (79EAC9E0-BAF9-11CE-8C82-00AA004BA90B) 'e0c9ea79f9bace118c8200aa004ba90b', url.size # Size of character array of the URL, including trailing zero # word (us). There are us/2-1 characters in the following # string. ].pack('H32V'), url if link.fragment frag = internal(link.fragment).dup << nullstr tail.push [frag.size / 2].pack('V'), frag end write_op opcode(:hlink), cell_range, guid, options, *tail end def write_hyperlink_table # TODO: theoretically it's possible to write fewer records by combining # identical neighboring links in cell-ranges @worksheet.each do |row| row.each_with_index do |cell, idx| if cell.is_a? Link write_hlink row.idx, idx, cell end end end end def write_iteration its = 0 # 0 = Iterations off; 1 = Iterations on write_op 0x0011, [its].pack('v') end ## # Write a cell with a String value. The String must have been stored in the # Shared String Table. def write_labelsst row, idx write_cell :labelsst, row, idx, @workbook.sst_index(self, row[idx]) end ## # Write multiple consecutive blank cells. def write_mulblank row, idx, multiples data = [ row.idx, # Index to row idx, # Index to first column (fc) ] # List of nc=lc-fc+1 16-bit indexes to XF records (➜ 6.115) multiples.each_with_index do |blank, cell_idx| xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx + cell_idx) data.push xf_idx end # Index to last column (lc) data.push idx + multiples.size - 1 write_op opcode(:mulblank), data.pack('v*') end ## # Write multiple consecutive cells with RK values (see #write_rk) def write_mulrk row, idx, multiples fmt = 'v2' data = [ row.idx, # Index to row idx, # Index to first column (fc) ] # List of nc=lc-fc+1 16-bit indexes to XF records (➜ 6.115) multiples.each_with_index do |cell, cell_idx| xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx + cell_idx) data.push xf_idx, encode_rk(cell) fmt << 'vV' end # Index to last column (lc) data.push idx + multiples.size - 1 write_op opcode(:mulrk), data.pack(fmt << 'v') end def write_multiples row, idx, multiples case multiples.last when NilClass write_mulblank row, idx, multiples when Numeric if multiples.size > 1 write_mulrk row, idx, multiples else write_rk row, idx end end end ## # Write a cell with a 64-bit double precision Float value def write_number row, idx # Offset Size Contents # 0 2 Index to row # 2 2 Index to column # 4 2 Index to XF record (➜ 6.115) # 6 8 IEEE 754 floating-point value (64-bit double precision) value = row[idx] case value when Date, Time value = encode_date(value) end write_cell :number, row, idx, value end def write_op op, *args data = args.join @io.write [op,data.size].pack("v2") @io.write data end def write_refmode # • The “RC” mode uses numeric indexes for rows and columns, for example # “R(1)C(-1)”, or “R1C1:R2C2”. # • The “A1” mode uses characters for columns and numbers for rows, for # example “B1”, or “$A$1:$B$2”. mode = 1 # 0 = RC mode; 1 = A1 mode write_op 0x000f, [mode].pack('v') end ## # Write a cell with a Numeric or Date value. def write_rk row, idx write_cell :rk, row, idx, encode_rk(row[idx]) end def write_row row # Offset Size Contents # 0 2 Index of this row # 2 2 Index to column of the first cell which # is described by a cell record # 4 2 Index to column of the last cell which is # described by a cell record, increased by 1 # 6 2 Bit Mask Contents # 14-0 0x7fff Height of the row, in twips = 1/20 of a point # 15 0x8000 0 = Row has custom height; # 1 = Row has default height # 8 2 Not used # 10 2 In BIFF3-BIFF4 this field contains a relative offset to # calculate stream position of the first cell record for this # row (➜ 5.7.1). In BIFF5-BIFF8 this field is not used # anymore, but the DBCELL record (➜ 6.26) instead. # 12 4 Option flags and default row formatting: # Bit Mask Contents # 2-0 0x00000007 Outline level of the row # 4 0x00000010 1 = Outline group starts or ends here # (depending on where the outline # buttons are located, see WSBOOL # record, ➜ 6.113), and is collapsed # 5 0x00000020 1 = Row is hidden (manually, or by a # filter or outline group) # 6 0x00000040 1 = Row height and default font height # do not match # 7 0x00000080 1 = Row has explicit default format (fl) # 8 0x00000100 Always 1 # 27-16 0x0fff0000 If fl = 1: Index to default XF record # (➜ 6.115) # 28 0x10000000 1 = Additional space above the row. # This flag is set, if the upper # border of at least one cell in this # row or if the lower border of at # least one cell in the row above is # formatted with a thick line style. # Thin and medium line styles are not # taken into account. # 29 0x20000000 1 = Additional space below the row. # This flag is set, if the lower # border of at least one cell in this # row or if the upper border of at # least one cell in the row below is # formatted with a medium or thick # line style. Thin line styles are # not taken into account. height = row.height || ROW_HEIGHT opts = row.outline_level & 0x00000007 opts |= 0x00000010 if row.collapsed? opts |= 0x00000020 if row.hidden? opts |= 0x00000040 if height != ROW_HEIGHT if fmt = row.default_format xf_idx = @workbook.xf_index @worksheet.workbook, fmt opts |= 0x00000080 opts |= xf_idx << 16 end opts |= 0x00000100 height = if height == ROW_HEIGHT (height * TWIPS).to_i | 0x8000 else height * TWIPS end attrs = [ row.idx, row.first_used, row.first_unused, height, opts] return if attrs.any?(&:nil?) # TODO: Row spacing data = attrs.pack binfmt(:row) write_op opcode(:row), data end def write_rowblock block # ●● ROW Properties of the used rows # ○○ Cell Block(s) Cell records for all used cells # ○ DBCELL Stream offsets to the cell records of each row block.each do |row| write_row row end block.each do |row| write_cellblocks row end end def write_rows row_blocks.each do |block| write_rowblock block end end def write_saverecalc # 0 = Do not recalculate; 1 = Recalculate before saving the document write_op 0x005f, [1].pack('v') end def write_window2 # This record contains additional settings for the document window # (BIFF2-BIFF4) or for the window of a specific worksheet (BIFF5-BIFF8). # It is part of the Sheet View Settings Block (➜ 4.5). # Offset Size Contents # 0 2 Option flags: # Bits Mask Contents # 0 0x0001 0 = Show formula results # 1 = Show formulas # 1 0x0002 0 = Do not show grid lines # 1 = Show grid lines # 2 0x0004 0 = Do not show sheet headers # 1 = Show sheet headers # 3 0x0008 0 = Panes are not frozen # 1 = Panes are frozen (freeze) # 4 0x0010 0 = Show zero values as empty cells # 1 = Show zero values # 5 0x0020 0 = Manual grid line colour # 1 = Automatic grid line colour # 6 0x0040 0 = Columns from left to right # 1 = Columns from right to left # 7 0x0080 0 = Do not show outline symbols # 1 = Show outline symbols # 8 0x0100 0 = Keep splits if pane freeze is removed # 1 = Remove splits if pane freeze is removed # 9 0x0200 0 = Sheet not selected # 1 = Sheet selected (BIFF5-BIFF8) # 10 0x0400 0 = Sheet not active # 1 = Sheet active (BIFF5-BIFF8) # 11 0x0800 0 = Show in normal view # 1 = Show in page break preview (BIFF8) # 2 2 Index to first visible row # 4 2 Index to first visible column # 6 2 Colour index of grid line colour (➜ 5.74). # Note that in BIFF2-BIFF5 an RGB colour is written instead. # 8 2 Not used # 10 2 Cached magnification factor in page break preview (in percent) # 0 = Default (60%) # 12 2 Cached magnification factor in normal view (in percent) # 0 = Default (100%) # 14 4 Not used 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, # but if [Row|Column]#outline_level = 0 the symbols are not shown. 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_pagesetup return unless @worksheet.pagesetup data = @worksheet.pagesetup[:orig_data].dup if @worksheet.pagesetup[:orientation] data[5] = @worksheet.pagesetup[:orientation] == :landscape ? 0 : 2 end if @worksheet.pagesetup[:adjust_to] data[1] = @worksheet.pagesetup[:adjust_to] end write_op opcode(:pagesetup), data.pack(binfmt(:pagesetup)) end def write_margins @worksheet.margins.each do |key, value| next unless [:left, :top, :right, :bottom].include?(key) write_op opcode(:"#{key}margin"), [value].pack(binfmt(:margin)) end end def write_proctection return unless @worksheet.protected? # ○ PROTECT Worksheet contents: 1 = protected (➜ 5.82) write_op opcode(:protect), [1].pack('v') # ○ OBJECTPROTECT Embedded objects: 1 = protected (➜ 5.72) # ○ SCENPROTECT Scenarios: 1 = protected (➜ 5.91) # ○ PASSWORD Hash value of the password; 0 = no password (➜ 5.76) write_op opcode(:password), [@worksheet.password_hash].pack('v') end def write_wsbool bits = [ # Bit Mask Contents 1, # 0 0x0001 0 = Do not show automatic page breaks # 1 = Show automatic page breaks 0, # 4 0x0010 0 = Standard sheet # 1 = Dialogue sheet (BIFF5-BIFF8) 0, # 5 0x0020 0 = No automatic styles in outlines # 1 = Apply automatic styles to outlines 1, # 6 0x0040 0 = Outline buttons above outline group # 1 = Outline buttons below outline group 1, # 7 0x0080 0 = Outline buttons left of outline group # 1 = Outline buttons right of outline group 0, # 8 0x0100 0 = Scale printout in percent (➜ 6.89) # 1 = Fit printout to number of pages (➜ 6.89) 0, # 9 0x0200 0 = Save external linked values # (BIFF3-BIFF4 only, ➜ 5.10) # 1 = Do not save external linked values # (BIFF3-BIFF4 only, ➜ 5.10) 1, # 10 0x0400 0 = Do not show row outline symbols # 1 = Show row outline symbols 0, # 11 0x0800 0 = Do not show column outline symbols # 1 = Show column outline symbols 0, # 13-12 0x3000 These flags specify the arrangement of windows. # They are stored in BIFF4 only. # 00 = Arrange windows tiled # 01 = Arrange windows horizontal 0, # 10 = Arrange windows vertical # 11 = Arrange windows cascaded # The following flags are valid for BIFF4-BIFF8 only: 0, # 14 0x4000 0 = Standard expression evaluation # 1 = Alternative expression evaluation 0, # 15 0x8000 0 = Standard formula entries # 1 = Alternative formula entries ] weights = [4,5,6,7,8,9,10,11,12,13,14,15] value = bits.inject do |a, b| a | (b << weights.shift) end write_op 0x0081, [value].pack('v') end end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/writer/workbook.rb0000644000004100000410000006457012216006760023746 0ustar www-datawww-datarequire 'spreadsheet/excel/internals' require 'spreadsheet/writer' require 'spreadsheet/excel/writer/biff8' require 'spreadsheet/excel/writer/format' require 'spreadsheet/excel/writer/worksheet' require 'ole/storage' module Spreadsheet module Excel module Writer ## # Writer class for Excel Workbooks. Most write_* method correspond to an # Excel-Record/Opcode. Designed to be able to write several Workbooks in # parallel (just because I can't imagine why you would want to do that # doesn't mean it shouldn't be possible ;). You should not need to call any of # its methods directly. If you think you do, look at #write_workbook class Workbook < Spreadsheet::Writer include Spreadsheet::Excel::Writer::Biff8 include Spreadsheet::Excel::Internals attr_reader :fonts, :date_base def initialize *args super @biff_version = 0x0600 @bof = 0x0809 @build_id = 3515 @build_year = 1996 @bof_types = { :globals => 0x0005, :visual_basic => 0x0006, :worksheet => 0x0010, :chart => 0x0020, :macro_sheet => 0x0040, :workspace => 0x0100, } @worksheets = {} @sst = {} @recordsize_limit = 8224 @fonts = {} @formats = {} @number_formats = {} end def cleanup workbook worksheets(workbook).each do |worksheet| @sst.delete worksheet end @fonts.delete workbook @formats.delete workbook @number_formats.delete workbook @worksheets.delete workbook end def collect_formats workbook, opts={} # The default cell format is always present in an Excel file, described by # the XF record with the fixed index 15 (0-based). By default, it uses the # worksheet/workbook default cell style, described by the very first XF # record (index 0). formats = [] unless opts[:existing_document] 15.times do formats.push Format.new(self, workbook, workbook.default_format, :type => :style) end formats.push Format.new(self, workbook) end workbook.formats.each do |fmt| formats.push Format.new(self, workbook, fmt) end @formats[workbook] = { :writers => [], :xf_indexes => {} } formats.each_with_index do |fmt, idx| @formats[workbook][:writers] << fmt @formats[workbook][:xf_indexes][fmt.format] ||= idx end end def complete_sst_update? workbook stored = workbook.sst.collect do |entry| entry.content end num_total = 0 current = worksheets(workbook).inject(Hash.new(0)) do |memo, worksheet| worksheet.strings.each do |k,v| memo[k] += v num_total += v end memo end current.delete '' if !stored.empty? && stored.all?{|x| current.include?(x) } ## if all previously stored strings are still needed, we don't have to # rewrite all cells because the sst-index of such string does not change. additions = current.keys - stored [:partial_update, num_total, stored + additions] else [:complete_update, num_total, current.keys] end end def font_index workbook, font_key idx = @fonts[workbook][font_key] || 0 ## this appears to be undocumented: the first 4 fonts seem to be accessed # with a 0-based index, but all subsequent font indices are 1-based. idx > 3 ? idx.next : idx end def number_format_index workbook, format @number_formats[workbook][format] || 0 end def sanitize_worksheets sheets return sheets if sheets.empty? found_selected = false sheets.each do |sheet| found_selected ||= sheet.selected sheet.format_dates! end unless found_selected sheets.first.selected = true end sheets end def worksheets workbook @worksheets[workbook] ||= workbook.worksheets.collect do |worksheet| Excel::Writer::Worksheet.new self, worksheet end end def write_bof workbook, writer, type data = [ @biff_version, # BIFF version (always 0x0600 for BIFF8) @bof_types[type], # Type of the following data: # 0x0005 = Workbook globals # 0x0006 = Visual Basic module # 0x0010 = Worksheet # 0x0020 = Chart # 0x0040 = Macro sheet # 0x0100 = Workspace file @build_id, # Build identifier @build_year, # Build year 0x000, # File history flags 0x006, # Lowest Excel version that can read # all records in this file ] write_op writer, @bof, data.pack("v4V2") end def write_bookbool workbook, writer write_placeholder writer, 0x00da end def write_boundsheets workbook, writer, offset worksheets = worksheets(workbook) worksheets.each do |worksheet| # account for boundsheet-entry offset += worksheet.boundsheet_size end worksheets.each do |worksheet| data = [ offset, # Absolute stream position of the BOF record of the sheet # represented by this record. This field is never encrypted # in protected files. 0x00, # Visibility: 0x00 = Visible # 0x01 = Hidden # 0x02 = Strong hidden (see below) 0x00, # Sheet type: 0x00 = Worksheet # 0x02 = Chart # 0x06 = Visual Basic module ] write_op writer, 0x0085, data.pack("VC2"), worksheet.name offset += worksheet.size end end ## # Copy unchanged data verbatim, adjust offsets and write new records for # changed data. def write_changes workbook, io sanitize_worksheets workbook.worksheets collect_formats workbook, :existing_document => true reader = workbook.ole sheet_data = {} sst_status, sst_total, sst_strings = complete_sst_update? workbook sst = {} sst_strings.each_with_index do |str, idx| sst.store str, idx end sheets = worksheets(workbook) positions = [] newsheets = [] sheets.each do |sheet| @sst[sheet] = sst pos, len = workbook.offsets[sheet.worksheet] if pos positions.push pos sheet.write_changes reader, pos + len, sst_status else newsheets.push sheet sheet.write_from_scratch end sheet_data[sheet.worksheet] = sheet.data end Ole::Storage.open io do |ole| ole.file.open 'Workbook', 'w' do |writer| reader.seek lastpos = 0 workbook.offsets.select do |key, pair| workbook.changes.include? key end.sort_by do |key, (pos, _)| pos end.each do |key, (pos, len)| data = reader.read(pos - lastpos) writer.write data case key when Spreadsheet::Worksheet writer.write sheet_data[key] when :boundsheets ## boundsheets are hard to calculate. The offset below is only # correct if there are no more changes in the workbook globals # string after this. oldoffset = positions.min - len lastpos = pos + len bytechange = 0 buffer = StringIO.new '' if tuple = workbook.offsets[:sst] write_sst_changes workbook, buffer, writer.pos, sst_total, sst_strings pos, len = tuple if offset = workbook.offsets[:extsst] len += offset[1].to_i end bytechange = buffer.size - len write_boundsheets workbook, writer, oldoffset + bytechange reader.seek lastpos writer.write reader.read(pos - lastpos) buffer.rewind writer.write buffer.read elsif sst.empty? || workbook.biff_version < 8 write_boundsheets workbook, writer, oldoffset + bytechange else write_sst workbook, buffer, writer.pos write_boundsheets workbook, writer, oldoffset + buffer.size pos = lastpos len = positions.min - lastpos if len > OPCODE_SIZE reader.seek pos writer.write reader.read(len - OPCODE_SIZE) end buffer.rewind writer.write buffer.read write_eof workbook, writer end else send "write_#{key}", workbook, writer end lastpos = [pos + len, reader.size - 1].min reader.seek lastpos end writer.write reader.read newsheets.each do |sheet| writer.write sheet.data end end end end def write_datemode workbook, writer mode = @date_base.year == 1899 ? 0x00 : 0x01 data = [ mode, # 0 = Base date is 1899-Dec-31 # (the cell value 1 represents 1900-Jan-01) # 1 = Base date is 1904-Jan-01 # (the cell value 1 represents 1904-Jan-02) ] write_op writer, 0x0022, data.pack('v') end def write_dsf workbook, writer data = [ 0x00, # 0 = Only the BIFF8 “Workbook” stream is present # 1 = Additional BIFF5/BIFF7 “Book” stream is in the file ] write_op writer, 0x0161, data.pack('v') end def write_encoding workbook, writer enc = workbook.encoding || 'UTF-16LE' if RUBY_VERSION >= '1.9' && enc.is_a?(Encoding) enc = enc.name.upcase end cp = SEGAPEDOC[enc] or raise "Invalid or Unknown Codepage '#{enc}'" write_op writer, 0x0042, [cp].pack('v') end def write_eof workbook, writer write_op writer, 0x000a end def write_extsst workbook, offsets, writer header = [SST_CHUNKSIZE].pack('v') data = offsets.collect do |pair| pair.push(0).pack('Vv2') end write_op writer, 0x00ff, header, data end def write_font workbook, writer, font # TODO: Colors/Palette index size = font.size * TWIPS color = SEDOC_ROLOC[font.color] || SEDOC_ROLOC[:text] weight = FONT_WEIGHTS.fetch(font.weight, font.weight) weight = [[weight, 1000].min, 100].max esc = SEPYT_TNEMEPACSE.fetch(font.escapement, 0) underline = SEPYT_ENILREDNU.fetch(font.underline, 0) family = SEILIMAF_TNOF.fetch(font.family, 0) encoding = SGNIDOCNE_TNOF.fetch(font.encoding, 0) options = 0 options |= 0x0001 if weight > 600 options |= 0x0002 if font.italic? options |= 0x0004 if underline > 0 options |= 0x0008 if font.strikeout? options |= 0x0010 if font.outline? options |= 0x0020 if font.shadow? data = [ size, # Height of the font (in twips = 1/20 of a point) options, # Option flags: # Bit Mask Contents # 0 0x0001 1 = Characters are bold (redundant, see below) # 1 0x0002 1 = Characters are italic # 2 0x0004 1 = Characters are underlined (redundant) # 3 0x0008 1 = Characters are struck out # 4 0x0010 1 = Characters are outlined (djberger) # 5 0x0020 1 = Characters are shadowed (djberger) color, # Palette index (➜ 6.70) weight, # Font weight (100-1000). Standard values are # 0x0190 (400) for normal text and # 0x02bc (700) for bold text. esc, # Escapement type: 0x0000 = None # 0x0001 = Superscript # 0x0002 = Subscript underline,# Underline type: 0x00 = None # 0x01 = Single # 0x02 = Double # 0x21 = Single accounting # 0x22 = Double accounting family, # Font family: 0x00 = None (unknown or don't care) # 0x01 = Roman (variable width, serifed) # 0x02 = Swiss (variable width, sans-serifed) # 0x03 = Modern (fixed width, # serifed or sans-serifed) # 0x04 = Script (cursive) # 0x05 = Decorative (specialised, # e.g. Old English, Fraktur) encoding, # Character set: 0x00 = 0 = ANSI Latin # 0x01 = 1 = System default # 0x02 = 2 = Symbol # 0x4d = 77 = Apple Roman # 0x80 = 128 = ANSI Japanese Shift-JIS # 0x81 = 129 = ANSI Korean (Hangul) # 0x82 = 130 = ANSI Korean (Johab) # 0x86 = 134 = ANSI Chinese Simplified GBK # 0x88 = 136 = ANSI Chinese Traditional BIG5 # 0xa1 = 161 = ANSI Greek # 0xa2 = 162 = ANSI Turkish # 0xa3 = 163 = ANSI Vietnamese # 0xb1 = 177 = ANSI Hebrew # 0xb2 = 178 = ANSI Arabic # 0xba = 186 = ANSI Baltic # 0xcc = 204 = ANSI Cyrillic # 0xde = 222 = ANSI Thai # 0xee = 238 = ANSI Latin II (Central European) # 0xff = 255 = OEM Latin I ] name = unicode_string font.name # Font name: Unicode string, # 8-bit string length (➜ 3.4) write_op writer, opcode(:font), data.pack(binfmt(:font)), name end def write_fonts workbook, writer fonts = @fonts[workbook] = {} @formats[workbook][:writers].map{|format| format.font }.compact.uniq.each do |font| unless fonts.include?(font.key) fonts.store font.key, fonts.size write_font workbook, writer, font end end end def write_formats workbook, writer # From BIFF5 on, the built-in number formats will be omitted. The built-in # formats are dependent on the current regional settings of the operating # system. BUILTIN_FORMATS shows which number formats are used by # default in a US-English environment. All indexes from 0 to 163 are # reserved for built-in formats. # The first user-defined format starts at 164 (0xa4). formats = @number_formats[workbook] = {} BUILTIN_FORMATS.each do |idx, str| formats.store client(str, 'UTF-8'), idx end ## Ensure at least a 'GENERAL' format is written formats.delete client('GENERAL', 'UTF-8') idx = 0xa4 workbook.formats.each do |fmt| str = fmt.number_format unless formats[str] formats.store str, idx # Number format string (Unicode string, 16-bit string length, ➜ 3.4) write_op writer, opcode(:format), [idx].pack('v'), unicode_string(str, 2) idx += 1 end end end ## # Write a new Excel file. def write_from_scratch workbook, io sanitize_worksheets workbook.worksheets collect_formats workbook sheets = worksheets workbook buffer1 = StringIO.new '' # ● BOF Type = workbook globals (➜ 6.8) write_bof workbook, buffer1, :globals # ○ File Protection Block ➜ 4.19 # ○ WRITEACCESS User name (BIFF3-BIFF8, ➜ 5.112) # ○ FILESHARING File sharing options (BIFF3-BIFF8, ➜ 5.44) # ○ CODEPAGE ➜ 6.17 write_encoding workbook, buffer1 # ○ DSF ➜ 6.32 write_dsf workbook, buffer1 # ○ TABID write_tabid workbook, buffer1 # ○ FNGROUPCOUNT # ○ Workbook Protection Block ➜ 4.18 # ○ WINDOWPROTECT Window settings: 1 = protected (➜ 5.111) # ○ PROTECT Cell contents: 1 = protected (➜ 5.82) write_protect workbook, buffer1 # ○ OBJECTPROTECT Embedded objects: 1 = protected (➜ 5.72) # ○ PASSWORD Hash value of the password; 0 = No password (➜ 5.76) write_password workbook, buffer1 # ○ BACKUP ➜ 5.5 # ○ HIDEOBJ ➜ 5.56 # ● WINDOW1 ➜ 5.109 write_window1 workbook, buffer1 # ○ DATEMODE ➜ 5.28 write_datemode workbook, buffer1 # ○ PRECISION ➜ 5.79 write_precision workbook, buffer1 # ○ REFRESHALL write_refreshall workbook, buffer1 # ○ BOOKBOOL ➜ 5.9 write_bookbool workbook, buffer1 # ●● FONT ➜ 5.45 write_fonts workbook, buffer1 # ○○ FORMAT ➜ 5.49 write_formats workbook, buffer1 # ●● XF ➜ 5.115 write_xfs workbook, buffer1 # ●● STYLE ➜ 5.103 write_styles workbook, buffer1 # ○ PALETTE ➜ 5.74 write_palette workbook, buffer1 # ○ USESELFS ➜ 5.106 buffer1.rewind # ●● BOUNDSHEET ➜ 5.95 buffer2 = StringIO.new '' # ○ COUNTRY ➜ 5.22 # ○ Link Table ➜ 4.10.3 # ○○ NAME ➜ 6.66 # ○ Shared String Table ➜ 4.11 # ● SST ➜ 5.100 # ● EXTSST ➜ 5.42 write_sst workbook, buffer2, buffer1.size # ● EOF ➜ 5.37 write_eof workbook, buffer2 buffer2.rewind # worksheet data can only be assembled after write_sst sheets.each do |worksheet| worksheet.write_from_scratch end Ole::Storage.open io do |ole| ole.file.open 'Workbook', 'w' do |writer| writer.write buffer1.read write_boundsheets workbook, writer, buffer1.size + buffer2.size writer.write buffer2.read sheets.each do |worksheet| writer.write worksheet.data end end end end def write_op writer, op, *args data = args.join limited = data.slice!(0...@recordsize_limit) writer.write [op,limited.size].pack("v2") writer.write limited data end def write_password workbook, writer write_placeholder writer, 0x0013 end def write_placeholder writer, op, value=0x0000, fmt='v' write_op writer, op, [value].pack(fmt) end def write_precision workbook, writer # 0 = Use displayed values; 1 = Use real cell values write_placeholder writer, 0x000e, 0x0001 end def write_protect workbook, writer write_placeholder writer, 0x0012 end def write_refreshall workbook, writer write_placeholder writer, 0x01b7 end def write_sst workbook, writer, offset # Offset Size Contents # 0 4 Total number of strings in the workbook (see below) # 4 4 Number of following strings (nm) # 8 var. List of nm Unicode strings, 16-bit string length (➜ 3.4) num_total = 0 strings = worksheets(workbook).inject(Hash.new(0)) do |memo, worksheet| worksheet.strings.each do |k,v| memo[k] += v num_total += v end memo end _write_sst workbook, writer, offset, num_total, strings.keys end def _write_sst workbook, writer, offset, total, strings sst = {} worksheets(workbook).each do |worksheet| offset += worksheet.boundsheet_size @sst[worksheet] = sst end sst_size = strings.size data = [total, sst_size].pack 'V2' op = 0x00fc wide = 0 offsets = [] strings.each_with_index do |string, idx| sst.store string, idx op_offset = data.size + 4 if idx % SST_CHUNKSIZE == 0 offsets.push [offset + writer.pos + op_offset, op_offset] end header, packed, next_wide = _unicode_string string, 2 # the first few bytes (header + first character) must not be split must_fit = header.size + wide + 1 while data.size + must_fit > @recordsize_limit op, data, wide = write_string_part writer, op, data, wide end wide = next_wide data << header << packed end until data.empty? op, data, wide = write_string_part writer, op, data, wide end write_extsst workbook, offsets, writer end def write_sst_changes workbook, writer, offset, total, strings _write_sst workbook, writer, offset, total, strings end def write_string_part writer, op, data, wide bef = data.size ## if we're writing wide characters, we need to make sure we don't cut # characters in half if wide > 0 && data.size > @recordsize_limit remove = @recordsize_limit - bef remove -= remove % 2 rest = data.slice!(remove..-1) write_op writer, op, data data = rest else data = write_op writer, op, data end op = 0x003c # Unicode strings are split in a special way. At the beginning of each # CONTINUE record the option flags byte is repeated. Only the # character size flag will be set in this flags byte, the Rich-Text # flag and the Far-East flag are set to zero. unless data.empty? if wide == 1 # check if we can compress the rest of the string data, wide = compress_unicode_string data end data = [wide].pack('C') << data end [op, data, wide] end def write_styles workbook, writer # TODO: Style implementation. The following is simply a standard builtin # style. # TODO: User defined styles data = [ 0x8000, # Bit Mask Contents # 11- 0 0x0fff Index to style XF record (➜ 6.115) # 15 0x8000 Always 1 for built-in styles 0x00, # Identifier of the built-in cell style: # 0x00 = Normal # 0x01 = RowLevel_lv (see next field) # 0x02 = ColLevel_lv (see next field) # 0x03 = Comma # 0x04 = Currency # 0x05 = Percent # 0x06 = Comma [0] (BIFF4-BIFF8) # 0x07 = Currency [0] (BIFF4-BIFF8) # 0x08 = Hyperlink (BIFF8) # 0x09 = Followed Hyperlink (BIFF8) 0xff, # Level for RowLevel or ColLevel style (zero-based, lv), # 0xff otherwise # The RowLevel and ColLevel styles specify the formatting of # subtotal cells in a specific outline level. The level is # specified by the last field in the STYLE record. Valid values # are 0…6 for the outline levels 1…7. ] write_op writer, 0x0293, data.pack('vC2') end def write_palette workbook, writer data = default_palette workbook.palette.each do |idx, color| idx = SEDOC_ROLOC[idx] - 8 if idx.kind_of? Symbol raise "Undefined color index: #{idx}" unless data[idx] data[idx] = color end writer.write [opcode(:palette), 2 + 4 * data.size, data.size].pack('v3') writer.write data.collect { |c| c.push(0).pack('C4') }.join end def write_tabid workbook, writer write_op writer, 0x013d, [1].pack('v') end def write_window1 workbook, writer selected = workbook.worksheets.find do |sheet| sheet.selected end actidx = workbook.worksheets.index selected data = [ 0x0000, # Horizontal position of the document window # (in twips = 1/20 of a point) 0x0000, # Vertical position of the document window # (in twips = 1/20 of a point) 0x4000, # Width of the document window (in twips = 1/20 of a point) 0x2000, # Height of the document window (in twips = 1/20 of a point) 0x0038, # Option flags: # Bit Mask Contents # 0 0x0001 0 = Window is visible # 1 = Window is hidden # 1 0x0002 0 = Window is open # 1 = Window is minimised # 3 0x0008 0 = Horizontal scroll bar hidden # 1 = Horizontal scroll bar visible # 4 0x0010 0 = Vertical scroll bar hidden # 1 = Vertical scroll bar visible # 5 0x0020 0 = Worksheet tab bar hidden # 1 = Worksheet tab bar visible actidx, # Index to active (displayed) worksheet 0x0000, # Index of first visible tab in the worksheet tab bar 0x0001, # Number of selected worksheets # (highlighted in the worksheet tab bar) 0x00e5, # Width of worksheet tab bar (in 1/1000 of window width). # The remaining space is used by the horizontal scrollbar. ] write_op writer, 0x003d, data.pack('v*') end ## # The main writer method. Calls #write_from_scratch or #write_changes # depending on the class and state of _workbook_. def write_workbook workbook, io unless workbook.is_a?(Excel::Workbook) && workbook.io @date_base = Date.new 1899, 12, 31 write_from_scratch workbook, io else @date_base = workbook.date_base if workbook.changes.empty? super else write_changes workbook, io end end ensure cleanup workbook end def write_xfs workbook, writer # The default cell format is always present in an Excel file, described by # the XF record with the fixed index 15 (0-based). By default, it uses the # worksheet/workbook default cell style, described by the very first XF # record (index 0). @formats[workbook][:writers].each do |fmt| fmt.write_xf writer end end def sst_index worksheet, str @sst[worksheet][str] end def xf_index workbook, format @formats[workbook][:xf_indexes][format] || 0 end ## # Returns Excel 97+ default colour palette. def default_palette [ [0x00, 0x00, 0x00], [0xff, 0xff, 0xff], [0xff, 0x00, 0x00], [0x00, 0xff, 0x00], [0x00, 0x00, 0xff], [0xff, 0xff, 0x00], [0xff, 0x00, 0xff], [0x00, 0xff, 0xff], [0x80, 0x00, 0x00], [0x00, 0x80, 0x00], [0x00, 0x00, 0x80], [0x80, 0x80, 0x00], [0x80, 0x00, 0x80], [0x00, 0x80, 0x80], [0xc0, 0xc0, 0xc0], [0x80, 0x80, 0x80], [0x99, 0x99, 0xff], [0x99, 0x33, 0x66], [0xff, 0xff, 0xcc], [0xcc, 0xff, 0xff], [0x66, 0x00, 0x66], [0xff, 0x80, 0x80], [0x00, 0x66, 0xcc], [0xcc, 0xcc, 0xff], [0x00, 0x00, 0x80], [0xff, 0x00, 0xff], [0xff, 0xff, 0x00], [0x00, 0xff, 0xff], [0x80, 0x00, 0x80], [0x80, 0x00, 0x00], [0x00, 0x80, 0x80], [0x00, 0x00, 0xff], [0x00, 0xcc, 0xff], [0xcc, 0xff, 0xff], [0xcc, 0xff, 0xcc], [0xff, 0xff, 0x99], [0x99, 0xcc, 0xff], [0xff, 0x99, 0xcc], [0xcc, 0x99, 0xff], [0xff, 0xcc, 0x99], [0x33, 0x66, 0xff], [0x33, 0xcc, 0xcc], [0x99, 0xcc, 0x00], [0xff, 0xcc, 0x00], [0xff, 0x99, 0x00], [0xff, 0x66, 0x00], [0x66, 0x66, 0x99], [0x96, 0x96, 0x96], [0x00, 0x33, 0x66], [0x33, 0x99, 0x66], [0x00, 0x33, 0x00], [0x33, 0x33, 0x00], [0x99, 0x33, 0x00], [0x99, 0x33, 0x66], [0x33, 0x33, 0x99], [0x33, 0x33, 0x33] ] end end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/writer/n_worksheet.rb0000644000004100000410000007752312216006760024443 0ustar www-datawww-datarequire 'stringio' require 'spreadsheet/excel/writer/biff8' require 'spreadsheet/excel/internals' require 'spreadsheet/excel/internals/biff8' module Spreadsheet module Excel module Writer ## # Writer class for Excel Worksheets. Most write_* method correspond to an # Excel-Record/Opcode. You should not need to call any of its methods directly. # If you think you do, look at #write_worksheet class Worksheet include Spreadsheet::Excel::Writer::Biff8 include Spreadsheet::Excel::Internals include Spreadsheet::Excel::Internals::Biff8 attr_reader :worksheet def initialize workbook, worksheet @workbook = workbook @worksheet = worksheet @io = StringIO.new '' @biff_version = 0x0600 @bof = 0x0809 @build_id = 3515 @build_year = 1996 @bof_types = { :globals => 0x0005, :visual_basic => 0x0006, :worksheet => 0x0010, :chart => 0x0020, :macro_sheet => 0x0040, :workspace => 0x0100, } end ## # The number of bytes needed to write a Boundsheet record for this Worksheet # Used by Writer::Worksheet to calculate various offsets. def boundsheet_size name.size + 10 end def data @io.rewind @io.read end def encode_date date return date if date.is_a? Numeric if date.is_a? Time date = DateTime.new date.year, date.month, date.day, date.hour, date.min, date.sec end base = @workbook.date_base value = date - base if LEAP_ERROR > base value += 1 end value end def encode_rk value # Bit Mask Contents # 0 0x00000001 0 = Value not changed 1 = Value is multiplied by 100 # 1 0x00000002 0 = Floating-point value 1 = Signed integer value # 31-2 0xFFFFFFFC Encoded value cent = 0 int = 2 higher = value * 100 if higher.is_a?(Float) && higher < 0xfffffffc cent = 1 if higher == higher.to_i value = higher.to_i else value = higher end end if value.is_a?(Integer) ## although not documented as signed, 'V' appears to correctly pack # negative numbers. value <<= 2 else # FIXME: precision of small numbers int = 0 value, = [value].pack(EIGHT_BYTE_DOUBLE).unpack('x4V') value &= 0xfffffffc end value | cent | int end def name unicode_string @worksheet.name end def need_number? cell if cell.is_a?(Numeric) && cell.abs > 0x1fffffff true elsif cell.is_a?(Float) and not cell.nan? higher = cell * 100 if higher == higher.to_i need_number? higher.to_i else test1, test2 = [cell * 100].pack(EIGHT_BYTE_DOUBLE).unpack('V2') test1 > 0 || need_number?(test2) end else false end end def row_blocks # All cells in an Excel document are divided into blocks of 32 consecutive # rows, called Row Blocks. The first Row Block starts with the first used # row in that sheet. Inside each Row Block there will occur ROW records # describing the properties of the rows, and cell records with all the cell # contents in this Row Block. blocks = [] @worksheet.reject do |row| row.empty? end.each_with_index do |row, idx| blocks << [] if idx % 32 == 0 blocks.last << row end blocks end def size @io.size end def strings @worksheet.inject(Hash.new(0)) do |memo, row| row.each do |cell| memo[cell] += 1 if (cell.is_a?(String) && !cell.empty?) end memo end end ## # Write a blank cell def write_blank row, idx write_cell :blank, row, idx end def write_bof data = [ @biff_version, # BIFF version (always 0x0600 for BIFF8) 0x0010, # Type of the following data: # 0x0005 = Workbook globals # 0x0006 = Visual Basic module # 0x0010 = Worksheet # 0x0020 = Chart # 0x0040 = Macro sheet # 0x0100 = Workspace file @build_id, # Build identifier @build_year, # Build year 0x000, # File history flags 0x006, # Lowest Excel version that can read # all records in this file ] write_op @bof, data.pack("v4V2") end ## # Write a cell with a Boolean or Error value def write_boolerr row, idx value = row[idx] type = 0 numval = 0 if value.is_a? Error type = 1 numval = value.code elsif value numval = 1 end data = [ numval, # Boolean or error value (type depends on the following byte) type # 0 = Boolean value; 1 = Error code ] write_cell :boolerr, row, idx, *data end def write_calccount count = 100 # Maximum number of iterations allowed in circular references write_op 0x000c, [count].pack('v') end def write_cell type, row, idx, *args xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx) data = [ row.idx, # Index to row idx, # Index to column xf_idx, # Index to XF record (➜ 6.115) ].concat args write_op opcode(type), data.pack(binfmt(type)) end def write_cellblocks row # BLANK ➜ 6.7 # BOOLERR ➜ 6.10 # INTEGER ➜ 6.56 (BIFF2 only) # LABEL ➜ 6.59 (BIFF2-BIFF7) # LABELSST ➜ 6.61 (BIFF8 only) # MULBLANK ➜ 6.64 (BIFF5-BIFF8) # MULRK ➜ 6.65 (BIFF5-BIFF8) # NUMBER ➜ 6.68 # RK ➜ 6.82 (BIFF3-BIFF8) # RSTRING ➜ 6.84 (BIFF5/BIFF7) multiples, first_idx = nil row = row.formatted row.each_with_index do |cell, idx| cell = nil if cell == '' ## it appears that there are limitations to RK precision, both for # Integers and Floats, that lie well below 2^30 significant bits, or # Ruby's Bignum threshold. In that case we'll just write a Number # record need_number = need_number? cell if multiples && (!multiples.last.is_a?(cell.class) || need_number) write_multiples row, first_idx, multiples multiples, first_idx = nil end nxt = idx + 1 case cell when NilClass if multiples multiples.push cell elsif nxt < row.size && row[nxt].nil? multiples = [cell] first_idx = idx else write_blank row, idx end when TrueClass, FalseClass, Error write_boolerr row, idx when String write_labelsst row, idx when Numeric ## RK encodes Floats with 30 significant bits, which is a bit more than # 10^9. Not sure what is a good rule of thumb here, but it seems that # Decimal Numbers with more than 4 significant digits are not represented # with sufficient precision by RK if need_number write_number row, idx elsif multiples multiples.push cell elsif nxt < row.size && row[nxt].is_a?(Numeric) multiples = [cell] first_idx = idx else write_rk row, idx end when Formula write_formula row, idx when Date, Time write_number row, idx end end write_multiples row, first_idx, multiples if multiples end def write_changes reader, endpos, sst_status ## FIXME this is not smart solution to update outline_level. # without this process, outlines in row disappear in MS Excel. @worksheet.row_count.times do |i| if @worksheet.row(i).outline_level > 0 @worksheet.row(i).outline_level = @worksheet.row(i).outline_level end end reader.seek @worksheet.offset blocks = row_blocks lastpos = reader.pos offsets = {} row_offsets = [] changes = @worksheet.changes @worksheet.offsets.each do |key, pair| if changes.include?(key) \ || (sst_status == :complete_update && key.is_a?(Integer)) offsets.store pair, key end end ## FIXME it may be smarter to simply write all rowblocks, instead of doing a # song-and-dance routine for every row... work = offsets.invert work.each do |key, (pos, len)| case key when Integer row_offsets.push [key, [pos, len]] when :dimensions row_offsets.push [-1, [pos, len]] end end row_offsets.sort! row_offsets.reverse! control = changes.size @worksheet.each do |row| key = row.idx if changes.include?(key) && !work.include?(key) row, pair = row_offsets.find do |idx, _| idx <= key end work.store key, pair end end if changes.size > control warn <<-EOS Your Worksheet was modified while it was being written. This should not happen. Please contact the author (hannes dot wyss at gmail dot com) with a sample file and minimal code that generates this warning. Thanks! EOS end work = work.sort_by do |key, (pos, len)| [pos, key.is_a?(Integer) ? key : -1] end work.each do |key, (pos, len)| @io.write reader.read(pos - lastpos) if pos > lastpos if key.is_a?(Integer) if block = blocks.find do |rows| rows.any? do |row| row.idx == key end end write_rowblock block blocks.delete block end else send "write_#{key}" end lastpos = pos + len reader.seek lastpos end # Necessary for outline (grouping) and hiding functions # but these below are not necessary to run # if [Row|Column]#hidden? = false and [Row|Column]#outline_level == 0 write_colinfos write_guts @io.write reader.read(endpos - lastpos) end 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 |= 0x1000 if col.collapsed? data = [ col.idx, # Index to first column in the range bunch.last.idx, # Index to last column in the range width.to_i, # Width of the columns in 1/256 of the width of the zero # character, using default font (first FONT record in the # file) xf_idx.to_i, # Index to XF record (➜ 6.115) for default column formatting opts, # 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 ] write_op opcode(:colinfo), data.pack(binfmt(:colinfo)) end def write_colinfos cols = @worksheet.columns bunch = [] cols.each_with_index do |column, idx| if column bunch << column if cols[idx.next] != column write_colinfo bunch bunch.clear end end end end def write_defaultrowheight data = [ 0x00, # Option flags: # Bit Mask Contents # 0 0x01 1 = Row height and default font height do not match # 1 0x02 1 = Row is hidden # 2 0x04 1 = Additional space above the row # 3 0x08 1 = Additional space below the row 0xf2, # Default height for unused rows, in twips = 1/20 of a point ] write_op 0x0225, data.pack('v2') end def write_defcolwidth # Offset Size Contents # 0 2 Column width in characters, using the width of the zero # character from default font (first FONT record in the # file). Excel adds some extra space to the default width, # depending on the default font and default font size. The # algorithm how to exactly calculate the resulting column # width is not known. # # Example: The default width of 8 set in this record results # in a column width of 8.43 using Arial font with a size of # 10 points. write_op 0x0055, [8].pack('v') end def write_dimensions # Offset Size Contents # 0 4 Index to first used row # 4 4 Index to last used row, increased by 1 # 8 2 Index to first used column # 10 2 Index to last used column, increased by 1 # 12 2 Not used write_op 0x0200, @worksheet.dimensions.pack(binfmt(:dimensions)) end def write_eof write_op 0x000a end ## # Write a cell with a Formula. May write an additional String record depending # on the stored result of the Formula. def write_formula row, idx xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx) cell = row[idx] data1 = [ row.idx, # Index to row idx, # Index to column xf_idx, # Index to XF record (➜ 6.115) ].pack 'v3' data2 = nil case value = cell.value when Numeric # IEEE 754 floating-point value (64-bit double precision) data2 = [value].pack EIGHT_BYTE_DOUBLE when String data2 = [ 0x00, # (identifier for a string value) 0xffff, # ].pack 'Cx5v' when true, false value = value ? 1 : 0 data2 = [ 0x01, # (identifier for a Boolean value) value, # 0 = FALSE, 1 = TRUE 0xffff, # ].pack 'CxCx3v' when Error data2 = [ 0x02, # (identifier for an error value) value.code, # Error code 0xffff, # ].pack 'CxCx3v' when nil data2 = [ 0x03, # (identifier for an empty cell) 0xffff, # ].pack 'Cx5v' else data2 = [ 0x02, # (identifier for an error value) 0x2a, # Error code: #N/A! Argument or function not available 0xffff, # ].pack 'CxCx3v' end opts = 0x03 opts |= 0x08 if cell.shared data3 = [ opts # Option flags: # Bit Mask Contents # 0 0x0001 1 = Recalculate always # 1 0x0002 1 = Calculate on open # 3 0x0008 1 = Part of a shared formula ].pack 'vx4' write_op opcode(:formula), data1, data2, data3, cell.data if cell.value.is_a?(String) write_op opcode(:string), unicode_string(cell.value, 2) end end ## # Write a new Worksheet. def write_from_scratch # ● BOF Type = worksheet (➜ 5.8) write_bof # ○ UNCALCED ➜ 5.105 # ○ INDEX ➜ 4.7 (Row Blocks), ➜ 5.59 # ○ Calculation Settings Block ➜ 4.3 write_calccount write_refmode write_iteration write_saverecalc # ○ PRINTHEADERS ➜ 5.81 # ○ PRINTGRIDLINES ➜ 5.80 # ○ GRIDSET ➜ 5.52 # ○ GUTS ➜ 5.53 write_guts # ○ DEFAULTROWHEIGHT ➜ 5.31 write_defaultrowheight # ○ WSBOOL ➜ 5.113 write_wsbool # ○ Page Settings Block ➜ 4.4 # ○ Worksheet Protection Block ➜ 4.18 # ○ DEFCOLWIDTH ➜ 5.32 write_defcolwidth # ○○ COLINFO ➜ 5.18 write_colinfos # ○ SORT ➜ 5.99 # ● DIMENSIONS ➜ 5.35 write_dimensions # ○○ Row Blocks ➜ 4.7 write_rows # ● Worksheet View Settings Block ➜ 4.5 # ● WINDOW2 ➜ 5.110 write_window2 # ○ SCL ➜ 5.92 (BIFF4-BIFF8 only) # ○ PANE ➜ 5.75 # ○○ SELECTION ➜ 5.93 # ○ STANDARDWIDTH ➜ 5.101 # ○○ MERGEDCELLS ➜ 5.67 # ○ LABELRANGES ➜ 5.64 # ○ PHONETIC ➜ 5.77 # ○ Conditional Formatting Table ➜ 4.12 # ○ Hyperlink Table ➜ 4.13 write_hyperlink_table # ○ Data Validity Table ➜ 4.14 # ○ SHEETLAYOUT ➜ 5.96 (BIFF8X only) # ○ SHEETPROTECTION Additional protection, ➜ 5.98 (BIFF8X only) # ○ RANGEPROTECTION Additional protection, ➜ 5.84 (BIFF8X only) # ● EOF ➜ 5.36 write_eof end ## # Write record that contains information about the layout of outline symbols. def write_guts # find the maximum outline_level in rows and columns row_outline_level = 0 col_outline_level = 0 if(row = @worksheet.rows.select{|x| x!=nil}.max{|a,b| a.outline_level <=> b.outline_level}) row_outline_level = row.outline_level end if(col = @worksheet.columns.select{|x| x!=nil}.max{|a,b| a.outline_level <=> b.outline_level}) col_outline_level = col.outline_level end # set data data = [ 0, # Width of the area to display row outlines (left of the sheet), in pixel 0, # Height of the area to display column outlines (above the sheet), in pixel row_outline_level+1, # Number of visible row outline levels (used row levels+1; or 0,if not used) col_outline_level+1 # Number of visible column outline levels (used column levels+1; or 0,if not used) ] # write record write_op opcode(:guts), data.pack('v4') end def write_hlink row, col, link # FIXME: only Hyperlinks are supported at present. cell_range = [ row, row, # Cell range address of all cells containing this hyperlink col, col, # (➜ 3.13.1) ].pack 'v4' guid = [ # GUID of StdLink: # D0 C9 EA 79 F9 BA CE 11 8C 82 00 AA 00 4B A9 0B # (79EAC9D0-BAF9-11CE-8C82-00AA004BA90B) "d0c9ea79f9bace118c8200aa004ba90b", ].pack 'H32' opts = 0x01 opts |= 0x02 opts |= 0x14 unless link == link.url opts |= 0x08 if link.fragment opts |= 0x80 if link.target_frame # TODO: UNC support options = [ 2, # Unknown value: 0x00000002 opts, # Option flags # Bit Mask Contents # 0 0x00000001 0 = No link extant # 1 = File link or URL # 1 0x00000002 0 = Relative file path # 1 = Absolute path or URL # 2 and 4 0x00000014 0 = No description # 1 (both bits) = Description # 3 0x00000008 0 = No text mark # 1 = Text mark # 7 0x00000080 0 = No target frame # 1 = Target frame # 8 0x00000100 0 = File link or URL # 1 = UNC path (incl. server name) ].pack('V2') tail = [] ## call internal to get the correct internal encoding in Ruby 1.9 nullstr = internal "\000" unless link == link.url desc = internal(link).dup << nullstr tail.push [desc.size / 2].pack('V'), desc end if link.target_frame frme = internal(link.target_frame).dup << nullstr tail.push [frme.size / 2].pack('V'), frme end url = internal(link.url).dup << nullstr tail.push [ # 6.53.2 Hyperlink containing a URL (Uniform Resource Locator) # These data fields occur for links which are not local files or files # in the local network (for instance HTTP and FTP links and e-mail # addresses). The lower 9 bits of the option flags field must be # 0.x00x.xx112 (x means optional, depending on hyperlink content). The # GUID could be used to distinguish a URL from a file link. # GUID of URL Moniker: # E0 C9 EA 79 F9 BA CE 11 8C 82 00 AA 00 4B A9 0B # (79EAC9E0-BAF9-11CE-8C82-00AA004BA90B) 'e0c9ea79f9bace118c8200aa004ba90b', url.size # Size of character array of the URL, including trailing zero # word (us). There are us/2-1 characters in the following # string. ].pack('H32V'), url if link.fragment frag = internal(link.fragment).dup << nullstr tail.push [frag.size / 2].pack('V'), frag end write_op opcode(:hlink), cell_range, guid, options, *tail end def write_hyperlink_table # TODO: theoretically it's possible to write fewer records by combining # identical neighboring links in cell-ranges links = [] @worksheet.each do |row| row.each_with_index do |cell, idx| if cell.is_a? Link write_hlink row.idx, idx, cell end end end end def write_iteration its = 0 # 0 = Iterations off; 1 = Iterations on write_op 0x0011, [its].pack('v') end ## # Write a cell with a String value. The String must have been stored in the # Shared String Table. def write_labelsst row, idx write_cell :labelsst, row, idx, @workbook.sst_index(self, row[idx]) end ## # Write multiple consecutive blank cells. def write_mulblank row, idx, multiples data = [ row.idx, # Index to row idx, # Index to first column (fc) ] # List of nc=lc-fc+1 16-bit indexes to XF records (➜ 6.115) multiples.each_with_index do |blank, cell_idx| xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx + cell_idx) data.push xf_idx end # Index to last column (lc) data.push idx + multiples.size - 1 write_op opcode(:mulblank), data.pack('v*') end ## # Write multiple consecutive cells with RK values (see #write_rk) def write_mulrk row, idx, multiples fmt = 'v2' data = [ row.idx, # Index to row idx, # Index to first column (fc) ] # List of nc=lc-fc+1 16-bit indexes to XF records (➜ 6.115) multiples.each_with_index do |cell, cell_idx| xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx + cell_idx) data.push xf_idx, encode_rk(cell) fmt << 'vV' end # Index to last column (lc) data.push idx + multiples.size - 1 write_op opcode(:mulrk), data.pack(fmt << 'v') end def write_multiples row, idx, multiples case multiples.last when NilClass write_mulblank row, idx, multiples when Numeric if multiples.size > 1 write_mulrk row, idx, multiples else write_rk row, idx end end end ## # Write a cell with a 64-bit double precision Float value def write_number row, idx # Offset Size Contents # 0 2 Index to row # 2 2 Index to column # 4 2 Index to XF record (➜ 6.115) # 6 8 IEEE 754 floating-point value (64-bit double precision) value = row[idx] case value when Date, Time value = encode_date(value) end write_cell :number, row, idx, value end def write_op op, *args data = args.join @io.write [op,data.size].pack("v2") @io.write data end def write_refmode # • The “RC” mode uses numeric indexes for rows and columns, for example # “R(1)C(-1)”, or “R1C1:R2C2”. # • The “A1” mode uses characters for columns and numbers for rows, for # example “B1”, or “$A$1:$B$2”. mode = 1 # 0 = RC mode; 1 = A1 mode write_op 0x000f, [mode].pack('v') end ## # Write a cell with a Numeric or Date value. def write_rk row, idx write_cell :rk, row, idx, encode_rk(row[idx]) end def write_row row # Offset Size Contents # 0 2 Index of this row # 2 2 Index to column of the first cell which # is described by a cell record # 4 2 Index to column of the last cell which is # described by a cell record, increased by 1 # 6 2 Bit Mask Contents # 14-0 0x7fff Height of the row, in twips = 1/20 of a point # 15 0x8000 0 = Row has custom height; # 1 = Row has default height # 8 2 Not used # 10 2 In BIFF3-BIFF4 this field contains a relative offset to # calculate stream position of the first cell record for this # row (➜ 5.7.1). In BIFF5-BIFF8 this field is not used # anymore, but the DBCELL record (➜ 6.26) instead. # 12 4 Option flags and default row formatting: # Bit Mask Contents # 2-0 0x00000007 Outline level of the row # 4 0x00000010 1 = Outline group starts or ends here # (depending on where the outline # buttons are located, see WSBOOL # record, ➜ 6.113), and is collapsed # 5 0x00000020 1 = Row is hidden (manually, or by a # filter or outline group) # 6 0x00000040 1 = Row height and default font height # do not match # 7 0x00000080 1 = Row has explicit default format (fl) # 8 0x00000100 Always 1 # 27-16 0x0fff0000 If fl = 1: Index to default XF record # (➜ 6.115) # 28 0x10000000 1 = Additional space above the row. # This flag is set, if the upper # border of at least one cell in this # row or if the lower border of at # least one cell in the row above is # formatted with a thick line style. # Thin and medium line styles are not # taken into account. # 29 0x20000000 1 = Additional space below the row. # This flag is set, if the lower # border of at least one cell in this # row or if the upper border of at # least one cell in the row below is # formatted with a medium or thick # line style. Thin line styles are # not taken into account. height = row.height || ROW_HEIGHT opts = row.outline_level & 0x00000007 opts |= 0x00000010 if row.collapsed? opts |= 0x00000020 if row.hidden? opts |= 0x00000040 if height != ROW_HEIGHT if fmt = row.default_format xf_idx = @workbook.xf_index @worksheet.workbook, fmt opts |= 0x00000080 opts |= xf_idx << 16 end opts |= 0x00000100 height = if height == ROW_HEIGHT (height * TWIPS).to_i | 0x8000 else height * TWIPS end attrs = [ row.idx, row.first_used, row.first_unused, height, opts] return if attrs.any?(&:nil?) # TODO: Row spacing data = attrs.pack binfmt(:row) write_op opcode(:row), data end def write_rowblock block # ●● ROW Properties of the used rows # ○○ Cell Block(s) Cell records for all used cells # ○ DBCELL Stream offsets to the cell records of each row block.each do |row| write_row row end block.each do |row| write_cellblocks row end end def write_rows row_blocks.each do |block| write_rowblock block end end def write_saverecalc # 0 = Do not recalculate; 1 = Recalculate before saving the document write_op 0x005f, [1].pack('v') end def write_window2 # This record contains additional settings for the document window # (BIFF2-BIFF4) or for the window of a specific worksheet (BIFF5-BIFF8). # It is part of the Sheet View Settings Block (➜ 4.5). # Offset Size Contents # 0 2 Option flags: # Bits Mask Contents # 0 0x0001 0 = Show formula results # 1 = Show formulas # 1 0x0002 0 = Do not show grid lines # 1 = Show grid lines # 2 0x0004 0 = Do not show sheet headers # 1 = Show sheet headers # 3 0x0008 0 = Panes are not frozen # 1 = Panes are frozen (freeze) # 4 0x0010 0 = Show zero values as empty cells # 1 = Show zero values # 5 0x0020 0 = Manual grid line colour # 1 = Automatic grid line colour # 6 0x0040 0 = Columns from left to right # 1 = Columns from right to left # 7 0x0080 0 = Do not show outline symbols # 1 = Show outline symbols # 8 0x0100 0 = Keep splits if pane freeze is removed # 1 = Remove splits if pane freeze is removed # 9 0x0200 0 = Sheet not selected # 1 = Sheet selected (BIFF5-BIFF8) # 10 0x0400 0 = Sheet not active # 1 = Sheet active (BIFF5-BIFF8) # 11 0x0800 0 = Show in normal view # 1 = Show in page break preview (BIFF8) # 2 2 Index to first visible row # 4 2 Index to first visible column # 6 2 Colour index of grid line colour (➜ 5.74). # Note that in BIFF2-BIFF5 an RGB colour is written instead. # 8 2 Not used # 10 2 Cached magnification factor in page break preview (in percent) # 0 = Default (60%) # 12 2 Cached magnification factor in normal view (in percent) # 0 = Default (100%) # 14 4 Not used 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, # but if [Row|Column]#outline_level = 0 the symbols are not shown. data = [ flags, 0, 0, 0, 0, 0 ].pack binfmt(:window2) write_op opcode(:window2), data end def write_wsbool bits = [ # Bit Mask Contents 1, # 0 0x0001 0 = Do not show automatic page breaks # 1 = Show automatic page breaks 0, # 4 0x0010 0 = Standard sheet # 1 = Dialogue sheet (BIFF5-BIFF8) 0, # 5 0x0020 0 = No automatic styles in outlines # 1 = Apply automatic styles to outlines 1, # 6 0x0040 0 = Outline buttons above outline group # 1 = Outline buttons below outline group 1, # 7 0x0080 0 = Outline buttons left of outline group # 1 = Outline buttons right of outline group 0, # 8 0x0100 0 = Scale printout in percent (➜ 6.89) # 1 = Fit printout to number of pages (➜ 6.89) 0, # 9 0x0200 0 = Save external linked values # (BIFF3-BIFF4 only, ➜ 5.10) # 1 = Do not save external linked values # (BIFF3-BIFF4 only, ➜ 5.10) 1, # 10 0x0400 0 = Do not show row outline symbols # 1 = Show row outline symbols 0, # 11 0x0800 0 = Do not show column outline symbols # 1 = Show column outline symbols 0, # 13-12 0x3000 These flags specify the arrangement of windows. # They are stored in BIFF4 only. # 00 = Arrange windows tiled # 01 = Arrange windows horizontal 0, # 10 = Arrange windows vertical # 11 = Arrange windows cascaded # The following flags are valid for BIFF4-BIFF8 only: 0, # 14 0x4000 0 = Standard expression evaluation # 1 = Alternative expression evaluation 0, # 15 0x8000 0 = Standard formula entries # 1 = Alternative formula entries ] weights = [4,5,6,7,8,9,10,11,12,13,14,15] value = bits.inject do |a, b| a | (b << weights.shift) end write_op 0x0081, [value].pack('v') end end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/writer/format.rb0000644000004100000410000002360712216006760023375 0ustar www-datawww-datarequire 'delegate' require 'spreadsheet/format' require 'spreadsheet/excel/internals' module Spreadsheet module Excel module Writer ## # This class encapsulates everything that is needed to write an XF record. class Format < DelegateClass Spreadsheet::Format include Spreadsheet::Excel::Internals def Format.boolean *args args.each do |key| define_method key do @format.send("#{key}?") ? 1 : 0 end end end def Format.color key, default define_method key do color_code(@format.send(key) || default) end end def Format.line_style key, default define_method key do style_code(@format.send(key) || default) end end boolean :hidden, :locked, :merge_range, :shrink, :text_justlast, :text_wrap, :cross_down, :cross_up line_style :left, :none line_style :right, :none line_style :top, :none line_style :bottom, :none color :left_color, :black color :right_color, :black color :top_color, :black color :bottom_color, :black color :diagonal_color, :black color :pattern_fg_color, :pattern_bg color :pattern_bg_color, :pattern_bg attr_reader :format def initialize writer, workbook, format=workbook.default_format, opts={} @opts = { :type => :format }.merge opts @format = format @writer = writer @workbook = workbook super format end def color_code color SEDOC_ROLOC[color] end def style_code style SELYTS_ENIL_REDROB_FX[style] end def font_index @writer.font_index @workbook, font.key end def horizontal_align XF_H_ALIGN.fetch @format.horizontal_align, 0 end def num_format @writer.number_format_index @workbook, @format.number_format end def text_direction XF_TEXT_DIRECTION.fetch @format.text_direction, 0 end def vertical_align XF_V_ALIGN.fetch @format.vertical_align, 2 end def write_op writer, op, *args data = args.join writer.write [op,data.size].pack("v2") writer.write data end def write_xf writer, type=@opts[:type] xf_type = xf_type_prot type data = [ font_index, # Index to FONT record (➜ 6.43) num_format, # Index to FORMAT record (➜ 6.45) xf_type, # Bit Mask Contents # 2-0 0x0007 XF_TYPE_PROT – XF type, cell protection # Bit Mask Contents # 0 0x01 1 = Cell is locked # 1 0x02 1 = Formula is hidden # 2 0x04 0 = Cell XF; 1 = Style XF # 15-4 0xfff0 Index to parent style XF # (always 0xfff in style XFs) xf_align, # Bit Mask Contents # 2-0 0x07 XF_HOR_ALIGN – Horizontal alignment # Value Horizontal alignment # 0x00 General # 0x01 Left # 0x02 Centred # 0x03 Right # 0x04 Filled # 0x05 Justified (BIFF4-BIFF8X) # 0x06 Centred across selection # (BIFF4-BIFF8X) # 0x07 Distributed (BIFF8X) # 3 0x08 1 = Text is wrapped at right border # 6-4 0x70 XF_VERT_ALIGN – Vertical alignment # Value Vertical alignment # 0x00 Top # 0x01 Centred # 0x02 Bottom # 0x03 Justified (BIFF5-BIFF8X) # 0x04 Distributed (BIFF8X) xf_rotation, # XF_ROTATION: Text rotation angle # Value Text rotation # 0 Not rotated # 1-90 1 to 90 degrees counterclockwise # 91-180 1 to 90 degrees clockwise # 255 Letters are stacked top-to-bottom, # but not rotated xf_indent, # Bit Mask Contents # 3-0 0x0f Indent level # 4 0x10 1 = Shrink content to fit into cell # 5 0x40 1 = Merge Range (djberger) # 7-6 0xc0 Text direction (BIFF8X only) # 0 = According to context # 1 = Left-to-right # 2 = Right-to-left xf_used_attr, # Bit Mask Contents # 7-2 0xfc XF_USED_ATTRIB – Used attributes # Each bit describes the validity of a # specific group of attributes. In cell XFs # a cleared bit means the attributes of the # parent style XF are used (but only if the # attributes are valid there), a set bit # means the attributes of this XF are used. # In style XFs a cleared bit means the # attribute setting is valid, a set bit # means the attribute should be ignored. # Bit Mask Contents # 0 0x01 Flag for number format # 1 0x02 Flag for font # 2 0x04 Flag for horizontal and # vertical alignment, text wrap, # indentation, orientation, # rotation, and text direction # 3 0x08 Flag for border lines # 4 0x10 Flag for background area style # 5 0x20 Flag for cell protection (cell # locked and formula hidden) xf_borders, # Cell border lines and background area: # Bit Mask Contents # 3- 0 0x0000000f Left line style (➜ 3.10) # 7- 4 0x000000f0 Right line style (➜ 3.10) # 11- 8 0x00000f00 Top line style (➜ 3.10) # 15-12 0x0000f000 Bottom line style (➜ 3.10) # 22-16 0x007f0000 Colour index (➜ 6.70) # for left line colour # 29-23 0x3f800000 Colour index (➜ 6.70) # for right line colour # 30 0x40000000 1 = Diagonal line # from top left to right bottom # 31 0x80000000 1 = Diagonal line # from bottom left to right top xf_brdcolors, # Bit Mask Contents # 6- 0 0x0000007f Colour index (➜ 6.70) # for top line colour # 13- 7 0x00003f80 Colour index (➜ 6.70) # for bottom line colour # 20-14 0x001fc000 Colour index (➜ 6.70) # for diagonal line colour # 24-21 0x01e00000 Diagonal line style (➜ 3.10) # 31-26 0xfc000000 Fill pattern (➜ 3.11) xf_pattern # Bit Mask Contents # 6-0 0x007f Colour index (➜ 6.70) # for pattern colour # 13-7 0x3f80 Colour index (➜ 6.70) # for pattern background ] write_op writer, 0x00e0, data.pack(binfmt(:xf)) end def xf_align align = horizontal_align align |= text_wrap << 3 align |= vertical_align << 4 align |= text_justlast << 7 align end def xf_borders border = left border |= right << 4 border |= top << 8 border |= bottom << 12 border |= left_color << 16 border |= right_color << 23 border |= cross_down << 30 border |= cross_up << 31 border end def xf_brdcolors border = top_color border |= bottom_color << 7 border |= diagonal_color << 14 border |= pattern << 26 border end def xf_indent indent = indent_level & 0x0f indent |= shrink << 4 indent |= merge_range << 5 indent |= text_direction << 6 indent end def xf_pattern ptrn = pattern_fg_color ptrn |= pattern_bg_color << 7 ptrn end def xf_rotation rot = @format.rotation if @format.rotation_stacked? rot = 255 elsif rot >= -90 or rotation <= 90 rot = -rot + 90 if rot < 0 else warn "rotation outside -90..90; rotation set to 0" rot = 0 end rot end def xf_type_prot type type = type.to_s.downcase == 'style' ? 0xfff4 : 0x0000 type |= locked type |= hidden << 1 type end def xf_used_attr atr_num = num_format & 1 atr_fnt = font_index & 1 atr_fnt = 1 unless @format.font.color == :text atr_alc = 0 if horizontal_align != 0 \ || vertical_align != 2 \ || indent_level > 0 \ || shrink? || merge_range? || text_wrap? then atr_alc = 1 end atr_bdr = 1 atr_pat = 0 if @format.pattern_fg_color != :border \ || @format.pattern_bg_color != :pattern_bg \ || pattern != 0x00 then atr_pat = 1 end atr_prot = hidden? || locked? ? 1 : 0 attrs = atr_num attrs |= atr_fnt << 1 attrs |= atr_alc << 2 attrs |= atr_bdr << 3 attrs |= atr_pat << 4 attrs |= atr_prot << 5 attrs << 2 end end end end end spreadsheet-0.9.0/lib/spreadsheet/excel/password_hash.rb0000644000004100000410000000072412216006760023431 0ustar www-datawww-datamodule Spreadsheet module Excel module Password class <> 15) end end end end end spreadsheet-0.9.0/lib/spreadsheet/font.rb0000644000004100000410000000737412216006760020442 0ustar www-datawww-data# encoding: utf-8 require 'spreadsheet/datatypes' require 'spreadsheet/encodings' module Spreadsheet ## # Font formatting data class Font include Spreadsheet::Datatypes include Spreadsheet::Encodings attr_accessor :name ## # You can set the following boolean Font attributes # * #italic # * #strikeout # * #outline # * #shadow boolean :italic, :strikeout, :outline, :shadow ## # Font color colors :color ## # Font weight # Valid values: :normal, :bold or any positive Integer. # In Excel: # 100 <= weight <= 1000 # :bold => 700 # :normal => 400 # Default: :normal enum :weight, :normal, :bold, Integer, :bold => :b ## # Escapement # Valid values: :normal, :superscript or :subscript. # Default: :normal enum :escapement, :normal, :superscript, :subscript, :subscript => :sub, :superscript => :super # Font size # Valid values: Any positive Integer. # Default: 10 enum :size, 10, Numeric # Underline type # Valid values: :none, :single, :double, :single_accounting and # :double_accounting. # Default: :none enum :underline, :none, :single, :double, :single_accounting, :double_accounting, :single => true # Font Family # Valid values: :none, :roman, :swiss, :modern, :script, :decorative # Default: :none enum :family, :none, :roman, :swiss, :modern, :script, :decorative # Font Family # Valid values: :default, :iso_latin1, :symbol, :apple_roman, :shift_jis, # :korean_hangul, :korean_johab, :chinese_simplified, # :chinese_traditional, :greek, :turkish, :vietnamese, # :hebrew, :arabic, :cyrillic, :thai, :iso_latin2, :oem_latin1 # Default: :default enum :encoding, :default, :iso_latin1, :symbol, :apple_roman, :shift_jis, :korean_hangul, :korean_johab, :chinese_simplified, :chinese_traditional, :greek, :turkish, :vietnamese, :hebrew, :arabic, :baltic, :cyrillic, :thai, :iso_latin2, :oem_latin1 def initialize name, opts={} self.name = name @color = :text @previous_fast_key = nil @size = nil @weight = nil @italic = nil @strikeout = nil @outline = nil @shadow = nil @escapement = nil @underline = nil @family = nil @encoding = nil opts.each do |key, val| self.send "#{key}=", val end end ## # Sets #weight to :bold if(_bool_), :normal otherwise. def bold= bool self.weight = bool ? :bold : nil end def key # :nodoc: fk = fast_key return @key if @previous_fast_key == fk @previous_fast_key = fk @key = build_key end private def build_key # :nodoc: underscore = client('_', 'UTF-8') key = [] key << @name key << underscore << client(size.to_s, 'US-ASCII') key << underscore << client(weight.to_s, 'US-ASCII') key << client('_italic', 'UTF-8') if italic? key << client('_strikeout', 'UTF-8') if strikeout? key << client('_outline', 'UTF-8') if outline? key << client('_shadow', 'UTF-8') if shadow? key << underscore << client(escapement.to_s, 'US-ASCII') key << underscore << client(underline.to_s, 'US-ASCII') key << underscore << client(color.to_s, 'US-ASCII') key << underscore << client(family.to_s, 'US-ASCII') key << underscore << client(encoding.to_s, 'US-ASCII') key.join("") end def fast_key [@name, @size, @weight, @italic, @strikeout, @outline, @shadow, @escapement, @underline, @color, @family, @encoding] end end end spreadsheet-0.9.0/lib/spreadsheet/datatypes.rb0000644000004100000410000001137612216006760021467 0ustar www-datawww-datarequire 'spreadsheet/compatibility' module Spreadsheet ## # This module defines convenience-methods for the definition of Spreadsheet # attributes (boolean, colors and enumerations) module Datatypes include Compatibility def Datatypes.append_features mod super mod.module_eval do class << self ## # Valid colors for color attributes. COLORS = [ :builtin_black, :builtin_white, :builtin_red, :builtin_green, :builtin_blue, :builtin_yellow, :builtin_magenta, :builtin_cyan, :text, :border, :pattern_bg, :dialog_bg, :chart_text, :chart_bg, :chart_border, :tooltip_bg, :tooltip_text, :aqua, :black, :blue, :cyan, :brown, :fuchsia, :gray, :grey, :green, :lime, :magenta, :navy, :orange, :purple, :red, :silver, :white, :yellow, :xls_color_0, :xls_color_1, :xls_color_2, :xls_color_3, :xls_color_4, :xls_color_5, :xls_color_6, :xls_color_7, :xls_color_8, :xls_color_9, :xls_color_10, :xls_color_11, :xls_color_12, :xls_color_13, :xls_color_14, :xls_color_15, :xls_color_16, :xls_color_17, :xls_color_18, :xls_color_19, :xls_color_20, :xls_color_21, :xls_color_22, :xls_color_23, :xls_color_24, :xls_color_25, :xls_color_26, :xls_color_27, :xls_color_28, :xls_color_29, :xls_color_30, :xls_color_31, :xls_color_32, :xls_color_33, :xls_color_34, :xls_color_35, :xls_color_36, :xls_color_37, :xls_color_38, :xls_color_39, :xls_color_40, :xls_color_41, :xls_color_42, :xls_color_43, :xls_color_44, :xls_color_45, :xls_color_46, :xls_color_47, :xls_color_48, :xls_color_49, :xls_color_50, :xls_color_51, :xls_color_52, :xls_color_53, :xls_color_54, :xls_color_55 ] ## # Define instance methods to read and write boolean attributes. def boolean *args args.each do |key| define_method key do name = ivar_name key !!(instance_variable_get(name) if instance_variables.include?(name)) end define_method "#{key}?" do send key end define_method "#{key}=" do |arg| arg = false if arg == 0 instance_variable_set(ivar_name(key), !!arg) end define_method "#{key}!" do send "#{key}=", true end end end ## # Define instance methods to read and write color attributes. # For valid colors see COLORS def colors *args args.each do |key| attr_reader key define_method "#{key}=" do |name| name = name.to_s.downcase.to_sym if COLORS.include?(name) instance_variable_set ivar_name(key), name else raise ArgumentError, "unknown color '#{name}'" end end end end ## # Define instance methods to read and write enumeration attributes. # * The first argument designates the attribute name. # * The second argument designates the default value. # * All subsequent attributes are possible values. # * If the last attribute is a Hash, each value in the Hash designates # aliases for the corresponding key. def enum key, *values aliases = {} if values.last.is_a? Hash values.pop.each do |value, synonyms| if synonyms.is_a? Array synonyms.each do |synonym| aliases.store synonym, value end else aliases.store synonyms, value end end end values.each do |value| aliases.store value, value end define_method key do name = ivar_name key value = instance_variable_get(name) if instance_variables.include? name value || values.first end define_method "#{key}=" do |arg| if arg arg = aliases.fetch arg do aliases.fetch arg.to_s.downcase.gsub(/[ \-]/, '_').to_sym, arg end if values.any? do |val| val === arg end instance_variable_set(ivar_name(key), arg) else valid = values.collect do |val| val.inspect end.join ', ' raise ArgumentError, "Invalid value '#{arg.inspect}' for #{key}. Valid values are: #{valid}" end else instance_variable_set ivar_name(key), values.first end end end end end end end end spreadsheet-0.9.0/lib/spreadsheet/row.rb0000644000004100000410000001141612216006760020273 0ustar www-datawww-datarequire 'spreadsheet/helpers' module Spreadsheet ## # The Row class. Encapsulates Cell data and formatting. # Since Row is a subclass of Array, you may use all the standard Array methods # to manipulate a Row. # By convention, Row#at will give you raw values, while Row#[] may be # overridden to return enriched data if necessary (see also the Date- and # DateTime-handling in Excel::Row#[] # # Useful Attributes are: # #idx:: The 0-based index of this Row in its Worksheet. # #formats:: A parallel array containing Formatting information for # all cells stored in a Row. # #default_format:: The default Format used when writing a Cell if no explicit # Format is stored in #formats for the cell. # #height:: The height of this Row in points (defaults to 12). class Row < Array include Datatypes class << self def format_updater *keys keys.each do |key| unless instance_methods.include? "unupdated_#{key}=" alias_method :"unupdated_#{key}=", :"#{key}=" define_method "#{key}=" do |value| send "unupdated_#{key}=", value @worksheet.row_updated @idx, self if @worksheet value end end end end def updater *keys keys.each do |key| ## Passing blocks to methods defined with define_method is not possible # in Ruby 1.8: # http://groups.google.com/group/ruby-talk-google/msg/778184912b769e5f # use class_eval as suggested by someone else in # http://rubyforge.org/tracker/index.php?func=detail&aid=25732&group_id=678&atid=2677 class_eval <<-SRC, __FILE__, __LINE__ def #{key}(*args) res = super(*args) @worksheet.row_updated @idx, self if @worksheet res end SRC end end end attr_reader :formats attr_accessor :idx, :height, :worksheet boolean :hidden, :collapsed enum :outline_level, 0, Integer updater :[]=, :clear, :concat, :delete, :delete_if, :fill, :insert, :map!, :pop, :push, :reject!, :replace, :reverse!, :shift, :slice!, :sort!, :uniq!, :unshift format_updater :collapsed, :height, :hidden, :outline_level def initialize worksheet, idx, cells=[] @default_format = nil @worksheet = worksheet @idx = idx super cells @formats = [] @height = 12.1 end ## # The default Format of this Row, if you have set one. # Returns the Worksheet's default or the Workbook's default Format otherwise. def default_format @default_format || @worksheet.default_format || @workbook.default_format end ## # Set the default Format used when writing a Cell if no explicit Format is # stored for the cell. def default_format= format @worksheet.add_format format if @worksheet @default_format = format end format_updater :default_format ## # #first_used the 0-based index of the first non-blank Cell. def first_used [ index_of_first(self), index_of_first(@formats) ].compact.min end ## # The Format for the Cell at _idx_ (0-based), or the first valid Format in # Row#default_format, Column#default_format and Worksheet#default_format. def format idx @formats[idx] || @default_format \ || @worksheet.column(idx).default_format if @worksheet end ## # Returns a copy of self with nil-values appended for empty cells that have # an associated Format. # This is primarily a helper-function for the writer classes. def formatted copy = dup Helpers.rcompact(@formats) if copy.length < @formats.size copy.concat Array.new(@formats.size - copy.length) end copy end ## # Same as Row#size, but takes into account formatted empty cells def formatted_size Helpers.rcompact(@formats) sz = size fs = @formats.size fs > sz ? fs : sz end ## # #first_unused (really last used + 1) - the 0-based index of the first of # all remaining contiguous blank Cells. alias :first_unused :formatted_size def inspect variables = instance_variables.collect do |name| "%s=%s" % [name, instance_variable_get(name)] end.join(' ') sprintf "#<%s:0x%014x %s %s>", self.class, object_id, variables, super end ## # Set the Format for the Cell at _idx_ (0-based). def set_format idx, fmt @formats[idx] = fmt @worksheet.add_format fmt @worksheet.row_updated @idx, self if @worksheet fmt end private def index_of_first ary # :nodoc: if first = ary.find do |elm| !elm.nil? end ary.index first end end end end spreadsheet-0.9.0/lib/spreadsheet/worksheet.rb0000644000004100000410000002406512216006760021503 0ustar www-datawww-datarequire 'date' require 'spreadsheet/column' require 'spreadsheet/encodings' require 'spreadsheet/row' require 'spreadsheet/excel/password_hash' module Spreadsheet ## # The Worksheet class. Contains most of the Spreadsheet data in Rows. # # Interesting Attributes # #name :: The Name of this Worksheet. # #default_format:: The default format used for all cells in this Workhseet # that have no format set explicitly or in # Row#default_format. # #rows :: The Rows in this Worksheet. It is not recommended to # Manipulate this Array directly. If you do, call # #updated_from with the smallest modified index. # #columns :: The Column formatting in this Worksheet. Column # instances may appear at more than one position in #columns. # If you modify a Column directly, your changes will be # reflected in all those positions. # #selected :: When a user chooses to print a Workbook, Excel will include # all selected Worksheets. If no Worksheet is selected at # Workbook#write, then the first Worksheet is selected by # default. class Worksheet include Spreadsheet::Encodings include Enumerable attr_accessor :name, :selected, :workbook, :password_hash attr_reader :rows, :columns, :merged_cells, :margins, :pagesetup def initialize opts={} @default_format = nil @selected = opts[:selected] @dimensions = [0,0,0,0] @pagesetup = { :orig_data => [9, 100, 1, 1, 1, 0, 300, 300, 0.5, 0.5, 1], :orientation => :portrait, :adjust_to => 100 } @margins = { :top => 1, :left => 0.75, :right => 0.75, :bottom => 1 } @name = opts[:name] || 'Worksheet' @workbook = opts[:workbook] @rows = [] @columns = [] @links = {} @merged_cells = [] @protected = false @password_hash = 0 end def active # :nodoc: warn "Worksheet#active is deprecated. Please use Worksheet#selected instead." selected end def active= selected # :nodoc: warn "Worksheet#active= is deprecated. Please use Worksheet#selected= instead." self.selected = selected end ## # Add a Format to the Workbook. If you use Row#set_format, you should not # need to use this Method. def add_format fmt @workbook.add_format fmt if fmt end ## # Get the enriched value of the Cell at _row_, _column_. # See also Worksheet#[], Row#[]. def cell row, column row(row)[column] end ## # Returns the Column at _idx_. def column idx @columns[idx] || Column.new(idx, default_format, :worksheet => self) end ## # The number of columns in this Worksheet which contain data. def column_count dimensions[3] - dimensions[2] end def column_updated idx, column @columns[idx] = column end ## # Delete the Row at _idx_ (0-based) from this Worksheet. def delete_row idx res = @rows.delete_at idx updated_from idx res end ## # The default Format of this Worksheet, if you have set one. # Returns the Workbook's default Format otherwise. def default_format @default_format || @workbook.default_format end ## # Set the default Format of this Worksheet. def default_format= format @default_format = format add_format format format end ## # Is the worksheet protected? def protected? @protected end ## # Set worklist protection def protect! password = '' @protected = true password = password.to_s if password.size == 0 @password_hash = 0 else @password_hash = Excel::Password.password_hash password end end ## # Dimensions:: [ first used row, first unused row, # first used column, first unused column ] # ( First used means that all rows or columns before that are # empty. First unused means that this and all following rows # or columns are empty. ) def dimensions @dimensions || recalculate_dimensions end ## # If no argument is given, #each iterates over all used Rows (from the first # used Row until but omitting the first unused Row, see also #dimensions). # # If the argument skip is given, #each iterates from that row until but # omitting the first unused Row, effectively skipping the first _skip_ Rows # from the top of the Worksheet. def each skip=dimensions[0] skip.upto(dimensions[1] - 1) do |idx| yield row(idx) end end def encoding # :nodoc: @workbook.encoding end ## # Sets the default Format of the column at _idx_. # # _idx_ may be an Integer, or an Enumerable that iterates over a number of # Integers. # # _format_ is a Format, or nil if you want to remove the Formatting at _idx_ # # Returns an instance of Column if _idx_ is an Integer, an Array of Columns # otherwise. def format_column idx, format=nil, opts={} opts[:worksheet] = self res = case idx when Integer column = nil if format column = Column.new(idx, format, opts) end @columns[idx] = column else idx.collect do |col| format_column col, format, opts end end shorten @columns res end ## # Formats all Date, DateTime and Time cells with _format_ or the default # formats: # - 'DD.MM.YYYY' for Date # - 'DD.MM.YYYY hh:mm:ss' for DateTime and Time def format_dates! format=nil new_formats = {} fmt_str_time = client('DD.MM.YYYY hh:mm:ss', 'UTF-8') fmt_str_date = client('DD.MM.YYYY', 'UTF-8') each do |row| row.each_with_index do |value, idx| unless row.formats[idx] || row.format(idx).date_or_time? numfmt = case value when DateTime, Time format || fmt_str_time when Date format || fmt_str_date end case numfmt when Format row.set_format idx, numfmt when String existing_format = row.format(idx) new_formats[existing_format] ||= {} new_format = new_formats[existing_format][numfmt] if !new_format new_format = new_formats[existing_format][numfmt] = existing_format.dup new_format.number_format = numfmt end row.set_format idx, new_format end end end end end ## # Insert a Row at _idx_ (0-based) containing _cells_ def insert_row idx, cells=[] res = @rows.insert idx, Row.new(self, idx, cells) updated_from idx res end def inspect names = instance_variables names.delete '@rows' variables = names.collect do |name| "%s=%s" % [name, instance_variable_get(name)] end.join(' ') sprintf "#<%s:0x%014x %s @rows[%i]>", self.class, object_id, variables, row_count end ## The last Row containing any data def last_row row(last_row_index) end ## The index of the last Row containing any data def last_row_index [dimensions[1] - 1, 0].max end ## # Replace the Row at _idx_ with the following arguments. Like #update_row, # but truncates the Row if there are fewer arguments than Cells in the Row. def replace_row idx, *cells if(row = @rows[idx]) && cells.size < row.size cells.concat Array.new(row.size - cells.size) end update_row idx, *cells end ## # The Row at _idx_ or a new Row. def row idx @rows[idx] || Row.new(self, idx) end ## # The number of Rows in this Worksheet which contain data. def row_count dimensions[1] - dimensions[0] end ## # Tell Worksheet that the Row at _idx_ has been updated and the #dimensions # need to be recalculated. You should not need to call this directly. def row_updated idx, row @dimensions = nil @rows[idx] = row end ## # Updates the Row at _idx_ with the following arguments. def update_row idx, *cells res = if row = @rows[idx] row[0, cells.size] = cells row else Row.new self, idx, cells end row_updated idx, res res end ## # Renumbers all Rows starting at _idx_ and calls #row_updated for each of # them. def updated_from index index.upto(@rows.size - 1) do |idx| row = row(idx) row.idx = idx row_updated idx, row end end ## # Get the enriched value of the Cell at _row_, _column_. # See also Worksheet#cell, Row#[]. def [] row, column row(row)[column] end ## # Set the value of the Cell at _row_, _column_ to _value_. # See also Row#[]=. 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 ary.index(ary.find do |elm| elm end) end def recalculate_dimensions # :nodoc: shorten @rows @dimensions = [] @dimensions[0] = index_of_first(@rows) || 0 @dimensions[1] = @rows.size compact = @rows.compact @dimensions[2] = compact.collect do |row| row.first_used end.compact.min || 0 @dimensions[3] = compact.collect do |row| row.first_unused end.max || 0 @dimensions end def shorten ary # :nodoc: return unless ary while !ary.empty? && !ary.last ary.pop end ary unless ary.empty? end end end spreadsheet-0.9.0/lib/spreadsheet/workbook.rb0000644000004100000410000001064312216006760021322 0ustar www-datawww-datarequire 'spreadsheet/format' require 'spreadsheet/encodings' module Spreadsheet ## # The Workbook class represents a Spreadsheet-Document and is the entry point # for all Spreadsheet manipulation. # # Interesting Attributes: # #default_format:: The default format used for all cells in this Workbook. # that have no format set explicitly or in # Row#default_format or Worksheet#default_format. class Workbook include Spreadsheet::Encodings attr_reader :io, :worksheets, :formats, :fonts, :palette attr_accessor :active_worksheet, :encoding, :default_format, :version def initialize io = nil, opts={:default_format => Format.new} @worksheets = [] @io = io @fonts = [] @palette = {} @formats = [] @formats_set = {} if @default_format = opts[:default_format] add_format @default_format end end ## # Add a Font to the Workbook. Used by the parser. You should not need to # use this Method. def add_font font @fonts.push(font).uniq! if font font end ## # Add a Format to the Workbook. If you use Row#set_format, you should not # need to use this Method. def add_format format if format && !@formats_set[format] @formats_set[format] = true @formats.push(format) end format end ## # Add a Worksheet to the Workbook. def add_worksheet worksheet worksheet.workbook = self @worksheets.push worksheet worksheet end ## # Delete a Worksheet from Workbook by it's index def delete_worksheet worksheet_index @worksheets.delete_at worksheet_index end ## # Change the RGB components of the elements in the colour palette. def set_custom_color idx, red, green, blue raise 'Invalid format' if [red, green, blue].find { |c| ! (0..255).include?(c) } @palette[idx] = [red, green, blue] end ## # Create a new Worksheet in this Workbook. # Used without options this creates a Worksheet with the name 'WorksheetN' # where the new Worksheet is the Nth Worksheet in this Workbook. # # Use the option :name => 'My pretty Name' to override this # behavior. def create_worksheet opts = {} opts[:name] ||= client("Worksheet#{@worksheets.size.next}", 'UTF-8') add_worksheet Worksheet.new(opts) end ## # Returns the count of total worksheets present. # Takes no arguments. Just returns the length of @worksheets array. def sheet_count @worksheets.length end ## # The Font at _idx_ def font idx @fonts[idx] end ## # The Format at _idx_, or - if _idx_ is a String - # the Format with name == _idx_ def format idx case idx when Integer @formats[idx] || @default_format when String @formats.find do |fmt| fmt.name == idx end end end def inspect variables = (instance_variables - uninspect_variables).collect do |name| "%s=%s" % [name, instance_variable_get(name)] end.join(' ') uninspect = uninspect_variables.collect do |name| var = instance_variable_get name "%s=%s[%i]" % [name, var.class, var.size] end.join(' ') sprintf "#<%s:0x%014x %s %s>", self.class, object_id, variables, uninspect end def uninspect_variables # :nodoc: %w{@formats @fonts @worksheets} end ## # The Worksheet at _idx_, or - if _idx_ is a String - # the Worksheet with name == _idx_ def worksheet idx case idx when Integer @worksheets[idx] when String @worksheets.find do |sheet| sheet.name == idx end end end ## # Write this Workbook to a File, IO Stream or Writer Object. The latter will # make more sense once there are more than just an Excel-Writer available. def write io_path_or_writer if io_path_or_writer.is_a? Writer io_path_or_writer.write self else writer(io_path_or_writer).write(self) end end ## # Returns a new instance of the default Writer class for this Workbook (can # only be an Excel::Writer::Workbook at this time) def writer io_or_path, type=Excel, version=self.version if type == Excel Excel::Writer::Workbook.new io_or_path else raise NotImplementedError, "No Writer defined for #{type}" end end end end spreadsheet-0.9.0/lib/spreadsheet/encodings.rb0000644000004100000410000000367512216006760021445 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 # Spreadsheet::Encoding -- spreadheet -- 07.09.2011 -- mhatakeyama@ywesee.com # Spreadsheet::Encoding -- spreadheet -- 03.07.2009 -- hwyss@ywesee.com module Spreadsheet ## # Methods for Encoding-conversions. You should not need to use any of these. module Encodings if RUBY_VERSION >= '1.9' def client string, internal='UTF-16LE' string = string.dup string.force_encoding internal string.encode Spreadsheet.client_encoding end def internal string, client=Spreadsheet.client_encoding string = string.dup string.force_encoding client string.encode('UTF-16LE').force_encoding('ASCII-8BIT') end def utf8 string, client=Spreadsheet.client_encoding string = string.dup string.force_encoding client string.encode('UTF-8') end else require 'iconv' @@iconvs = {} def client string, internal='UTF-16LE' string = string.dup key = [Spreadsheet.client_encoding, internal] iconv = @@iconvs[key] ||= Iconv.new(Spreadsheet.client_encoding, internal) iconv.iconv string end def internal string, client=Spreadsheet.client_encoding string = string.dup key = ['UTF-16LE', client] iconv = @@iconvs[key] ||= Iconv.new('UTF-16LE//TRANSLIT//IGNORE', client) iconv.iconv string end def utf8 string, client=Spreadsheet.client_encoding string = string.dup key = ['UTF-8', client] iconv = @@iconvs[key] ||= Iconv.new('UTF-8//TRANSLIT//IGNORE', client) iconv.iconv string end end rescue LoadError warn "You don't have Iconv support compiled in your Ruby. Spreadsheet may not work as expected" def client string, internal='UTF-16LE' string.delete "\0" end def internal string, internal='UTF-16LE' string.split('').zip(Array.new(string.size, 0.chr)).join end end end spreadsheet-0.9.0/lib/spreadsheet/writer.rb0000644000004100000410000000125412216006760020777 0ustar www-datawww-datamodule Spreadsheet ## # Parent Class for all Writers. Implements the copying of unmodified # Spreadsheet documents. class Writer def initialize io_or_path @io_or_path = io_or_path end def write workbook if @io_or_path.respond_to? :seek @io_or_path.binmode write_workbook workbook, @io_or_path else File.open(@io_or_path, "wb+") do |fh| write_workbook workbook, fh end end end private def write_workbook workbook, io reader = workbook.io unless io == reader reader.rewind data = reader.read io.rewind io.write data end end end end spreadsheet-0.9.0/lib/spreadsheet/column.rb0000644000004100000410000000450312216006760020760 0ustar www-datawww-datarequire 'spreadsheet/datatypes' module Spreadsheet ## # The Column class. Encapsulates column-formatting and width, and provides a # means to iterate over all cells in a column. # # Useful Attributes: # #width:: The width in characters (in respect to the '0' character # of the Worksheet's default Font). Float values are # permitted, for Excel the available Precision is at 1/256 # characters. # #default_format:: The default Format for cells in this column (applied if # there is no explicit Cell Format and no default Row format # for the Cell). # #hidden:: The Column is hidden. # #collapsed:: The Column is collapsed. # #outline_level:: Outline level of the column. class Column class << self def updater *keys keys.each do |key| unless instance_methods.include? "unupdated_#{key}=" alias_method :"unupdated_#{key}=", :"#{key}=" define_method "#{key}=" do |value| send "unupdated_#{key}=", value @worksheet.column_updated @idx, self if @worksheet value end end end end end include Datatypes include Enumerable attr_accessor :width, :worksheet attr_reader :default_format, :idx boolean :hidden, :collapsed enum :outline_level, 0, Integer updater :collapsed, :hidden, :outline_level, :width def initialize idx, format, opts={} @worksheet = nil @idx = idx opts[:width] ||= 10 opts.each do |key, value| self.send "#{key}=", value end self.default_format = format end ## # Set the default Format for Cells in this Column. def default_format= format @worksheet.add_format format if @worksheet @default_format = format @worksheet.column_updated @idx, self if @worksheet format end ## # Iterate over all cells in this column. def each @worksheet.each do |row| yield row[idx] end end def == other # :nodoc: other.is_a?(Column) && default_format == other.default_format \ && width == other.width && hidden == other.hidden \ && collapsed == other.collapsed && outline_level == other.outline_level end end end spreadsheet-0.9.0/lib/spreadsheet/link.rb0000644000004100000410000000275112216006760020423 0ustar www-datawww-datarequire 'uri' require 'spreadsheet/encodings' module Spreadsheet ## # The Link class. Is a Subclass of String, which lets you treat a Cell that # contains a Link just as if it was a String (containing the link's description # if there is one or the url with fragment otherwise), but gives you access # to the url, fragment and target_frame if you need it. # # # Interesting Attributes # #url :: The Uniform Resource Location this Link points to. # #fragment :: Also called text mark: http://example.com/page.html#fragment # #target_frame :: Which frame a Link should be opened in, should also support # the special frames _blank, _parent, _self and _top. # #dos :: Excel may store a DOS-Filename together with the long # Filename introduced in VFAT. You probably will not need this, # but if you do, here is where you can find it. class Link < String include Encodings attr_accessor :target_frame, :url, :dos, :fragment def initialize url='', description=url, fragment=nil super description @url = url @fragment = fragment end ## # The Url with the fragment appended if present. def href href = (@url || @dos).to_s.dup if @fragment href << client('#', 'UTF-8') << @fragment end href end ## # Attempts to parse the output of href. May raise a URI::InvalidURIError def to_uri URI.parse href end end end spreadsheet-0.9.0/lib/spreadsheet/format.rb0000644000004100000410000001665212216006760020763 0ustar www-datawww-data# encoding: utf-8 require 'spreadsheet/datatypes' require 'spreadsheet/encodings' require 'spreadsheet/font' module Spreadsheet ## # Formatting data class Format include Spreadsheet::Datatypes include Spreadsheet::Encodings ## # You can set the following boolean attributes: # #cross_down:: Draws a Line from the top-left to the bottom-right # corner of a cell. # #cross_up:: Draws a Line from the bottom-left to the top-right # corner of a cell. # #hidden:: The cell is hidden. # #locked:: The cell is locked. # #merge_range:: The cell is in a merged range. # #shrink:: Shrink the contents to fit the cell. # #text_justlast:: Force the last line of a cell to be justified. This # probably makes sense if horizontal_align = :justify # #left:: Apply a border style to the left of the cell. # #right:: Apply a border style to the right of the cell. # #top:: Apply a border style at the top of the cell. # #bottom:: Apply a border style at the bottom of the cell. # #rotation_stacked:: Characters in the cell are stacked on top of each # other. Excel will ignore other rotation values if # this is set. boolean :cross_down, :cross_up, :hidden, :locked, :merge_range, :shrink, :text_justlast, :text_wrap, :rotation_stacked ## # Border line styles # Valid values: :none, :thin, :medium, :dashed, :dotted, :thick, # :double, :hair, :medium_dashed, :thin_dash_dotted, # :medium_dash_dotted, :thin_dash_dot_dotted, # :medium_dash_dot_dotted, :slanted_medium_dash_dotted # Default: :none styles = [ :thin, :medium, :dashed, :dotted, :thick, :double, :hair, :medium_dashed, :thin_dash_dotted, :medium_dash_dotted, :thin_dash_dot_dotted, :medium_dash_dot_dotted, :slanted_medium_dash_dotted ] enum :left, :none, *styles enum :right, :none, *styles enum :top, :none, *styles enum :bottom, :none, *styles ## # Color attributes colors :bottom_color, :top_color, :left_color, :right_color, :pattern_fg_color, :pattern_bg_color, :diagonal_color ## # Text direction # Valid values: :context, :left_to_right, :right_to_left # Default: :context enum :text_direction, :context, :left_to_right, :right_to_left, :left_to_right => [:ltr, :l2r], :right_to_left => [:rtl, :r2l] alias :reading_order :text_direction alias :reading_order= :text_direction= ## # Indentation level enum :indent_level, 0, Integer alias :indent :indent_level alias :indent= :indent_level= ## # Horizontal alignment # Valid values: :default, :left, :center, :right, :fill, :justify, :merge, # :distributed # Default: :default enum :horizontal_align, :default, :left, :center, :right, :fill, :justify, :merge, :distributed, :center => :centre, :merge => [ :center_across, :centre_across ], :distributed => :equal_space ## # Vertical alignment # Valid values: :bottom, :top, :middle, :justify, :distributed # Default: :bottom enum :vertical_align, :bottom, :top, :middle, :justify, :distributed, :distributed => [:vdistributed, :vequal_space, :equal_space], :justify => :vjustify, :middle => [:vcenter, :vcentre, :center, :centre] attr_accessor :font, :number_format, :name, :pattern, :used_merge ## # Text rotation attr_reader :rotation def initialize opts={} @font = Font.new client("Arial", 'UTF-8'), :family => :swiss @number_format = client 'GENERAL', 'UTF-8' @rotation = 0 @pattern = 0 @bottom_color = :black @top_color = :black @left_color = :black @right_color = :black @diagonal_color = :black @pattern_fg_color = :border @pattern_bg_color = :pattern_bg @regexes = { :date => Regexp.new(client("[YMD]", 'UTF-8')), :date_or_time => Regexp.new(client("[hmsYMD]", 'UTF-8')), :datetime => Regexp.new(client("([YMD].*[HS])|([HS].*[YMD])", 'UTF-8')), :time => Regexp.new(client("[hms]", 'UTF-8')), :number => Regexp.new(client("[\#]", 'UTF-8')) } # Temp code to prevent merged formats in non-merged cells. @used_merge = 0 opts.each do |key, val| writer = "#{key}=" if @font.respond_to? writer @font.send writer, val else self.send writer, val end end yield self if block_given? end ## # Combined method for both horizontal and vertical alignment. Sets the # first valid value (e.g. Format#align = :justify only sets the horizontal # alignment. Use one of the aliases prefixed with :v if you need to # disambiguate.) # # This is essentially a backward-compatibility method and may be removed at # some point in the future. def align= location self.horizontal_align = location rescue ArgumentError self.vertical_align = location rescue ArgumentError end ## # Returns an Array containing the line styles of the four borders: # bottom, top, right, left def border [bottom, top, right, left] end ## # Set same line style on all four borders at once (left, right, top, bottom) def border=(style) [:bottom=, :top=, :right=, :left=].each do |writer| send writer, style end end ## # Returns an Array containing the colors of the four borders: # bottom, top, right, left def border_color [@bottom_color,@top_color,@right_color,@left_color] end ## # Set all four border colors to _color_ (left, right, top, bottom) def border_color=(color) [:bottom_color=, :top_color=, :right_color=, :left_color=].each do |writer| send writer, color end end ## # Set the Text rotation # Valid values: Integers from -90 to 90, # or :stacked (sets #rotation_stacked to true) def rotation=(rot) if rot.to_s.downcase == 'stacked' @rotation_stacked = true @rotation = 0 elsif rot.kind_of?(Integer) @rotation_stacked = false @rotation = rot % 360 else raise TypeError, "rotation value must be an Integer or the String 'stacked'" end end ## # Backward compatibility method. May disappear at some point in the future. def center_across! self.horizontal_align = :merge end alias :merge! :center_across! ## # Is the cell formatted as a Date? def date? !number? && !!@regexes[:date].match(@number_format.to_s) end ## # Is the cell formatted as a Date or Time? def date_or_time? !number? && !!@regexes[:date_or_time].match(@number_format.to_s) end ## # Is the cell formatted as a DateTime? def datetime? !number? && !!@regexes[:datetime].match(@number_format.to_s) end ## # Is the cell formatted as a Time? def time? !number? && !!@regexes[:time].match(@number_format.to_s) end ## # Is the cell formatted as a number? def number? !!@regexes[:number].match(@number_format.to_s) end end end spreadsheet-0.9.0/lib/spreadsheet/helpers.rb0000644000004100000410000000030112216006760021115 0ustar www-datawww-datamodule Spreadsheet module Helpers def rcompact(array) while !array.empty? && array.last.nil? array.pop end array end module_function :rcompact end end spreadsheet-0.9.0/lib/spreadsheet/compatibility.rb0000644000004100000410000000106612216006760022335 0ustar www-datawww-datamodule Spreadsheet module Compatibility ## # One of the most incisive changes in terms of meta-programming in Ruby 1.9 # is the switch from representing instance-variable names as Strings to # presenting them as Symbols. ivar_name provides compatibility. if RUBY_VERSION >= '1.9' def ivar_name symbol :"@#{symbol}" end def method_name symbol symbol.to_sym end else def ivar_name symbol "@#{symbol}" end def method_name symbol symbol.to_s end end end end spreadsheet-0.9.0/lib/spreadsheet/excel.rb0000644000004100000410000000440512216006760020564 0ustar www-datawww-datarequire 'spreadsheet' warn <<-EOS [DEPRECATED] By requiring 'spreadsheet/excel' you are loading a Compatibility layer which provides a drop-in replacement for Spreadsheet::Excel versions <= 0.3.5.1. This code will be removed in Spreadsheet version 1.0.0 EOS ## # Spreadsheet::Excel Compatibility Layer. # Drop-in replacement for Spreadsheet::Excel version <= 0.3.5.1 module Spreadsheet module Excel class ExcelCompatibleWorkbook < Workbook def initialize file_path, *args super *args @file_path = file_path end def close write @file_path end end def Excel.new file_path ExcelCompatibleWorkbook.new file_path end class Workbook def add_worksheet name if name.is_a? String create_worksheet :name => name else super end end end end class Worksheet unless instance_methods.include? "new_format_column" alias :new_format_column :format_column def format_column column, width=nil, format=nil if width.is_a? Format new_format_column column, width, format else new_format_column column, format, :width => width end end end def write row, col, data=nil, format=nil if data.is_a? Array write_row row, col, data, format else row = row(row) row[col] = data row.set_format col, format end end def write_column row, col, data=nil, format=nil if data.is_a? Array data.each do |token| if token.is_a? Array write_row row, col, token, format else write row, col, token, format end row += 1 end else write row, col, data, format end end def write_row row, col, data=nil, format=nil if data.is_a? Array data.each do |token| if token.is_a? Array write_column row, col, token, format else write row, col, token, format end col += 1 end else write row, col, data, format end end def write_url row, col, url, string=url, format=nil row(row)[col] = Link.new url, string end end end spreadsheet-0.9.0/lib/spreadsheet/formula.rb0000644000004100000410000000045012216006760021125 0ustar www-datawww-datamodule Spreadsheet ## # Formula implementation. At the moment this is just a placeholder. # You may access the last calculated #value, other attributes are needed for # writing the Formula back into modified Excel Files. class Formula attr_accessor :data, :value, :shared end end spreadsheet-0.9.0/lib/parseexcel.rb0000644000004100000410000000176112216006760017312 0ustar www-datawww-data### Spreadsheet - A Library for reading and writing Spreadsheet Documents. # # Copyright (C) 2008 Hannes Wyss # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Contact Information: # # E-Mail: hannes.wyss@gmail.com # P-Mail: ywesee GmbH # Hannes Wyss # Winterthurerstrasse 52 # 8006 Zürich ### Switzerland require 'parseexcel/parseexcel' spreadsheet-0.9.0/lib/spreadsheet.rb0000644000004100000410000000460012216006760017461 0ustar www-datawww-data### Spreadsheet - A Library for reading and writing Spreadsheet Documents. # # Copyright (C) 2008-2010 ywesee GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Contact Information: # # E-Mail: mhatakeyama@ywesee.com, zdavatz@ywesee.com # P-Mail: ywesee GmbH # Zeno R.R. Davatz # Winterthurerstrasse 52 # 8006 Zürich ### Switzerland require 'spreadsheet/excel/workbook' require 'spreadsheet/excel/reader' require 'spreadsheet/excel/rgb' # = Synopsis # The Spreadsheet Library is designed to read and write Spreadsheet Documents. # As of version 0.6.0, only Microsoft Excel compatible spreadsheets are # supported. # # == Example # require 'spreadsheet' # # book = Spreadsheet.open '/path/to/an/excel-file.xls' # sheet = book.worksheet 0 # sheet.each do |row| puts row[0] end module Spreadsheet ## # The version of Spreadsheet you are using. VERSION = '0.9.0' ## # Default client Encoding. Change this value if your application uses a # different Encoding: # Spreadsheet.client_encoding = 'ISO-LATIN-1//TRANSLIT//IGNORE' @client_encoding = 'UTF-8' class << self attr_accessor :client_encoding ## # Parses a Spreadsheet Document and returns a Workbook object. At present, # only Excel-Documents can be read. def open io_or_path, mode="rb+" if io_or_path.respond_to? :seek Excel::Workbook.open(io_or_path) elsif block_given? File.open(io_or_path, mode) do |fh| yield open(fh) end else open File.open(io_or_path, mode) end end ## # Returns a Writer object for the specified path. At present, only the # Excel-Writer is available. def writer io_or_path, type=Excel Excel::Writer::Workbook.new io_or_path end end end spreadsheet-0.9.0/lib/parseexcel/0000755000004100000410000000000012216006760016760 5ustar www-datawww-dataspreadsheet-0.9.0/lib/parseexcel/parser.rb0000644000004100000410000000025312216006760020601 0ustar www-datawww-datarequire 'parseexcel' module Spreadsheet module ParseExcel # :nodoc: all class Parser def parse path Spreadsheet.open path end end end end spreadsheet-0.9.0/lib/parseexcel/parseexcel.rb0000644000004100000410000000360112216006760021440 0ustar www-datawww-datarequire 'spreadsheet' warn <<-EOS [DEPRECATED] By requiring 'parseexcel', 'parseexcel/parseexcel' and/or 'parseexcel/parser' you are loading a Compatibility layer which provides a drop-in replacement for the ParseExcel library. This code makes the reading of Spreadsheet documents less efficient and will be removed in Spreadsheet version 1.0.0 EOS module Spreadsheet ## # The ParseExcel module is provided as a drop-in replacement for the # ParseExcel library. This code is deprecated and will be removed in # Spreadsheet version 1.0.0 module ParseExcel def ParseExcel.parse path Spreadsheet.open path end class Worksheet class Cell attr_accessor :value, :kind, :numeric, :code, :book, :format, :rich, :encoding, :annotation def initialize value, format, row, idx @format = format @idx = idx @row = row @value = value @encoding = Spreadsheet.client_encoding end def date @row.date @idx end def datetime @row.datetime @idx end def to_i @value.to_i end def to_f @value.to_f end def to_s(target_encoding=nil) if(target_encoding) begin Iconv.new(target_encoding, @encoding).iconv(@value) rescue Iconv.new(target_encoding, 'ascii').iconv(@value.to_s) end else @value.to_s end end def type if @format && (@format.date? || @format.time?) :date elsif @value.is_a?(Numeric) :numeric else :text end end end end end module Excel class Reader # :nodoc: all def set_cell worksheet, row, column, xf, value=nil cells = @current_row_block[row] ||= Row.new(nil, row) cells.formats[column] = xf = @workbook.format(xf) cells[column] = ParseExcel::Worksheet::Cell.new(value, xf, cells, column) end end end end spreadsheet-0.9.0/Gemfile.lock0000644000004100000410000000037312216006760016304 0ustar www-datawww-dataGEM remote: https://rubygems.org/ specs: hoe (3.7.1) rake (>= 0.8, < 11.0) rake (10.1.0) ruby-ole (1.2.11.7) spreadsheet (0.8.9) ruby-ole (>= 1.0) PLATFORMS ruby DEPENDENCIES hoe (>= 3.4) ruby-ole spreadsheet