lib/writeexcel/workbook.rb in writeexcel-0.6.9 vs lib/writeexcel/workbook.rb in writeexcel-0.6.10

- old
+ new

@@ -10,25 +10,32 @@ # # original written in Perl by John McNamara # converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp # require 'nkf' +require 'forwardable' require 'writeexcel/biffwriter' require 'writeexcel/worksheet' require 'writeexcel/chart' require 'writeexcel/format' require 'writeexcel/formula' require 'writeexcel/olewriter' require 'writeexcel/storage_lite' require 'writeexcel/compatibility' +require 'writeexcel/shared_string_table' +require 'writeexcel/worksheets' class Workbook < BIFFWriter require 'writeexcel/properties' require 'writeexcel/helper' + extend Forwardable + attr_reader :url_format, :parser, :tempdir, :date_1904 - attr_reader :compatibility, :palette, :sinfo + attr_reader :compatibility, :palette + attr_reader :ext_refs + attr_reader :worksheets BOF = 12 # :nodoc: EOF = 4 # :nodoc: # @@ -75,34 +82,25 @@ @file = file @default_formats = default_formats @parser = Writeexcel::Formula.new(@byte_order) @tempdir = nil @date_1904 = false - @selected = 0 @xf_index = 0 @biffsize = 0 @sheet_count = 0 @chart_count = 0 @codepage = 0x04E4 @country = 1 - @worksheets = [] + @worksheets = Worksheets.new @formats = [] @palette = [] @biff_only = 0 @internal_fh = 0 @fh_out = "" - @sinfo = { - :activesheet => 0, - :firstsheet => 0, - :str_total => 0, - :str_unique => 0, - :str_table => {} - } - @str_array = [] - @str_block_sizes = [] + @shared_string_table = SharedStringTable.new @extsst_offsets = [] # array of [global_offset, local_offset] @extsst_buckets = 0 @extsst_bucket_size = 0 @ext_refs = {} @@ -222,23 +220,20 @@ # # name = [0x263a].pack('n') # worksheet = workbook.add_worksheet(name, true) # Smiley # def add_worksheet(sheetname = '', name_utf16be = false) - index = @worksheets.size - name, name_utf16be = check_sheetname(sheetname, name_utf16be) init_data = [ self, name, - index, name_utf16be ] worksheet = Writeexcel::Worksheet.new(*init_data) - @worksheets[index] = worksheet # Store ref for iterator - @parser.set_ext_sheets(name, index) # Store names in Formula.rb + @worksheets << worksheet # Store ref for iterator + @parser.set_ext_sheets(name, worksheet.index) # Store names in Formula.rb worksheet end # # Create a chart for embedding or as as new sheet. @@ -313,11 +308,10 @@ # examples directory of the distro. # def add_chart(properties) name = '' name_utf16be = false - index = @worksheets.size # Type must be specified so we can create the required chart instance. type = properties[:type] raise "Must define chart type in add_chart()" if type.nil? @@ -332,23 +326,18 @@ end init_data = [ self, name, - index, name_utf16be ] chart = Writeexcel::Chart.factory(type, *init_data) # If the chart isn't embedded let the workbook control it. if !embedded - @worksheets[index] = chart # Store ref for iterator + @worksheets << chart # Store ref for iterator else - # Set index to 0 so that the activate() and set_first_sheet() methods - # point back to the first worksheet if used for embedded charts. - chart.index = 0 - chart.set_embedded_config_data end chart end @@ -363,25 +352,22 @@ # This feature is semi-deprecated in favour of the "native" charts created # using add_chart(). Read external_charts.txt in the external_charts # directory of the distro for a full explanation. # def add_chart_ext(filename, chartname, name_utf16be = false) - index = @worksheets.size type = 'extarnal' name, name_utf16be = check_sheetname(chartname, name_utf16be) init_data = [ filename, name, - index, - name_utf16be, - @sinfo + name_utf16be ] chart = Writeexcel::Chart.factory(self, type, init_data) - @worksheets[index] = chart # Store ref for iterator + @worksheets << chart # Store ref for iterator chart end # # The add_format method can be used to create new Format objects which are @@ -396,11 +382,11 @@ def add_format(*args) fmts = {} args.each { |arg| fmts = fmts.merge(arg) } format = Writeexcel::Format.new(@xf_index, @default_formats.merge(fmts)) @xf_index += 1 - formats.push format # Store format reference + @formats.push format # Store format reference format end # # Set the compatibility mode. @@ -804,14 +790,10 @@ # Set a flag for when the files is written. @add_doc_properties = true end - def str_unique=(val) # :nodoc: - @sinfo[:str_unique] = val - end - def extsst_buckets # :nodoc: @extsst_buckets end def extsst_bucket_size # :nodoc: @@ -828,10 +810,14 @@ def localtime=(val) # :nodoc: @localtime = val end + def update_str_table(str) + @shared_string_table << str + end + #========================================== # Internal method #========================================== private @@ -1117,29 +1103,17 @@ # Calculate size required for MSO records and update worksheets. calc_mso_sizes # Ensure that at least one worksheet has been selected. - @worksheets[0].select if @sinfo[:activesheet] == 0 - - # Calculate the number of selected sheet tabs and set the active sheet. - @worksheets.each do |sheet| - @selected += 1 if sheet.selected? - sheet.active = 1 if sheet.index == @sinfo[:activesheet] + unless @worksheets.activesheet + @worksheets.activesheet = @worksheets.first + @worksheets.activesheet.select end # Add Workbook globals - store_bof(0x0005) - store_codepage - store_window1 - store_hideobj - store_1904 - store_all_fonts - store_all_num_formats - store_all_xfs - store_all_styles - store_palette + add_workbook_globals # Calculate the offsets required by the BOUNDSHEET records calc_sheet_offsets # Add BOUNDSHEET records. @@ -1164,10 +1138,23 @@ # Store the workbook in an OLE container store_ole_file end + def add_workbook_globals + store_bof(0x0005) + store_codepage + store_window1 + store_hideobj + store_1904 + store_all_fonts + store_all_num_formats + store_all_xfs + store_all_styles + store_palette + end + # # Store the workbook in an OLE container using the default handler or using # OLE::Storage_Lite if the workbook data is > ~ 7MB. # def store_ole_file #:nodoc: @@ -1261,11 +1248,11 @@ # Add the length of the SST and associated CONTINUEs offset += calculate_shared_string_sizes # Add the length of the EXTSST record. - offset += calculate_extsst_size + offset += calculate_extsst_size(@shared_string_table.str_unique) # Add the length of the SUPBOOK, EXTERNSHEET and NAME records offset += calculate_extern_sizes # Add the length of the MSODRAWINGGROUP records including an extra 4 bytes @@ -1294,14 +1281,11 @@ # # In the following SPID is shape id, according to Escher nomenclature. # def calc_mso_sizes #:nodoc: mso_size = 0 # Size of the MSODRAWINGGROUP record - start_spid = 1024 # Initial spid for each sheet max_spid = 1024 # spidMax - num_clusters = 1 # cidcl - shapes_saved = 0 # cspSaved drawings_saved = 0 # cdgSaved clusters = [] process_images @@ -1312,56 +1296,27 @@ # and space required to store the record and the MSODRAWING parameters # required by each worksheet. # @worksheets.each do |sheet| next unless sheet.type == 0x0000 + next if sheet.num_shapes == 1 - num_images = sheet.num_images - num_comments = sheet.prepare_comments - num_charts = sheet.prepare_charts - num_filters = sheet.filter_count - - next if num_images + num_comments + num_charts + num_filters == 0 - - # Include 1 parent MSODRAWING shape, per sheet, in the shape count. - num_shapes = 1 + num_images + num_comments + - num_charts + num_filters - shapes_saved += num_shapes - mso_size += sheet.image_mso_size - - # Add a drawing object for each sheet with comments. - drawings_saved += 1 - - # For each sheet start the spids at the next 1024 interval. - max_spid = 1024 * (1 + Integer((max_spid -1)/1024.0)) - start_spid = max_spid - - # Max spid for each sheet and eventually for the workbook. - max_spid += num_shapes - - # Store the cluster ids - i = num_shapes - while i > 0 - num_clusters += 1 - mso_size += 8 - size = i > 1024 ? 1024 : i - - clusters.push([drawings_saved, size]) - i -= 1024 - end - - # Pass calculated values back to the worksheet - sheet.object_ids = [start_spid, drawings_saved, num_shapes, max_spid -1] + mso_size, drawings_saved, max_spid, start_spid = + sheet.push_object_ids(mso_size, drawings_saved, max_spid, start_spid, clusters) end # Calculate the MSODRAWINGGROUP size if we have stored some shapes. - mso_size += 86 if mso_size != 0 # Smallest size is 86+8=94 + mso_size += 86 if mso_size != 0 # Smallest size is 86+8=94 + shapes_saved = sheets.collect { |sheet| sheet.num_shapes }. + select { |num_shapes| num_shapes > 1 }. + inject(0) { |result, num_shapes| result + num_shapes } + @mso_size = mso_size @mso_clusters = [ - max_spid, num_clusters, shapes_saved, + max_spid, clusters.size + 1, shapes_saved, drawings_saved, clusters ] end # @@ -1382,11 +1337,11 @@ image_id = 1 images_size = 0 @worksheets.each do |sheet| next unless sheet.type == 0x0000 - next if sheet.prepare_images == 0 + next if sheet.images_size == 0 num_images = 0 image_mso_size = 0 sheet.images_array.each do |image| @@ -1437,17 +1392,15 @@ # # Store the Excel FONT records. # def store_all_fonts #:nodoc: - format = formats[15] # The default cell format. + format = @formats[15] # The default cell format. font = format.get_font # Fonts are 0-indexed. According to the SDK there is no index 4, - (0..3).each do - append(font) - end + 4.times { append(font) } # Add the default fonts for charts and comments. This aren't connected # to XF formats. Note, the font size, and some other properties of # chart fonts are set in the FBI record of the chart. @@ -1500,11 +1453,11 @@ fonts[key] = 0 # Index of the default font # Fonts that are marked as '_font_only' are always stored. These are used # mainly for charts and may not have an associated XF record. - formats.each do |fmt| + @formats.each do |fmt| key = fmt.get_font_key if fmt.font_only == 0 and !fonts[key].nil? # FONT has already been used fmt.font_index = fonts[key] else @@ -1530,11 +1483,11 @@ index = 164 # User defined FORMAT records start from 0xA4 # Iterate through the XF objects and write a FORMAT record if it isn't a # built-in format type and if the FORMAT string hasn't already been used. # - formats.each do |format| + @formats.each do |format| num_format = format.num_format encoding = format.num_format_enc # Check if $num_format is an index to a built-in format. # Also check for a string of zeros, which is a valid format string @@ -1559,11 +1512,11 @@ # # Write all XF records. # def store_all_xfs #:nodoc: - formats.each do |format| + @formats.each do |format| xf = format.get_xf append(xf) end end @@ -1622,94 +1575,35 @@ create_print_title_name_records(sorted_worksheets) end def create_autofilter_name_records(sorted_worksheets) #:nodoc: sorted_worksheets.each do |worksheet| - index = worksheet.index - - # Write a Name record if Autofilter has been defined - if worksheet.filter_count != 0 - store_name_short( - worksheet.index, - 0x0D, # NAME type = Filter Database - @ext_refs["#{index}:#{index}"], - worksheet.filter_area[0], - worksheet.filter_area[1], - worksheet.filter_area[2], - worksheet.filter_area[3], - 1 # Hidden - ) - end + append(*worksheet.autofilter_name_record_short(true)) end end def create_print_area_name_records(sorted_worksheets) #:nodoc: sorted_worksheets.each do |worksheet| - index = worksheet.index - - # Write a Name record if the print area has been defined - if !worksheet.print_rowmin.nil? - store_name_short( - worksheet.index, - 0x06, # NAME type = Print_Area - @ext_refs["#{index}:#{index}"], - worksheet.print_rowmin, - worksheet.print_rowmax, - worksheet.print_colmin, - worksheet.print_colmax - ) - end + append(*worksheet.print_area_name_record_short) end end def create_print_title_name_records(sorted_worksheets) #:nodoc: sorted_worksheets.each do |worksheet| - index = worksheet.index - rowmin = worksheet.title_rowmin - rowmax = worksheet.title_rowmax - colmin = worksheet.title_colmin - colmax = worksheet.title_colmax - key = "#{index}:#{index}" - ref = @ext_refs[key] - # Determine if row + col, row, col or nothing has been defined # and write the appropriate record # - if rowmin && colmin + if worksheet.title_range.row_min && worksheet.title_range.col_min # Row and column titles have been defined. # Row title has been defined. - store_name_long( - worksheet.index, - 0x07, # NAME type = Print_Titles - ref, - rowmin, - rowmax, - colmin, - colmax - ) - elsif rowmin + append(*worksheet.print_title_name_record_long) + elsif worksheet.title_range.row_min # Row title has been defined. - store_name_short( - worksheet.index, - 0x07, # NAME type = Print_Titles - ref, - rowmin, - rowmax, - 0x00, - 0xff - ) - elsif colmin + append(*worksheet.print_title_name_record_short) + elsif worksheet.title_range.col_min # Column title has been defined. - store_name_short( - worksheet.index, - 0x07, # NAME type = Print_Titles - ref, - 0x0000, - 0xffff, - colmin, - colmax - ) + append(*worksheet.print_title_name_record_short) else # Nothing left to do end end end @@ -1732,16 +1626,15 @@ y_pos = 0x0000 # Vertical position of window dx_win = 0x355C # Width of window dy_win = 0x30ED # Height of window grbit = 0x0038 # Option flags - ctabsel = @selected # Number of workbook tabs selected + ctabsel = @worksheets.selected_count # Number of workbook tabs selected tab_ratio = 0x0258 # Tab to scrollbar ratio - tab_cur = @sinfo[:activesheet] # Active worksheet - tab_first = @sinfo[:firstsheet] # 1st displayed worksheet - + tab_cur = @worksheets.activesheet_index # Active worksheet + tab_first = @worksheets.firstsheet_index # 1st displayed worksheet header = [record, length].pack("vv") data = [ x_pos, y_pos, dx_win, dy_win, grbit, tab_cur, tab_first, @@ -1759,33 +1652,11 @@ # type # Worksheet type # hidden # Worksheet hidden flag # encoding # Sheet name encoding # def store_boundsheet(sheet) #:nodoc: - sheetname = sheet.name - offset = sheet.offset - type = sheet.type - hidden = sheet.hidden? ? 1 : 0 - encoding = sheet.is_name_utf16be? ? 1 : 0 - - record = 0x0085 # Record identifier - length = 0x08 + sheetname.bytesize # Number of bytes to follow - - cch = sheetname.bytesize # Length of sheet name - - grbit = type | hidden - - # Character length is num of chars not num of bytes - cch /= 2 if encoding != 0 - - # Change the UTF-16 name from BE to LE - sheetname = sheetname.unpack('v*').pack('n*') if encoding != 0 - - header = [record, length].pack("vv") - data = [offset, grbit, cch, encoding].pack("VvCC") - - append(header, data, sheetname) + append(sheet.boundsheet) end # # Write Excel BIFF STYLE records. # @@ -1830,11 +1701,11 @@ # Handle Unicode format strings. if encoding == 1 raise "Uneven number of bytes in Unicode font name" if cch % 2 != 0 cch /= 2 if encoding != 0 - format = format.unpack('n*').pack('v*') + format = utf16be_to_16le(format) end =begin # Special case to handle Euro symbol, 0x80, in non-Unicode strings. if encoding == 0 and format =~ /\x80/ @@ -1962,139 +1833,10 @@ append(header, data) end # - # Store the NAME record in the short format that is used for storing the print - # area, repeat rows only and repeat columns only. - # - # index # Sheet index - # type - # ext_ref # TODO - # rowmin # Start row - # rowmax # End row - # colmin # Start column - # colmax # end column - # hidden # Name is hidden - # - def store_name_short(index, type, ext_ref, rowmin, rowmax, colmin, colmax, hidden = nil) #:nodoc: - record = 0x0018 # Record identifier - length = 0x001b # Number of bytes to follow - - grbit = 0x0020 # Option flags - chkey = 0x00 # Keyboard shortcut - cch = 0x01 # Length of text name - cce = 0x000b # Length of text definition - unknown01 = 0x0000 # - ixals = index + 1 # Sheet index - unknown02 = 0x00 # - cch_cust_menu = 0x00 # Length of cust menu text - cch_description = 0x00 # Length of description text - cch_helptopic = 0x00 # Length of help topic text - cch_statustext = 0x00 # Length of status bar text - rgch = type # Built-in name type - unknown03 = 0x3b # - - grbit = 0x0021 if hidden - - header = [record, length].pack("vv") - data = [grbit].pack("v") - data += [chkey].pack("C") - data += [cch].pack("C") - data += [cce].pack("v") - data += [unknown01].pack("v") - data += [ixals].pack("v") - data += [unknown02].pack("C") - data += [cch_cust_menu].pack("C") - data += [cch_description].pack("C") - data += [cch_helptopic].pack("C") - data += [cch_statustext].pack("C") - data += [rgch].pack("C") - data += [unknown03].pack("C") - data += [ext_ref].pack("v") - - data += [rowmin].pack("v") - data += [rowmax].pack("v") - data += [colmin].pack("v") - data += [colmax].pack("v") - - append(header, data) - end - - # - # Store the NAME record in the long format that is used for storing the repeat - # rows and columns when both are specified. This share a lot of code with - # store_name_short() but we use a separate method to keep the code clean. - # Code abstraction for reuse can be carried too far, and I should know. ;-) - # - # index # Sheet index - # type - # ext_ref # TODO - # rowmin # Start row - # rowmax # End row - # colmin # Start column - # colmax # end column - # - def store_name_long(index, type, ext_ref, rowmin, rowmax, colmin, colmax) #:nodoc: - record = 0x0018 # Record identifier - length = 0x002a # Number of bytes to follow - - grbit = 0x0020 # Option flags - chkey = 0x00 # Keyboard shortcut - cch = 0x01 # Length of text name - cce = 0x001a # Length of text definition - unknown01 = 0x0000 # - ixals = index + 1 # Sheet index - unknown02 = 0x00 # - cch_cust_menu = 0x00 # Length of cust menu text - cch_description = 0x00 # Length of description text - cch_helptopic = 0x00 # Length of help topic text - cch_statustext = 0x00 # Length of status bar text - rgch = type # Built-in name type - - unknown03 = 0x29 - unknown04 = 0x0017 - unknown05 = 0x3b - - header = [record, length].pack("vv") - data = [grbit].pack("v") - data += [chkey].pack("C") - data += [cch].pack("C") - data += [cce].pack("v") - data += [unknown01].pack("v") - data += [ixals].pack("v") - data += [unknown02].pack("C") - data += [cch_cust_menu].pack("C") - data += [cch_description].pack("C") - data += [cch_helptopic].pack("C") - data += [cch_statustext].pack("C") - data += [rgch].pack("C") - - # Column definition - data += [unknown03].pack("C") - data += [unknown04].pack("v") - data += [unknown05].pack("C") - data += [ext_ref].pack("v") - data += [0x0000].pack("v") - data += [0xffff].pack("v") - data += [colmin].pack("v") - data += [colmax].pack("v") - - # Row definition - data += [unknown05].pack("C") - data += [ext_ref].pack("v") - data += [rowmin].pack("v") - data += [rowmax].pack("v") - data += [0x00].pack("v") - data += [0xff].pack("v") - # End of data - data += [0x10].pack("C") - - append(header, data) - end - - # # Stores the PALETTE biff record. # def store_palette #:nodoc: record = 0x0092 # Record identifier length = 2 + 4 * @palette.size # Number of bytes to follow @@ -2171,29 +1913,26 @@ defined_names.each do |defined_name| length += 19 + defined_name[:name].bytesize + defined_name[:formula].bytesize end @worksheets.each do |worksheet| - - rowmin = worksheet.title_rowmin - colmin = worksheet.title_colmin key = "#{index}:#{index}" index += 1 # Add area NAME records # - if worksheet.print_rowmin + if worksheet.print_range.row_min add_ext_refs(ext_refs, key) unless ext_refs[key] length += 31 end # Add title NAME records # - if rowmin and colmin + if worksheet.title_range.row_min && worksheet.title_range.col_min add_ext_refs(ext_refs, key) unless ext_refs[key] length += 46 - elsif rowmin or colmin + elsif worksheet.title_range.row_min || worksheet.title_range.col_min add_ext_refs(ext_refs, key) unless ext_refs[key] length += 31 else # TODO, may need this later. end @@ -2240,120 +1979,27 @@ # The first pass through the data is also used to calculate the size of the SST # and CONTINUE records for use in setting the BOUNDSHEET record offsets. The # downside of this is that the same algorithm repeated in store_shared_strings. # def calculate_shared_string_sizes #:nodoc: - strings = Array.new(@sinfo[:str_unique]) + str_block_sizes = @shared_string_table.block_sizes - @sinfo[:str_table].each_key do |key| - strings[@sinfo[:str_table][key]] = key - end - # The SST data could be very large, free some memory (maybe). - @sinfo[:str_table] = nil - @str_array = strings - - # Iterate through the strings to calculate the CONTINUE block sizes. - # - # The SST blocks requires a specialised CONTINUE block, so we have to - # ensure that the maximum data block size is less than the limit used by - # add_continue() in BIFFwriter.pm. For simplicity we use the same size - # for the SST and CONTINUE records: - # 8228 : Maximum Excel97 block size - # -4 : Length of block header - # -8 : Length of additional SST header information - # -8 : Arbitrary number to keep within add_continue() limit - # = 8208 - # - continue_limit = 8208 - block_length = 0 - written = 0 - block_sizes = [] - continue = 0 - - strings.each do |string| - string_length = string.bytesize - - # Block length is the total length of the strings that will be - # written out in a single SST or CONTINUE block. - # - block_length += string_length - - # We can write the string if it doesn't cross a CONTINUE boundary - if block_length < continue_limit - written += string_length - next - end - - # Deal with the cases where the next string to be written will exceed - # the CONTINUE boundary. If the string is very long it may need to be - # written in more than one CONTINUE record. - encoding = string.unpack("xx C")[0] - split_string = 0 - while block_length >= continue_limit - header_length, space_remaining, align, split_string = - split_string_setup(encoding, split_string, continue_limit, written, continue) - - if space_remaining > header_length - # Write as much as possible of the string in the current block - written += space_remaining - - # Reduce the current block length by the amount written - block_length -= continue_limit -continue -align - - # Store the max size for this block - block_sizes.push(continue_limit -align) - - # If the current string was split then the next CONTINUE block - # should have the string continue flag (grbit) set unless the - # split string fits exactly into the remaining space. - # - if block_length > 0 - continue = 1 - else - continue = 0 - end - else - # Store the max size for this block - block_sizes.push(written +continue) - - # Not enough space to start the string in the current block - block_length -= continue_limit -space_remaining -continue - continue = 0 - end - - # If the string (or substr) is small enough we can write it in the - # new CONTINUE block. Else, go through the loop again to write it in - # one or more CONTINUE blocks - # - if block_length < continue_limit - written = block_length - else - written = 0 - end - end - end - - # Store the max size for the last block unless it is empty - block_sizes.push(written +continue) if written +continue != 0 - - @str_block_sizes = block_sizes.dup - # Calculate the total length of the SST and associated CONTINUEs (if any). # The SST record will have a length even if it contains no strings. # This length is required to set the offsets in the BOUNDSHEET records since # they must be written before the SST records # - length = 12 - length += block_sizes.shift unless block_sizes.empty? # SST - while !block_sizes.empty? do - length += 4 + block_sizes.shift # CONTINUEs - end + # when array = [a, b, c] # array not empty? + # length = 12 + a + 4 + b + 4 + c = 12 + a + b + c + 4 * (array.size - 1) + # + length = str_block_sizes.inject(12){|result, item| result + item} + length += 4 * (str_block_sizes.size - 1) unless str_block_sizes.empty? length end - def split_string_setup(encoding, split_string, continue_limit, written, continue) #:nodoc: + def self.split_string_setup(encoding, split_string, continue_limit, written, continue) #:nodoc: # We need to avoid the case where a string is continued in the first # n bytes that contain the string header information. header_length = 3 # Min string + header size -1 space_remaining = continue_limit - written - continue @@ -2380,10 +2026,12 @@ end end [header_length, space_remaining, align, split_string] end + def_delegator self, :split_string_setup + # # Write all of the workbooks strings into an indexed array. # # See the comments in calculate_shared_string_sizes() for more information. # @@ -2391,12 +2039,10 @@ # In order to do this we first identify the first string in an EXTSST bucket # and then store its global and local offset within the SST table. The offset # occurs wherever the start of the bucket string is written out via append(). # def store_shared_strings #:nodoc: - strings = @str_array - record = 0x00FC # Record identifier length = 0x0008 # Number of bytes to follow total = 0x0000 # Iterate through the strings to calculate the CONTINUE block sizes @@ -2405,11 +2051,11 @@ written = 0 continue = 0 # The SST and CONTINUE block sizes have been pre-calculated by # calculate_shared_string_sizes() - block_sizes = @str_block_sizes + block_sizes = @shared_string_table.block_sizes # The SST record is required even if it contains no strings. Thus we will # always have a length # if block_sizes.size != 0 @@ -2423,16 +2069,16 @@ extsst_str_num = -1 sst_block_start = @datasize # Write the SST block header information header = [record, length].pack("vv") - data = [@sinfo[:str_total], @sinfo[:str_unique]].pack("VV") + data = [@shared_string_table.str_total, @shared_string_table.str_unique].pack("VV") append(header, data) # Iterate through the strings and write them out - return if strings.empty? - strings.each do |string| + return if @shared_string_table.strings.empty? + @shared_string_table.strings.each do |string| string_length = string.bytesize # Check if the string is at the start of a EXTSST bucket. extsst_str_num += 1 @@ -2539,12 +2185,12 @@ # strings per bucket (bucket size) has a minimum value of 8 and a theoretical # maximum of 2^16. For "number of strings" < 1024 there is a constant bucket # size of 8. The following algorithm generates the same size/bucket ratio # as Excel. # - def calculate_extsst_size #:nodoc: - unique_strings = @sinfo[:str_unique] + def calculate_extsst_size(str_unique) #:nodoc: + unique_strings = str_unique if unique_strings < 1024 bucket_size = 8 else bucket_size = 1 + Integer(unique_strings / 128.0) @@ -2626,11 +2272,11 @@ mso_group = 0x00EB # Record identifier continue = 0x003C # Record identifier block_count = 1 # Ignore the base class add_continue() method. - @ignore_continue = 1 + @ignore_continue = true # Case 1 above. Just return the data as it is. if data.bytesize <= limit append(data) return @@ -2659,11 +2305,11 @@ # Last CONTINUE block for remaining data. Case 2 and 3 above. header = [continue, data.bytesize].pack("vv") append(header, data) # Turn the base class add_continue() method back on. - @ignore_continue = 0 + @ignore_continue = false end def devide_string(string, nth) #:nodoc: first_string = string[0, nth] latter_string = string[nth, string.size - nth] @@ -2817,13 +2463,9 @@ sheets.each { |sheet| sheet.cleanup } end def add_doc_properties @add_doc_properties ||= false - end - - def formats - @formats end def defined_names @defined_names end