lib/writeexcel/workbook.rb in writeexcel-0.6.4 vs lib/writeexcel/workbook.rb in writeexcel-0.6.5
- old
+ new
@@ -24,22 +24,27 @@
class Workbook < BIFFWriter
require 'writeexcel/properties'
require 'writeexcel/helper'
- NonAscii = /[^!"#\$%&'\(\)\*\+,\-\.\/\:\;<=>\?@0-9A-Za-z_\[\\\]\{\}^` ~\0\n]/
-
- BOF = 11 # :nodoc:
+ BOF = 12 # :nodoc:
EOF = 4 # :nodoc:
SheetName = "Sheet" # :nodoc:
+ private
+
+ attr_accessor :add_doc_properties #:nodoc:
+ attr_reader :formats, :defined_names #:nodoc:
+
+ public
+
#
- # file is a filename (as string) or io object where to out spreadsheet data.
- # you can set default format of workbook using default_formats.
+ # _file_ is a filename (as string) or io object where to out spreadsheet data.
+ # you can set default format of workbook using _default_formats_.
#
# A new Excel workbook is created using the new() constructor which accepts
- # either a filename or a filehandle as a parameter. The following example
+ # either a filename or an IO object as a parameter. The following example
# creates a new Excel file based on a filename:
#
# workbook = WriteExcel.new('filename.xls')
# worksheet = workbook.add_worksheet
# worksheet.write(0, 0, 'Hi Excel!')
@@ -72,12 +77,10 @@
@file = file
@default_formats = default_formats
@parser = Writeexcel::Formula.new(@byte_order)
@tempdir = nil
@date_1904 = false
- @sheet =
-
@selected = 0
@xf_index = 0
@fileclosed = false
@biffsize = 0
@sheet_name = "Sheet"
@@ -86,11 +89,10 @@
@chart_count = 0
@url_format = ''
@codepage = 0x04E4
@country = 1
@worksheets = []
- @sheetnames = []
@formats = []
@palette = []
@biff_only = 0
@internal_fh = 0
@@ -115,11 +117,10 @@
@mso_size = 0
@hideobj = 0
@compatibility = 0
- @add_doc_properties = 0
@summary = ''
@doc_summary = ''
@localtime = Time.now
@defined_names = []
@@ -163,11 +164,11 @@
get_checksum_method
end
###############################################################################
#
- # _get_checksum_method.
+ # get_checksum_method.
#
# Check for modules available to calculate image checksum. Excel uses MD4 but
# MD5 will also work.
#
# ------- cxn03651 add -------
@@ -180,29 +181,17 @@
#
# Calls finalization methods and explicitly close the OLEwriter files
# handle.
#
- # In general your Excel file will be closed automatically when your program
- # ends or when the Workbook object goes out of scope, however the close method
- # can be used to explicitly close an Excel file.
- #
- # workbook.close
- #
# An explicit close() is required if the file must be closed prior to performing
# some external action on it such as copying it, reading its size or attaching
# it to an email.
#
# In general, if you create a file with a size of 0 bytes or you fail to create
# a file you need to call close().
#
- # The return value of close() is the same as that returned by perl when it
- # closes the file created by new(). This allows you to handle error conditions
- # in the usual way:
- #
- # $workbook.close() or die "Error closing file: $!";
- #
def close
return if @fileclosed # Prevent close() from being called twice.
@fileclosed = true
store_workbook
@@ -243,15 +232,11 @@
#
def sheets(*args)
if args.empty?
@worksheets
else
- ary = []
- args.each do |i|
- ary << @worksheets[i]
- end
- ary
+ args.collect{|i| @worksheets[i] }
end
end
#
# Add a new worksheet to the Excel workbook.
@@ -264,11 +249,11 @@
# worksheet1 = workbook.add_worksheet # Sheet1
# worksheet2 = workbook.add_worksheet('Foglio2') # Foglio2
# worksheet3 = workbook.add_worksheet('Data') # Data
# worksheet4 = workbook.add_worksheet # Sheet4
#
- # If sheetname is not specified the default Excel convention will be followed,
+ # If _sheetname_ is not specified the default Excel convention will be followed,
# i.e. Sheet1, Sheet2, etc. The utf_16_be parameter is optional, see below.
#
# The worksheet name must be a valid Excel worksheet name, i.e. it cannot
# contain any of the following characters, [ ] : * ? / \
# and it must be less than 32 characters. In addition, you cannot use the same,
@@ -306,95 +291,97 @@
nil, # Palette. Not used yet. See add_chart().
@sinfo,
]
worksheet = Writeexcel::Worksheet.new(*init_data)
@worksheets[index] = worksheet # Store ref for iterator
- @sheetnames[index] = name # Store EXTERNSHEET names
@parser.set_ext_sheets(name, index) # Store names in Formula.rb
worksheet
end
- ###############################################################################
#
- # add_chart(params)
- #
# Create a chart for embedding or as as new sheet.
#
# This method is use to create a new chart either as a standalone worksheet
# (the default) or as an embeddable object that can be inserted into a
# worksheet via the insert_chart() Worksheet method.
#
# chart = workbook.add_chart(:type => 'Chart::Column')
#
# The properties that can be set are:
#
- # :type (required)
- # :name (optional)
- # :embedded (optional)
+ # :type (required)
+ # :name (optional)
+ # :encoding (optional)
+ # :embedded (optional)
#
- # * type
+ # * type
#
- # This is a required parameter. It defines the type of chart that will be created.
+ # This is a required parameter. It defines the type of chart that will be created.
#
- # chart = workbook.add_chart(:type => 'Chart::Line')
+ # chart = workbook.add_chart(:type => 'Chart::Line')
#
- # The available types are:
+ # The available types are:
#
- # 'Chart::Column'
- # 'Chart::Bar'
- # 'Chart::Line'
- # 'Chart::Area'
- # 'Chart::Pie'
- # 'Chart::Scatter'
- # 'Chart::Stock'
+ # 'Chart::Column'
+ # 'Chart::Bar'
+ # 'Chart::Line'
+ # 'Chart::Area'
+ # 'Chart::Pie'
+ # 'Chart::Scatter'
+ # 'Chart::Stock'
#
- # * :name
+ # * :name
#
- # Set the name for the chart sheet. The name property is optional and
- # if it isn't supplied will default to Chart1 .. n. The name must be
- # a valid Excel worksheet name. See add_worksheet() for more details
- # on valid sheet names. The name property can be omitted for embedded
- # charts.
+ # Set the name for the chart sheet. The name property is optional and
+ # if it isn't supplied will default to Chart1 .. n. The name must be
+ # a valid Excel worksheet name. See add_worksheet() for more details
+ # on valid sheet names. The name property can be omitted for embedded
+ # charts.
#
- # chart = workbook.add_chart(
- # :type => 'Chart::Line',
- # :name => 'Results Chart'
- # )
+ # chart = workbook.add_chart(
+ # :type => 'Chart::Line',
+ # :name => 'Results Chart'
+ # )
#
- # * :embedded
+ # * :encoding
#
- # Specifies that the Chart object will be inserted in a worksheet via
- # the insert_chart() Worksheet method. It is an error to try insert a
- # Chart that doesn't have this flag set.
+ # if :name is UTF-16BE format, pass 1 as :encoding.
#
- # chart = workbook.add_chart(:type => 'Chart::Line', :embedded => 1)
+ # * :embedded
#
- # # Configure the chart.
- # ...
+ # Specifies that the Chart object will be inserted in a worksheet via
+ # the insert_chart() Worksheet method. It is an error to try insert a
+ # Chart that doesn't have this flag set.
#
- # # Insert the chart into the a worksheet.
- # worksheet.insert_chart('E2', chart)
+ # chart = workbook.add_chart(:type => 'Chart::Line', :embedded => 1)
#
+ # # Configure the chart.
+ # ...
+ #
+ # # Insert the chart into the a worksheet.
+ # worksheet.insert_chart('E2', chart)
+ #
# See WriteExcel::Chart for details on how to configure the
- # chart object once it is created. See also the chart_*.pl programs in the
+ # chart object once it is created. See also the chart_*.rb programs in the
# examples directory of the distro.
#
- def add_chart(params)
+ def add_chart(properties)
name = ''
encoding = 0
index = @worksheets.size
# Type must be specified so we can create the required chart instance.
- type = params[:type]
- print "Must define chart type in add_chart()" if type.nil?
+ type = properties[:type]
+ raise "Must define chart type in add_chart()" if type.nil?
# Ensure that the chart defaults to non embedded.
- embedded = params[:embedded]
+ embedded = properties[:embedded]
# Check the worksheet name for non-embedded charts.
unless embedded
- name, encoding = check_sheetname(params[:name], params[:name_encoding], 1)
+ name, encoding =
+ check_sheetname(properties[:name], properties[:encoding], true)
end
init_data = [
name,
index,
@@ -410,25 +397,21 @@
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
- @sheetnames[index] = name # Store EXTERNSHEET names
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
- ###############################################################################
#
- # add_chart_ext($filename, $name)
- #
# Add an externally created chart.
#
# This method is use to include externally generated charts in a WriteExcel
# file.
#
@@ -452,54 +435,74 @@
@sinfo
]
chart = Writeexcel::Chart.factory(self, type, init_data)
@worksheets[index] = chart # Store ref for iterator
- @sheetnames[index] = name # Store EXTERNSHEET names
chart
end
###############################################################################
#
- # _check_sheetname($name, $encoding)
+ # check_sheetname(name, encoding)
#
# Check for valid worksheet names. We check the length, if it contains any
# invalid characters and if the name is unique in the workbook.
#
- def check_sheetname(name, encoding = 0, chart = 0) #:nodoc:
+ def check_sheetname(name, encoding = 0, chart = nil) #:nodoc:
encoding ||= 0
- limit = encoding != 0 ? 62 : 31
- invalid_char = %r![\[\]:*?/\\]!
# Increment the Sheet/Chart number used for default sheet names below.
- if chart != 0
+ if chart
@chart_count += 1
else
@sheet_count += 1
end
# Supply default Sheet/Chart name if none has been defined.
if name.nil? || name == ""
encoding = 0
- if chart != 0
+ if chart
name = @chart_name + @chart_count.to_s
else
name = @sheet_name + @sheet_count.to_s
end
end
ruby_19 { name = convert_to_ascii_if_ascii(name) }
+ check_sheetname_length(name, encoding)
+ check_sheetname_even(name) if encoding == 1
+ check_sheetname_valid_chars(name, encoding)
+ # Handle utf8 strings
+ if is_utf8?(name)
+ name = utf8_to_16be(name)
+ encoding = 1
+ end
+
+ check_sheetname_uniq(name, encoding)
+ [name, encoding]
+ end
+ private :check_sheetname
+
+ def check_sheetname_length(name, encoding) #:nodoc:
# Check that sheetname is <= 31 (1 or 2 byte chars). Excel limit.
+ limit = encoding != 0 ? 62 : 31
raise "Sheetname $name must be <= 31 chars" if name.bytesize > limit
+ end
+ private :check_sheetname_length
+ def check_sheetname_even(name) #:nodoc:
# Check that Unicode sheetname has an even number of bytes
- if encoding == 1 && (name.bytesize % 2 != 0)
+ if (name.bytesize % 2 != 0)
raise "Odd number of bytes in Unicode worksheet name: #{name}"
end
+ end
+ private :check_sheetname_even
+ def check_sheetname_valid_chars(name, encoding) #:nodoc:
# Check that sheetname doesn't contain any invalid characters
+ invalid_char = %r![\[\]:*?/\\]!
if encoding != 1 && name =~ invalid_char
# Check ASCII names
raise "Invalid character []:*?/\\ in worksheet name: #{name}"
else
# Extract any 8bit clean chars from the UTF16 name and validate them.
@@ -510,36 +513,25 @@
raise 'Invalid character []:*?/\\ in worksheet name: ' + name
end
str = $~.post_match
end
end
+ end
+ private :check_sheetname_valid_chars
- # Handle utf8 strings
- ruby_18 do
- if name =~ NonAscii
- name = utf8_to_16be(name)
- encoding = 1
- end
- end
- ruby_19 do
- if name.encoding == Encoding::UTF_8
- name = utf8_to_16be(name)
- encoding = 1
- end
- 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
- # since the names 'Sheet1' and 'sheet1' are equivalent. The tests also have
- # to take the encoding into account.
- #
+ # 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
+ # since the names 'Sheet1' and 'sheet1' are equivalent. The tests also have
+ # to take the encoding into account.
+ #
+ def check_sheetname_uniq(name, encoding) #:nodoc:
@worksheets.each do |worksheet|
name_a = name
encd_a = encoding
name_b = worksheet.name
encd_b = worksheet.encoding
- error = 0;
+ error = false
if encd_a == 0 and encd_b == 0
error = (name_a.downcase == name_b.downcase)
elsif encd_a == 0 and encd_b == 1
name_a = ascii_to_16be(name_a)
@@ -567,13 +559,12 @@
end
if error
raise "Worksheet name '#{name}', with case ignored, is already in use"
end
end
- [name, encoding]
end
- private :check_sheetname
+ private :check_sheetname_uniq
#
# The add_format method can be used to create new Format objects which are
# used to apply formatting to a cell. You can either define the properties
# at creation time via a hash of property values or later via method calls.
@@ -582,15 +573,15 @@
# 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(*args)
- formats = {}
- args.each { |arg| formats = formats.merge(arg) }
- format = Writeexcel::Format.new(@xf_index, @default_formats.merge(formats))
+ 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.
@@ -817,11 +808,10 @@
[0x99, 0x33, 0x00, 0x00], # 60
[0x99, 0x33, 0x66, 0x00], # 61
[0x33, 0x33, 0x99, 0x00], # 62
[0x33, 0x33, 0x33, 0x00] # 63
]
- 0
end
private :set_palette_xl97
#
# Change the default temp directory
@@ -946,20 +936,20 @@
tokens.collect! { |t| t.gsub(/_range3d/, '_range3dR') }
# Parse the tokens into a formula string.
formula = parser.parse_tokens(tokens)
- @defined_names.push(
+ defined_names.push(
{
:name => name,
:encoding => encoding,
:sheet_index => sheet_index,
:formula => formula
}
)
- index = @defined_names.size
+ index = defined_names.size
parser.set_ext_name(name, index)
end
#
@@ -1018,111 +1008,115 @@
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.respond_to?(:to_str)
end
- # List of valid input parameters.
- properties = {
- :codepage => [0x0001, 'VT_I2' ],
- :title => [0x0002, 'VT_LPSTR' ],
- :subject => [0x0003, 'VT_LPSTR' ],
- :author => [0x0004, 'VT_LPSTR' ],
- :keywords => [0x0005, 'VT_LPSTR' ],
- :comments => [0x0006, 'VT_LPSTR' ],
- :last_author => [0x0008, 'VT_LPSTR' ],
- :created => [0x000C, 'VT_FILETIME'],
- :category => [0x0002, 'VT_LPSTR' ],
- :manager => [0x000E, 'VT_LPSTR' ],
- :company => [0x000F, 'VT_LPSTR' ],
- :utf8 => 1
- }
# Check for valid input parameters.
- params.each_key do |k|
- unless properties.has_key?(k)
- raise "Unknown parameter '#{k}' in set_properties()";
- end
- end
+ check_valid_params_for_properties(params)
# Set the creation time unless specified by the user.
- unless params.has_key?(:created)
- params[:created] = @localtime
- end
+ params[:created] = @localtime unless params.has_key?(:created)
#
# Create the SummaryInformation property set.
#
# Get the codepage of the strings in the property set.
- strings = ["title", "subject", "author", "keywords", "comments", "last_author"]
- params[:codepage] = get_property_set_codepage(params, strings)
+ properties = [:title, :subject, :author, :keywords, :comments, :last_author]
+ params[:codepage] = get_property_set_codepage(params, properties)
# Create an array of property set values.
- property_sets = []
- strings.unshift("codepage")
- strings.push("created")
- strings.each do |string|
- property = string.to_sym
- property_sets.push(property_set(properties, property, params)) if params[property]
- end
+ properties.unshift(:codepage)
+ properties.push(:created)
# Pack the property sets.
- @summary = create_summary_property_set(property_sets)
+ @summary =
+ create_summary_property_set(property_sets(properties, params))
#
# Create the DocSummaryInformation property set.
#
# Get the codepage of the strings in the property set.
- strings = ["category", "manager", "company"]
- params[:codepage] = get_property_set_codepage(params, strings)
+ properties = [:category, :manager, :company]
+ params[:codepage] = get_property_set_codepage(params, properties)
# Create an array of property set values.
- property_sets = []
+ properties.unshift(:codepage)
- [: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)
+ @doc_summary =
+ create_doc_summary_property_set(property_sets(properties, params))
# Set a flag for when the files is written.
- @add_doc_properties = 1
+ add_doc_properties = true
end
- def property_set(properties, property, params)
- [ properties[property][0], properties[property][1], params[property] ]
+ def property_set(property, params) #:nodoc:
+ valid_properties[property][0..1] + [params[property]]
end
private :property_set
+ def property_sets(properties, params) #:nodoc:
+ properties.select { |property| params[property.to_sym] }.
+ collect do |property|
+ property_set(property.to_sym, params)
+ end
+ end
+ private :property_sets
+
+ # List of valid input parameters.
+ def valid_properties #:nodoc:
+ {
+ :codepage => [0x0001, 'VT_I2' ],
+ :title => [0x0002, 'VT_LPSTR' ],
+ :subject => [0x0003, 'VT_LPSTR' ],
+ :author => [0x0004, 'VT_LPSTR' ],
+ :keywords => [0x0005, 'VT_LPSTR' ],
+ :comments => [0x0006, 'VT_LPSTR' ],
+ :last_author => [0x0008, 'VT_LPSTR' ],
+ :created => [0x000C, 'VT_FILETIME'],
+ :category => [0x0002, 'VT_LPSTR' ],
+ :manager => [0x000E, 'VT_LPSTR' ],
+ :company => [0x000F, 'VT_LPSTR' ],
+ :utf8 => 1
+ }
+ end
+ private :valid_properties
+
+ def check_valid_params_for_properties(params) #:nodoc:
+ params.each_key do |k|
+ unless valid_properties.has_key?(k)
+ raise "Unknown parameter '#{k}' in set_properties()";
+ end
+ end
+ end
+ private :check_valid_params_for_properties
+
###############################################################################
#
- # _get_property_set_codepage()
+ # get_property_set_codepage()
#
# Get the character codepage used by the strings in a property set. If one of
# the strings used is utf8 then the codepage is marked as utf8. Otherwise
# Latin 1 is used (although in our case this is limited to 7bit ASCII).
#
- def get_property_set_codepage(params, strings) #:nodoc:
+ def get_property_set_codepage(params, properties) #:nodoc:
# Allow for manually marked utf8 strings.
- unless params[:utf8].nil?
- return 0xFDE9
- else
- strings.each do |string|
- next unless params.has_key?(string.to_sym)
- ruby_18 { return 0xFDE9 if params[string.to_sym] =~ NonAscii }
- ruby_19 { return 0xFDE9 if params[string.to_sym].encoding == Encoding::UTF_8 }
- end
- return 0x04E4; # Default codepage, Latin 1.
+ return 0xFDE9 unless params[:utf8].nil?
+ properties.each do |property|
+ next unless params.has_key?(property.to_sym)
+ return 0xFDE9 if is_utf8?(params[property.to_sym])
end
+ return 0x04E4; # Default codepage, Latin 1.
end
private :get_property_set_codepage
###############################################################################
#
- # _store_workbook()
+ # store_workbook()
#
# Assemble worksheets into a workbook and send the BIFF data to an OLE
# storage.
#
def store_workbook #:nodoc:
@@ -1166,11 +1160,11 @@
sheet.encoding
)
end
# NOTE: If any records are added between here and EOF the
- # _calc_sheet_offsets() should be updated to include the new length.
+ # calc_sheet_offsets() should be updated to include the new length.
store_country
if @ext_refs.keys.size != 0
store_supbook
store_externsheet
store_names
@@ -1211,20 +1205,20 @@
@localtime = val
end
###############################################################################
#
- # _store_ole_filie()
+ # 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_filie #:nodoc:
maxsize = 7_087_104
# maxsize = 1
- if @add_doc_properties == 0 && @biffsize <= maxsize
+ if !add_doc_properties && @biffsize <= maxsize
# Write the OLE file using OLEwriter if data <= 7MB
ole = OLEWriter.new(@fh_out)
# Write the BIFF data without the OLE container for testing.
ole.biff_only = @biff_only
@@ -1264,11 +1258,11 @@
streams = []
streams << workbook
# Create the properties streams, if any.
- if @add_doc_properties != 0
+ if add_doc_properties
stream = "\5SummaryInformation".unpack('C*').pack('v*')
summary = OLEStorageLitePPSFile.new(stream, @summary)
streams << summary
stream = "\5DocumentSummaryInformation".unpack('C*').pack('v*')
summary = OLEStorageLitePPSFile.new(stream, @doc_summary)
@@ -1291,18 +1285,15 @@
end
private :store_ole_filie
###############################################################################
#
- # _calc_sheet_offsets()
+ # calc_sheet_offsets()
#
# Calculate Worksheet BOF offsets records for use in the BOUNDSHEET records.
#
def calc_sheet_offsets #:nodoc:
- _bof = 12
- _eof = 4
-
offset = @datasize
# Add the length of the COUNTRY record
offset += 8
@@ -1314,20 +1305,20 @@
# 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
- # for any CONTINUE headers. See _add_mso_drawing_group_continue().
+ # for any CONTINUE headers. See add_mso_drawing_group_continue().
mso_size = @mso_size
mso_size += 4 * Integer((mso_size -1) / Float(@limit))
offset += mso_size
@worksheets.each do |sheet|
- offset += _bof + sheet.name.bytesize
+ offset += BOF + sheet.name.bytesize
end
- offset += _eof
+ offset += EOF
@worksheets.each do |sheet|
sheet.offset = offset
sheet.close
offset += sheet.datasize
end
@@ -1336,11 +1327,11 @@
end
private :calc_sheet_offsets
###############################################################################
#
- # _calc_mso_sizes()
+ # calc_mso_sizes()
#
# Calculate the MSODRAWINGGROUP sizes and the indexes of the Worksheet
# MSODRAWING records.
#
# In the following SPID is shape id, according to Escher nomenclature.
@@ -1417,11 +1408,11 @@
end
private :calc_mso_sizes
###############################################################################
#
- # _process_images()
+ # process_images()
#
# We need to process each image in each worksheet and extract information.
# Some of this information is stored and used in the Workbook and some is
# passed back into each Worksheet. The overall size for the image related
# BIFF structures in the Workbook is calculated here.
@@ -1552,14 +1543,14 @@
end
private :process_images
###############################################################################
#
- # _image_checksum()
+ # image_checksum()
#
# Generate a checksum for the image using whichever module is available..The
- # available modules are checked in _get_checksum_method(). Excel uses an MD4
+ # available modules are checked in get_checksum_method(). Excel uses an MD4
# checksum but any other will do. In the event of no checksum module being
# available we simulate a checksum using the image index.
#
def image_checksum(data, index1, index2 = 0) #:nodoc:
if @checksum_method == 1
@@ -1578,11 +1569,11 @@
end
private :image_checksum
###############################################################################
#
- # _process_png()
+ # process_png()
#
# Extract width and height information from a PNG file.
#
def process_png(data) #:nodoc:
type = 6 # Excel Blip type (MSOBLIPTYPE).
@@ -1593,11 +1584,11 @@
end
private :process_png
###############################################################################
#
- # _process_bmp()
+ # process_bmp()
#
# Extract width and height information from a BMP file.
#
# Most of these checks came from the old Worksheet::_process_bitmap() method.
#
@@ -1642,11 +1633,11 @@
end
private :process_bmp
###############################################################################
#
- # _process_jpg()
+ # process_jpg()
#
# Extract width and height information from a JPEG file.
#
def process_jpg(data, filename) # :nodoc:
type = 5 # Excel Blip type (MSOBLIPTYPE).
@@ -1681,16 +1672,16 @@
[type, width, height]
end
###############################################################################
#
- # _store_all_fonts()
+ # store_all_fonts()
#
# 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)
@@ -1749,11 +1740,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
@@ -1772,22 +1763,22 @@
end
private :store_all_fonts
###############################################################################
#
- # _store_all_num_formats()
+ # store_all_num_formats()
#
# Store user defined numerical formats i.e. FORMAT records
#
def store_all_num_formats #:nodoc:
num_formats = {}
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
@@ -1811,25 +1802,25 @@
end
private :store_all_num_formats
###############################################################################
#
- # _store_all_xfs()
+ # store_all_xfs()
#
# 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
private :store_all_xfs
###############################################################################
#
- # _store_all_styles()
+ # store_all_styles()
#
# Write all STYLE records.
#
def store_all_styles #:nodoc:
# Excel adds the built-in styles in alphabetical order.
@@ -1856,72 +1847,82 @@
end
private :store_all_styles
###############################################################################
#
- # _store_names()
+ # store_names()
#
# Write the NAME record to define the print area and the repeat rows and cols.
#
def store_names # :nodoc:
# Create the user defined names.
- @defined_names.each do |defined_name|
+ defined_names.each do |defined_name|
store_name(
defined_name[:name],
defined_name[:encoding],
defined_name[:sheet_index],
defined_name[:formula]
)
end
# Sort the worksheets into alphabetical order by name. This is a
# requirement for some non-English language Excel patch levels.
- worksheets = @worksheets.sort_by{ |x| x.name }
+ sorted_worksheets = @worksheets.sort_by{ |x| x.name }
# Create the autofilter NAME records
- worksheets.each do |worksheet|
+ create_autofilter_name_records(sorted_worksheets)
+
+ # Create the print area NAME records
+ create_print_area_name_records(sorted_worksheets)
+
+ # Create the print title NAME records
+ create_print_title_name_records(sorted_worksheets)
+ end
+
+ def create_autofilter_name_records(sorted_worksheets) #:nodoc:
+ sorted_worksheets.each do |worksheet|
index = worksheet.index
- key = "#{index}:#{index}"
- ref = @ext_refs[key]
# Write a Name record if Autofilter has been defined
if worksheet.filter_count != 0
store_name_short(
worksheet.index,
0x0D, # NAME type = Filter Database
- ref,
+ @ext_refs["#{index}:#{index}"],
worksheet.filter_area[0],
worksheet.filter_area[1],
worksheet.filter_area[2],
worksheet.filter_area[3],
1 # Hidden
)
end
end
+ end
+ private :create_autofilter_name_records
- # Create the print area NAME records
- worksheets.each do |worksheet|
+ def create_print_area_name_records(sorted_worksheets) #:nodoc:
+ sorted_worksheets.each do |worksheet|
index = worksheet.index
- key = "#{index}:#{index}"
- ref = @ext_refs[key]
# 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
- ref,
+ @ext_refs["#{index}:#{index}"],
worksheet.print_rowmin,
worksheet.print_rowmax,
worksheet.print_colmin,
worksheet.print_colmax
)
end
end
+ end
+ private :create_print_area_name_records
- # Create the print title NAME records
- worksheets.each do |worksheet|
+ 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
@@ -1968,21 +1969,22 @@
else
# Nothing left to do
end
end
end
+ private :create_print_title_name_records
###############################################################################
###############################################################################
#
# BIFF RECORDS
#
###############################################################################
#
- # _store_window1()
+ # store_window1()
#
# Write Excel BIFF WINDOW1 record.
#
def store_window1 #:nodoc:
record = 0x003D # Record identifier
@@ -2012,11 +2014,11 @@
end
private :store_window1
###############################################################################
#
- # _store_boundsheet()
+ # store_boundsheet()
# my $sheetname = $_[0]; # Worksheet name
# my $offset = $_[1]; # Location of worksheet BOF
# my $type = $_[2]; # Worksheet type
# my $hidden = $_[3]; # Worksheet hidden flag
# my $encoding = $_[4]; # Sheet name encoding
@@ -2044,11 +2046,11 @@
end
private :store_boundsheet
###############################################################################
#
- # _store_style()
+ # store_style()
# type = $_[0] # Built-in style
# xf_index = $_[1] # Index to style XF
#
# Write Excel BIFF STYLE records.
#
@@ -2067,11 +2069,11 @@
end
private :store_style
###############################################################################
#
- # _store_num_format()
+ # store_num_format()
# my $format = $_[0]; # Custom format string
# my $ifmt = $_[1]; # Format index code
# my $encoding = $_[2]; # Char encoding for format string
#
# Writes Excel FORMAT record for non "built-in" numerical formats.
@@ -2084,22 +2086,14 @@
cch = format.bytesize
ruby_19 { format = convert_to_ascii_if_ascii(format) }
# Handle utf8 strings
- ruby_18 do
- if format =~ NonAscii
- format = utf8_to_16be(format)
- encoding = 1
- end
+ if is_utf8?(format)
+ format = utf8_to_16be(format)
+ encoding = 1
end
- ruby_19 do
- if format.encoding == Encoding::UTF_8
- format = utf8_to_16be(format)
- encoding = 1
- end
- end
# 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
@@ -2123,11 +2117,11 @@
end
private :store_num_format
###############################################################################
#
- # _store_1904()
+ # store_1904()
#
# Write Excel 1904 record to indicate the date system in use.
#
def store_1904 #:nodoc:
record = 0x0022 # Record identifier
@@ -2142,11 +2136,11 @@
end
private :store_1904
###############################################################################
#
- # _store_supbook()
+ # store_supbook()
#
# Write BIFF record SUPBOOK to indicate that the workbook contains external
# references, in our case, formula, print area and print title refs.
#
def store_supbook #:nodoc:
@@ -2163,11 +2157,11 @@
end
private :store_supbook
###############################################################################
#
- # _store_externsheet()
+ # store_externsheet()
#
# Writes the Excel BIFF EXTERNSHEET record. These references are used by
# formulas. TODO NAME record is required to define the print area and the
# repeat rows and columns.
#
@@ -2196,12 +2190,12 @@
#
# Store the NAME record used for storing the print area, repeat rows, repeat
# columns, autofilters and defined names.
#
- # TODO. This is a more generic version that will replace _store_name_short()
- # and _store_name_long().
+ # TODO. This is a more generic version that will replace store_name_short()
+ # and store_name_long().
#
def store_name(name, encoding, sheet_index, formula) # :nodoc:
ruby_19 { formula = convert_to_ascii_if_ascii(formula) }
record = 0x0018 # Record identifier
@@ -2246,11 +2240,11 @@
append(header, data)
end
###############################################################################
#
- # _store_name_short()
+ # store_name_short()
# index = shift # Sheet index
# type = shift
# ext_ref = shift # TODO
# rowmin = $_[0] # Start row
# rowmax = $_[1] # End row
@@ -2307,11 +2301,11 @@
end
private :store_name_short
###############################################################################
#
- # _store_name_long()
+ # store_name_long()
# my $index = shift; # Sheet index
# my $type = shift;
# my $ext_ref = shift; # TODO
# my $rowmin = $_[0]; # Start row
# my $rowmax = $_[1]; # End row
@@ -2319,11 +2313,11 @@
# my $colmax = $_[3]; # end column
#
#
# 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.
+ # 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. ;-)
#
def store_name_long(index, type, ext_ref, rowmin, rowmax, colmin, colmax) #:nodoc:
record = 0x0018 # Record identifier
length = 0x002a # Number of bytes to follow
@@ -2383,11 +2377,11 @@
end
private :store_name_long
###############################################################################
#
- # _store_palette()
+ # store_palette()
#
# Stores the PALETTE biff record.
#
def store_palette #:nodoc:
record = 0x0092 # Record identifier
@@ -2406,11 +2400,11 @@
end
private :store_palette
###############################################################################
#
- # _store_codepage()
+ # store_codepage()
#
# Stores the CODEPAGE biff record.
#
def store_codepage #:nodoc:
record = 0x0042 # Record identifier
@@ -2421,11 +2415,11 @@
end
private :store_codepage
###############################################################################
#
- # _store_country()
+ # store_country()
#
# Stores the COUNTRY biff record.
#
def store_country #:nodoc:
record = 0x008C # Record identifier
@@ -2437,11 +2431,11 @@
end
private :store_country
###############################################################################
#
- # _store_hideobj()
+ # store_hideobj()
#
# Stores the HIDEOBJ biff record.
#
def store_hideobj #:nodoc:
record = 0x008D # Record identifier
@@ -2450,38 +2444,38 @@
store_common(record, length, hide)
end
private :store_hideobj
- def store_common(record, length, *data)
+ def store_common(record, length, *data) #:nodoc:
header = [record, length].pack("vv")
add_data = [*data].pack("v*")
append(header, add_data)
end
private :store_common
###############################################################################
#
- # _calculate_extern_sizes()
+ # 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
length = 0
index = 0
- unless @defined_names.empty?
+ unless defined_names.empty?
index = 0
key = "#{index}:#{index}"
add_ext_refs(ext_refs, key) unless ext_refs.has_key?(key)
end
- @defined_names.each do |defined_name|
+ defined_names.each do |defined_name|
length += 19 + defined_name[:name].bytesize + defined_name[:formula].bytesize
end
@worksheets.each do |worksheet|
@@ -2533,30 +2527,30 @@
length += 6 * (1 + ext_ref_count)
length
end
- def add_ext_refs(ext_refs, key)
+ def add_ext_refs(ext_refs, key) #:nodoc:
ext_refs[key] = ext_refs.keys.size
end
private :add_ext_refs
###############################################################################
#
- # _calculate_shared_string_sizes()
+ # calculate_shared_string_sizes()
#
# Handling of the SST continue blocks is complicated by the need to include an
# additional continuation byte depending on whether the string is split between
# blocks or whether it starts at the beginning of the block. (There are also
# additional complications that will arise later when/if Rich Strings are
# supported). As such we cannot use the simple CONTINUE mechanism provided by
- # the _add_continue() method in BIFFwriter.pm. Thus we have to make two passes
+ # the add_continue() method in BIFFwriter.pm. Thus we have to make two passes
# through the strings data. The first is to calculate the required block sizes
- # and the second, in _store_shared_strings(), is to write the actual strings.
+ # and the second, in store_shared_strings(), is to write the actual strings.
# 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.
+ # 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])
@sinfo[:str_table].each_key do |key|
@@ -2568,16 +2562,16 @@
# 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
+ # 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
+ # -8 : Arbitrary number to keep within add_continue() limit
# = 8208
#
continue_limit = 8208
block_length = 0
written = 0
@@ -2603,11 +2597,11 @@
# 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)
+ 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
@@ -2665,11 +2659,11 @@
length
end
private :calculate_shared_string_sizes
- def _split_string_setup(encoding, split_string, continue_limit, written, continue)
+ def 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
@@ -2695,19 +2689,19 @@
split_string = 1
end
end
[header_length, space_remaining, align, split_string]
end
- private :_split_string_setup
+ private :split_string_setup
###############################################################################
#
- # _store_shared_strings()
+ # store_shared_strings()
#
# Write all of the workbooks strings into an indexed array.
#
- # See the comments in _calculate_shared_string_sizes() for more information.
+ # See the comments in calculate_shared_string_sizes() for more information.
#
# We also use this routine to record the offsets required by the EXTSST table.
# 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().
@@ -2724,11 +2718,11 @@
block_length = 0
written = 0
continue = 0
# The SST and CONTINUE block sizes have been pre-calculated by
- # _calculate_shared_string_sizes()
+ # calculate_shared_string_sizes()
block_sizes = @str_block_sizes
# The SST record is required even if it contains no strings. Thus we will
# always have a length
#
@@ -2783,11 +2777,11 @@
# 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)
+ 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
tmp = string[0, space_remaining]
@@ -2855,11 +2849,11 @@
end
private :store_shared_strings
###############################################################################
#
- # _calculate_extsst_size
+ # calculate_extsst_size
#
# The number of buckets used in the EXTSST is between 0 and 128. The number of
# 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
@@ -2882,13 +2876,13 @@
6 + 8 * buckets
end
###############################################################################
#
- # _store_extsst
+ # store_extsst
#
- # Write EXTSST table using the offsets calculated in _store_shared_strings().
+ # Write EXTSST table using the offsets calculated in store_shared_strings().
#
def store_extsst #:nodoc:
offsets = @extsst_offsets
bucket_size = @extsst_bucket_size
@@ -2910,11 +2904,11 @@
# Methods related to comments and MSO objects.
#
###############################################################################
#
- # _add_mso_drawing_group()
+ # add_mso_drawing_group()
#
# Write the MSODRAWINGGROUP record that keeps track of the Escher drawing
# objects in the file such as images, comments and filters.
#
def add_mso_drawing_group #:nodoc:
@@ -2941,11 +2935,11 @@
end
private :add_mso_drawing_group
###############################################################################
#
- # _add_mso_drawing_group_continue()
+ # add_mso_drawing_group_continue()
#
# See first the WriteExcel::BIFFwriter::_add_continue() method.
#
# Add specialised CONTINUE headers to large MSODRAWINGGROUP data block.
# We use the Excel 97 max block size of 8228 - 4 bytes for the header = 8224.
@@ -2960,11 +2954,11 @@
limit = 8228 -4
mso_group = 0x00EB # Record identifier
continue = 0x003C # Record identifier
block_count = 1
- # Ignore the base class _add_continue() method.
+ # Ignore the base class add_continue() method.
@ignore_continue = 1
# Case 1 above. Just return the data as it is.
if data.bytesize <= limit
append(data)
@@ -2993,25 +2987,25 @@
# 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.
+ # Turn the base class add_continue() method back on.
@ignore_continue = 0
end
private :add_mso_drawing_group_continue
- def devide_string(string, nth)
+ def devide_string(string, nth) #:nodoc:
first_string = string[0, nth]
latter_string = string[nth, string.size - nth]
[first_string, latter_string]
end
private :devide_string
###############################################################################
#
- # _store_mso_dgg_container()
+ # store_mso_dgg_container()
#
# Write the Escher DggContainer record that is part of MSODRAWINGGROUP.
#
def store_mso_dgg_container #:nodoc:
type = 0xF000
@@ -3024,11 +3018,11 @@
end
private :store_mso_dgg_container
###############################################################################
#
- # _store_mso_dgg()
+ # store_mso_dgg()
# my $max_spid = $_[0];
# my $num_clusters = $_[1];
# my $shapes_saved = $_[2];
# my $drawings_saved = $_[3];
# my $clusters = $_[4];
@@ -3056,11 +3050,11 @@
end
private :store_mso_dgg
###############################################################################
#
- # _store_mso_bstore_container()
+ # store_mso_bstore_container()
#
# Write the Escher BstoreContainer record that is part of MSODRAWINGGROUP.
#
def store_mso_bstore_container #:nodoc:
return '' if @images_size == 0
@@ -3075,11 +3069,11 @@
end
private :store_mso_bstore_container
###############################################################################
#
- # _store_mso_images()
+ # store_mso_images()
# ref_count = $_[0]
# image_type = $_[1]
# image = $_[2]
# size = $_[3]
# checksum1 = $_[4]
@@ -3107,11 +3101,11 @@
end
private :store_mso_images
###############################################################################
#
- # _store_mso_blip_store_entry()
+ # store_mso_blip_store_entry()
# ref_count = $_[0]
# image_type = $_[1]
# size = $_[2]
# checksum1 = $_[3]
#
@@ -3138,11 +3132,11 @@
end
private :store_mso_blip_store_entry
###############################################################################
#
- # _store_mso_blip()
+ # store_mso_blip()
# image_type = $_[0]
# image_data = $_[1]
# size = $_[2]
# checksum1 = $_[3]
# checksum2 = $_[4]
@@ -3170,11 +3164,11 @@
end
private :store_mso_blip
###############################################################################
#
- # _store_mso_opt()
+ # store_mso_opt()
#
# Write the Escher Opt record that is part of MSODRAWINGGROUP.
#
def store_mso_opt #:nodoc:
type = 0xF00B
@@ -3189,11 +3183,11 @@
end
private :store_mso_opt
###############################################################################
#
- # _store_mso_split_menu_colors()
+ # store_mso_split_menu_colors()
#
# Write the Escher SplitMenuColors record that is part of MSODRAWINGGROUP.
#
def store_mso_split_menu_colors #:nodoc:
type = 0xF11E
@@ -3206,10 +3200,11 @@
add_mso_generic(type, version, instance, data, length)
end
private :store_mso_split_menu_colors
- def cleanup
+ def cleanup #:nodoc:
super
sheets.each { |sheet| sheet.cleanup }
end
+ private :cleanup
end