lib/writeexcel/workbook.rb in writeexcel-0.4.0 vs lib/writeexcel/workbook.rb in writeexcel-0.4.1

- old
+ new

@@ -12,23 +12,15 @@ # converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp # require 'digest/md5' require 'nkf' require 'writeexcel/biffwriter' -require 'writeexcel/olewriter' -require 'writeexcel/formula' -require 'writeexcel/format' require 'writeexcel/worksheet' require 'writeexcel/chart' -require 'writeexcel/charts/area' -require 'writeexcel/charts/bar' -require 'writeexcel/charts/column' -require 'writeexcel/charts/external' -require 'writeexcel/charts/line' -require 'writeexcel/charts/pie' -require 'writeexcel/charts/scatter' -require 'writeexcel/charts/stock' +require 'writeexcel/format' +require 'writeexcel/formula' +require 'writeexcel/olewriter' require 'writeexcel/storage_lite' require 'writeexcel/compatibility' class Workbook < BIFFWriter require 'writeexcel/properties' @@ -110,15 +102,14 @@ :str_unique => 0, :str_table => {} } @str_array = [] @str_block_sizes = [] - @extsst_offsets = [] + @extsst_offsets = [] # array of [global_offset, local_offset] @extsst_buckets = 0 @extsst_bucket_size = 0 - @ext_ref_count = 0 @ext_refs = {} @mso_clusters = [] @mso_size = 0 @@ -156,11 +147,11 @@ add_format(:type => 1, :num_format => 0x09) # 20 Percent # Add the default format for hyperlinks @url_format = add_format(:color => 'blue', :underline => 1) - if file.kind_of?(String) && file != '' + if file.respond_to?(:to_str) && file != '' @fh_out = open(file, "wb") @internal_fh = 1 else @fh_out = file end @@ -521,12 +512,11 @@ end end # Handle utf8 strings if name.encoding == Encoding::UTF_8 - name = NKF.nkf('-w16B0 -m0 -W', name) - name.force_encoding('UTF-16BE') + name = utf8_to_16be(name) encoding = 1 end # Check that the worksheet name doesn't already exist since this is a fatal # error in Excel 97. The check must also exclude case insensitive matches @@ -539,46 +529,37 @@ name_b = worksheet.name encd_b = worksheet.encoding error = 0; if encd_a == 0 and encd_b == 0 - error = 1 if name_a.downcase == name_b.downcase + error = (name_a.downcase == name_b.downcase) elsif encd_a == 0 and encd_b == 1 - name_a = name_a.unpack("C*").pack("n*") - error = 1 if name_a.downcase == name_b.downcase + name_a = ascii_to_16be(name_a) + error = (name_a.downcase == name_b.downcase) elsif encd_a == 1 and encd_b == 0 - name_b = name_b.unpack("C*").pack("n*") - error = 1 if name_a.downcase == name_b.downcase + name_b = ascii_to_16be(name_b) + error = (name_a.downcase == name_b.downcase) elsif encd_a == 1 and encd_b == 1 - # # We can do a true case insensitive test with Perl 5.8 and utf8. - # if ($] >= 5.008) { - # $name_a = Encode::decode("UTF-16BE", $name_a); - # $name_b = Encode::decode("UTF-16BE", $name_b); - # $error = 1 if lc($name_a) eq lc($name_b); - # } - # else { - # # We can't easily do a case insensitive test of the UTF16 names. - # # As a special case we check if all of the high bytes are nulls and - # # then do an ASCII style case insensitive test. + # TODO : not converted yet. + + # We can't easily do a case insensitive test of the UTF16 names. + # As a special case we check if all of the high bytes are nulls and + # then do an ASCII style case insensitive test. # - # # Strip out the high bytes (funkily). - # my $hi_a = grep {ord} $name_a =~ /(.)./sg; - # my $hi_b = grep {ord} $name_b =~ /(.)./sg; + # Strip out the high bytes (funkily). + # my $hi_a = grep {ord} $name_a =~ /(.)./sg; + # my $hi_b = grep {ord} $name_b =~ /(.)./sg; # - # if ($hi_a or $hi_b) { - # $error = 1 if $name_a eq $name_b; - # } - # else { - # $error = 1 if lc($name_a) eq lc($name_b); - # } - # } - # } - # # If any of the cases failed we throw the error here. + # if ($hi_a or $hi_b) { + # $error = 1 if $name_a eq $name_b; + # } + # else { + # $error = 1 if lc($name_a) eq lc($name_b); + # } end - if error != 0 - raise "Worksheet name '#{name}', with case ignored, " + - "is already in use" + if error + raise "Worksheet name '#{name}', with case ignored, is already in use" end end [name, encoding] end private :check_sheetname @@ -591,11 +572,13 @@ # format1 = workbook.add_format(props) # Set properties at creation # format2 = workbook.add_format # Set properties later # # See the "CELL FORMATTING" section for more details about Format properties and how to set them. # - def add_format(formats = {}) + def add_format(*args) + formats = {} + args.each { |arg| formats = formats.merge(arg) } format = Format.new(@xf_index, @default_formats.merge(formats)) @xf_index += 1 @formats.push format # Store format reference format end @@ -1021,14 +1004,14 @@ # # See also the properties.rb program in the examples directory of the distro. # def set_properties(params) # Ignore if no args were passed. - return -1 if !params.kind_of?(Hash) || params.empty? + return -1 if !params.respond_to?(:to_hash) || params.empty? params.each do |k, v| - params[k] = convert_to_ascii_if_ascii(v) if v.kind_of?(String) + params[k] = convert_to_ascii_if_ascii(v) if v.respond_to?(:to_str) end # List of valid input parameters. properties = { :codepage => [0x0001, 'VT_I2' ], :title => [0x0002, 'VT_LPSTR' ], @@ -1066,18 +1049,13 @@ # Create an array of property set values. property_sets = [] strings.unshift("codepage") strings.push("created") - strings.each do |property| - if params.has_key?(property.to_sym) && !params[property.to_sym].nil? - property_sets.push( - [ properties[property.to_sym][0], - properties[property.to_sym][1], - params[property.to_sym] ] - ) - end + strings.each do |string| + property = string.to_sym + property_sets.push(property_set(properties, property, params)) if params[property] end # Pack the property sets. @summary = create_summary_property_set(property_sets) @@ -1090,27 +1068,26 @@ params[:codepage] = get_property_set_codepage(params, strings) # Create an array of property set values. property_sets = [] - ["codepage", "category", "manager", "company"].each do |property| - if params.has_key?(property.to_sym) && !params[property.to_sym].nil? - property_sets.push( - [ properties[property.to_sym][0], - properties[property.to_sym][1], - params[property.to_sym] ] - ) - end + [:codepage, :category, :manager, :company].each do |property| + property_sets.push(property_set(properties, property, params)) if params[property] end # Pack the property sets. @doc_summary = create_doc_summary_property_set(property_sets) # Set a flag for when the files is written. @add_doc_properties = 1 end + def property_set(properties, property, params) + [ properties[property][0], properties[property][1], params[property] ] + end + private :property_set + ############################################################################### # # _get_property_set_codepage() # # Get the character codepage used by the strings in a property set. If one of @@ -1181,11 +1158,11 @@ end # NOTE: If any records are added between here and EOF the # _calc_sheet_offsets() should be updated to include the new length. store_country - if @ext_ref_count != 0 + if @ext_refs.keys.size != 0 store_supbook store_externsheet store_names end add_mso_drawing_group @@ -1194,11 +1171,11 @@ # End Workbook globals store_eof # Store the workbook in an OLE container - store_OLE_file + store_ole_filie end private :store_workbook def str_unique=(val) # :nodoc: @sinfo[:str_unique] = val @@ -1224,16 +1201,16 @@ @localtime = val end ############################################################################### # - # _store_OLE_file() + # _store_ole_filie() # # 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: + def store_ole_filie #:nodoc: maxsize = 7_087_104 # maxsize = 1 if @add_doc_properties == 0 && @biffsize <= maxsize # Write the OLE file using OLEwriter if data <= 7MB @@ -1264,17 +1241,15 @@ stream = 'Workbook'.unpack('C*').pack('v*') workbook = OLEStorageLitePPSFile.new(stream) workbook.set_file # use tempfile while tmp = get_data - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) workbook.append(tmp) end @worksheets.each do |worksheet| while tmp = worksheet.get_data - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) workbook.append(tmp) end end streams = [] @@ -1302,11 +1277,11 @@ # Close the filehandle if it was created internally. return @fh_out.close if @internal_fh != 0 end end - private :store_OLE_file + private :store_ole_filie ############################################################################### # # _calc_sheet_offsets() # @@ -1706,11 +1681,10 @@ 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 - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(font) end # Add the default fonts for charts and comments. This aren't connected # to XF formats. Note, the font size, and some other properties of @@ -1719,47 +1693,42 @@ # Index 5. Axis numbers. tmp_format = Format.new( nil, :font_only => 1 ) - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(tmp_format.get_font) # Index 6. Series names. tmp_format = Format.new( nil, :font_only => 1 ) - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(tmp_format.get_font) # Index 7. Title. tmp_format = Format.new( nil, :font_only => 1, :bold => 1 ) - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(tmp_format.get_font) # Index 8. Axes. tmp_format = Format.new( nil, :font_only => 1, :bold => 1 ) - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(tmp_format.get_font) # Index 9. Comments. tmp_format = Format.new( nil, :font_only => 1, :font => 'Tahoma', :size => 8 ) - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(tmp_format.get_font) # Iterate through the XF objects and write a FONT record if it isn't the # same as the default FONT and if it hasn't already been used. # @@ -1785,11 +1754,10 @@ end fmt.font_index = index index += 1 font = fmt.get_font - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(font) end end end private :store_all_fonts @@ -1840,11 +1808,10 @@ # Write all XF records. # def store_all_xfs #:nodoc: @formats.each do |format| xf = format.get_xf - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(xf) end end private :store_all_xfs @@ -2009,31 +1976,30 @@ # def store_window1 #:nodoc: record = 0x003D # Record identifier length = 0x0012 # Number of bytes to follow - xWn = 0x0000 # Horizontal position of window - yWn = 0x0000 # Vertical position of window - dxWn = 0x355C # Width of window - dyWn = 0x30ED # Height of window + x_pos = 0x0000 # Horizontal position of window + 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 - wTabRatio = 0x0258 # Tab to scrollbar ratio + tab_ratio = 0x0258 # Tab to scrollbar ratio - itabFirst = @sinfo[:firstsheet] # 1st displayed worksheet - itabCur = @sinfo[:activesheet] # Active worksheet + tab_cur = @sinfo[:activesheet] # Active worksheet + tab_first = @sinfo[:firstsheet] # 1st displayed worksheet header = [record, length].pack("vv") data = [ - xWn, yWn, dxWn, dyWn, + x_pos, y_pos, dx_win, dy_win, grbit, - itabCur, itabFirst, - ctabsel, wTabRatio + tab_cur, tab_first, + ctabsel, tab_ratio ].pack("vvvvvvvvv") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data) end private :store_window1 ############################################################################### @@ -2062,11 +2028,10 @@ sheetname = sheetname.unpack('v*').pack('n*') if encoding != 0 header = [record, length].pack("vv") data = [offset, grbit, cch, encoding].pack("VvCC") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data, sheetname) end private :store_boundsheet ############################################################################### @@ -2086,11 +2051,10 @@ xf_index |= 0x8000 # Add flag to indicate built-in style. header = [record, length].pack("vv") data = [xf_index, type, level].pack("vCC") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data) end private :store_style ############################################################################### @@ -2101,22 +2065,21 @@ # my $encoding = $_[2]; # Char encoding for format string # # Writes Excel FORMAT record for non "built-in" numerical formats. # def store_num_format(format, ifmt, encoding) #:nodoc: - format = format.to_s unless format.kind_of?(String) + format = format.to_s unless format.respond_to?(:to_str) record = 0x041E # Record identifier # length # Number of bytes to follow # Char length of format string cch = format.bytesize format = convert_to_ascii_if_ascii(format) # Handle utf8 strings if format.encoding == Encoding::UTF_8 - format = NKF.nkf('-w16B0 -m0 -W', format) - format.force_encoding('UTF-16BE') + format = utf8_to_16be(format) encoding = 1 end # Handle Unicode format strings. if encoding == 1 @@ -2136,11 +2099,10 @@ length = 0x05 + format.bytesize header = [record, length].pack("vv") data = [ifmt, cch, encoding].pack("vvC") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data, format) end private :store_num_format ############################################################################### @@ -2156,11 +2118,10 @@ f1904 = @date_1904 ? 1 : 0 # Flag for 1904 date system header = [record, length].pack("vv") data = [f1904].pack("v") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data) end private :store_1904 ############################################################################### @@ -2172,17 +2133,16 @@ # def store_supbook #:nodoc: record = 0x01AE # Record identifier length = 0x0004 # Number of bytes to follow - ctabs = @worksheets.size # Number of worksheets - stVirtPath = 0x0401 # Encoded workbook filename + tabs = @worksheets.size # Number of worksheets + virt_path = 0x0401 # Encoded workbook filename header = [record, length].pack("vv") - data = [ctabs, stVirtPath].pack("vv") + data = [tabs, virt_path].pack("vv") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data) end private :store_supbook ############################################################################### @@ -2195,12 +2155,11 @@ # def store_externsheet # :nodoc: record = 0x0017 # Record identifier # Get the external refs - ext_refs = @ext_refs - ext = ext_refs.keys.sort + ext = @ext_refs.keys.sort # Change the external refs from stringified "1:1" to [1, 1] ext.map! {|e| e.split(/:/).map! {|v| v.to_i} } cxti = ext.size # Number of Excel XTI structures @@ -2212,11 +2171,10 @@ end data = [cxti].pack("v") + rgxti header = [record, data.bytesize].pack("vv") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data) end # # Store the NAME record used for storing the print area, repeat rows, repeat @@ -2265,11 +2223,10 @@ data += name data += formula header = [record, data.bytesize].pack("vv") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data) end ############################################################################### # @@ -2290,47 +2247,46 @@ 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 + 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 # - cchCustMenu = 0x00 # Length of cust menu text - cchDescription = 0x00 # Length of description text - cchHelptopic = 0x00 # Length of help topic text - cchStatustext = 0x00 # Length of status bar text + 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 += [chkey].pack("C") data += [cch].pack("C") data += [cce].pack("v") data += [unknown01].pack("v") data += [ixals].pack("v") data += [unknown02].pack("C") - data += [cchCustMenu].pack("C") - data += [cchDescription].pack("C") - data += [cchHelptopic].pack("C") - data += [cchStatustext].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") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data) end private :store_name_short ############################################################################### @@ -2353,38 +2309,38 @@ 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 + 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 # - cchCustMenu = 0x00 # Length of cust menu text - cchDescription = 0x00 # Length of description text - cchHelptopic = 0x00 # Length of help topic text - cchStatustext = 0x00 # Length of status bar text + 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 += [chkey].pack("C") data += [cch].pack("C") data += [cce].pack("v") data += [unknown01].pack("v") data += [ixals].pack("v") data += [unknown02].pack("C") - data += [cchCustMenu].pack("C") - data += [cchDescription].pack("C") - data += [cchHelptopic].pack("C") - data += [cchStatustext].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") @@ -2403,11 +2359,10 @@ data += [0x00].pack("v") data += [0xff].pack("v") # End of data data += [0x10].pack("C") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data) end private :store_name_long ############################################################################### @@ -2427,11 +2382,10 @@ data += p.pack('CCCC') end header = [record, length, ccv].pack("vvv") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data) end private :store_palette ############################################################################### @@ -2443,15 +2397,11 @@ def store_codepage #:nodoc: record = 0x0042 # Record identifier length = 0x0002 # Number of bytes to follow cv = @codepage # The code page - header = [record, length].pack("vv") - data = [cv].pack("v") - - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) - append(header, data) + store_common(record, length, cv) end private :store_codepage ############################################################################### # @@ -2463,14 +2413,11 @@ record = 0x008C # Record identifier length = 0x0004 # Number of bytes to follow country_default = @country country_win_ini = @country - header = [record, length].pack("vv") - data = [country_default, country_win_ini].pack("vv") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) - append(header, data) + store_common(record, length, country_default, country_win_ini) end private :store_country ############################################################################### # @@ -2481,101 +2428,81 @@ def store_hideobj #:nodoc: record = 0x008D # Record identifier length = 0x0002 # Number of bytes to follow hide = @hideobj # Option to hide objects - header = [record, length].pack("vv") - data = [hide].pack("v") - - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) - append(header, data) + store_common(record, length, hide) end private :store_hideobj - ############################################################################### - ############################################################################### - ############################################################################### + def store_common(record, length, *data) + header = [record, length].pack("vv") + add_data = [*data].pack("v*") + append(header, add_data) + end + private :store_common - ############################################################################### # # _calculate_extern_sizes() # # We need to calculate the space required by the SUPBOOK, EXTERNSHEET and NAME # records so that it can be added to the BOUNDSHEET offsets. # def calculate_extern_sizes # :nodoc: ext_refs = @parser.get_ext_sheets - ext_ref_count = ext_refs.keys.size length = 0 index = 0 unless @defined_names.empty? index = 0 key = "#{index}:#{index}" - unless ext_refs.has_key?(key) - ext_refs[key] = ext_ref_count - ext_ref_count += 1 - end + add_ext_refs(ext_refs, key) unless ext_refs.has_key?(key) end @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 - filter = worksheet.filter_count key = "#{index}:#{index}" index += 1 # Add area NAME records # - if !worksheet.print_rowmin.nil? - if ext_refs[key].nil? - ext_refs[key] = ext_ref_count - ext_ref_count += 1 - end + if worksheet.print_rowmin + add_ext_refs(ext_refs, key) unless ext_refs[key] length += 31 end # Add title NAME records # if rowmin and colmin - if ext_refs[key].nil? - ext_refs[key] = ext_ref_count - ext_ref_count += 1 - end - + add_ext_refs(ext_refs, key) unless ext_refs[key] length += 46 elsif rowmin or colmin - if ext_refs[key].nil? - ext_refs[key] = ext_ref_count - ext_ref_count += 1 - end + add_ext_refs(ext_refs, key) unless ext_refs[key] length += 31 else # TODO, may need this later. end # Add Autofilter NAME records # - if filter != 0 - if ext_refs[key].nil? - ext_refs[key] = ext_ref_count - ext_ref_count += 1 - end + unless worksheet.filter_count == 0 + add_ext_refs(ext_refs, key) unless ext_refs[key] length += 31 end end # Update the ref counts. - @ext_ref_count = ext_ref_count + ext_ref_count = ext_refs.keys.size @ext_refs = ext_refs # If there are no external refs then we don't write, SUPBOOK, EXTERNSHEET # and NAME. Therefore the length is 0. @@ -2588,10 +2515,15 @@ length += 6 * (1 + ext_ref_count) length end + def add_ext_refs(ext_refs, key) + ext_refs[key] = ext_refs.keys.size + end + private :add_ext_refs + ############################################################################### # # _calculate_shared_string_sizes() # # Handling of the SST continue blocks is complicated by the need to include an @@ -2633,14 +2565,11 @@ written = 0 block_sizes = [] continue = 0 strings.each do |string| - string_length = string.bytesize - encoding = string.unpack("xx C")[0] - split_string = 0 # 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 @@ -2649,49 +2578,19 @@ 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) - # 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 - - - # Unicode data should only be split on char (2 byte) boundaries. - # Therefore, in some cases we need to reduce the amount of available - # space by 1 byte to ensure the correct alignment. - align = 0 - - # Only applies to Unicode strings - if encoding == 1 - # Min string + header size -1 - header_length = 4 - - if space_remaining > header_length - # String contains 3 byte header => split on odd boundary - if split_string == 0 and space_remaining % 2 != 1 - space_remaining -= 1 - align = 1 - # Split section without header => split on even boundary - elsif split_string != 0 and space_remaining % 2 == 1 - space_remaining -= 1 - align = 1 - end - - split_string = 1 - end - end - 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 @@ -2748,10 +2647,42 @@ length end private :calculate_shared_string_sizes + def _split_string_setup(encoding, split_string, continue_limit, written, continue) + # 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 + + # Unicode data should only be split on char (2 byte) boundaries. + # Therefore, in some cases we need to reduce the amount of available + # space by 1 byte to ensure the correct alignment. + align = 0 + + # Only applies to Unicode strings + if encoding == 1 + # Min string + header size -1 + header_length = 4 + if space_remaining > header_length + # String contains 3 byte header => split on odd boundary + if split_string == 0 and space_remaining % 2 != 1 + space_remaining -= 1 + align = 1 + # Split section without header => split on even boundary + elsif split_string != 0 and space_remaining % 2 == 1 + space_remaining -= 1 + align = 1 + end + split_string = 1 + end + end + [header_length, space_remaining, align, split_string] + end + private :_split_string_setup + ############################################################################### # # _store_shared_strings() # # Write all of the workbooks strings into an indexed array. @@ -2795,106 +2726,63 @@ sst_block_start = @datasize # Write the SST block header information header = [record, length].pack("vv") data = [@sinfo[:str_total], @sinfo[:str_unique]].pack("VV") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data) # Iterate through the strings and write them out return if strings.empty? strings.each do |string| string_length = string.bytesize - encoding = string.unpack("xx C")[0] - split_string = 0 - bucket_string = 0 # Used to track EXTSST bucket offsets. # Check if the string is at the start of a EXTSST bucket. extsst_str_num += 1 - if extsst_str_num % @extsst_bucket_size == 0 - bucket_string = 1 - end + # Used to track EXTSST bucket offsets. + bucket_string = (extsst_str_num % @extsst_bucket_size == 0) # 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 # Store location of EXTSST bucket string. - if bucket_string != 0 - global_offset = @datasize - local_offset = @datasize - sst_block_start - - @extsst_offsets.push([global_offset, local_offset]) - bucket_string = 0 + if bucket_string + @extsst_offsets.push([@datasize, @datasize - sst_block_start]) + bucket_string = false end - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(string) 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) - # 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 - - - # Unicode data should only be split on char (2 byte) boundaries. - # Therefore, in some cases we need to reduce the amount of available - # space by 1 byte to ensure the correct alignment. - align = 0 - - # Only applies to Unicode strings - if encoding == 1 - # Min string + header size -1 - header_length = 4 - - if space_remaining > header_length - # String contains 3 byte header => split on odd boundary - if split_string == 0 and space_remaining % 2 != 1 - space_remaining -= 1 - align = 1 - # Split section without header => split on even boundary - elsif split_string != 0 and space_remaining % 2 == 1 - space_remaining -= 1 - align = 1 - end - - split_string = 1 - end - end - if space_remaining > header_length # Write as much as possible of the string in the current block tmp = string[0, space_remaining] # Store location of EXTSST bucket string. - if bucket_string != 0 - global_offset = @datasize - local_offset = @datasize - sst_block_start - - @extsst_offsets.push([global_offset, local_offset]) - bucket_string = 0 + if bucket_string + @extsst_offsets.push([@datasize, @datasize - sst_block_start]) + bucket_string = false end - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(tmp) - # The remainder will be written in the next block(s) string = string[space_remaining .. string.length-1] # Reduce the current block length by the amount written block_length -= continue_limit -continue -align @@ -2922,30 +2810,24 @@ length = block_sizes.shift header = [record, length].pack("vv") header += [encoding].pack("C") if continue != 0 - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header) 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 # Store location of EXTSST bucket string. - if bucket_string != 0 - global_offset = @datasize - local_offset = @datasize - sst_block_start - - @extsst_offsets.push([global_offset, local_offset]) - - bucket_string = 0 + if bucket_string + @extsst_offsets.push([@datasize, @datasize - sst_block_start]) + bucket_string = false end - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(string) written = block_length else written = 0 @@ -3000,11 +2882,10 @@ offsets.each do |offset| data += [offset[0], offset[1], 0].pack('Vvv') end - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data) end private :store_extsst # @@ -3066,20 +2947,18 @@ # Ignore the base class _add_continue() method. @ignore_continue = 1 # Case 1 above. Just return the data as it is. if data.bytesize <= limit - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(data) return end # Change length field of the first MSODRAWINGGROUP block. Case 2 and 3. tmp = data.dup tmp[0, limit + 4] = "" tmp[2, 2] = [limit].pack('v') - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(tmp) # Add MSODRAWINGGROUP and CONTINUE blocks for Case 3 above. while data.bytesize > limit if block_count == 1 @@ -3091,16 +2970,14 @@ header = [continue, limit].pack("vv") end tmp = data.dup tmp[0, limit] = '' - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, tmp) end # Last CONTINUE block for remaining data. Case 2 and 3 above. header = [continue, data.bytesize].pack("vv") - print "#{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data) # Turn the base class _add_continue() method back on. @ignore_continue = 0 end