Class: BIFFWriter

Inherits:
Object
  • Object
show all
Defined in:
lib/WriteExcel/biffwriter.rb

Overview

require ‘tempfile’

Direct Known Subclasses

Worksheet

Constant Summary collapse

BIFF_Version =
0x0600
BigEndian =
[1].pack("I") == [1].pack("N")

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeBIFFWriter

The args here aren’t used by BIFFWriter, but they are needed by its subclasses. I don’t feel like creating multiple constructors.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/WriteExcel/biffwriter.rb', line 27

def initialize
  set_byte_order
  @data            = ''
  @datasize        = 0
  @limit           = 8224
  @ignore_continue = 0
  
  # Open a tmp file to store the majority of the Worksheet data. If this fails,
  # for example due to write permissions, store the data in memory. This can be
  # slow for large files.
  @filehandle = Tempfile.new('spreadsheetWriteExcel')
  @filehandle.binmode

  # failed. store temporary data in memory.
  @using_tmpfile = @filehandle ? true : false
  
end

Instance Attribute Details

#byte_orderObject (readonly)

Returns the value of attribute byte_order.



20
21
22
# File 'lib/WriteExcel/biffwriter.rb', line 20

def byte_order
  @byte_order
end

#dataObject (readonly)

Returns the value of attribute data.



20
21
22
# File 'lib/WriteExcel/biffwriter.rb', line 20

def data
  @data
end

#datasizeObject (readonly)

Returns the value of attribute datasize.



20
21
22
# File 'lib/WriteExcel/biffwriter.rb', line 20

def datasize
  @datasize
end

Instance Method Details

#add_continue(data) ⇒ Object

_add_continue()

Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In Excel 97 the limit is 8228 bytes. Records that are longer than these limits must be split up into CONTINUE blocks.

This function take a long BIFF record and inserts CONTINUE records as necessary.

Some records have their own specialised Continue blocks so there is also an option to bypass this function.



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/WriteExcel/biffwriter.rb', line 198

def add_continue(data)
  record      = 0x003C # Record identifier

  # Skip this if another method handles the continue blocks.
  return data if @ignore_continue != 0

  # The first 2080/8224 bytes remain intact. However, we have to change
  # the length field of the record.
  #

  # in perl
  #  $tmp = substr($data, 0, $limit, "");
  if data.length > @limit
    tmp = data[0, @limit]
    data[0, @limit] = ''
  else
    tmp = data.dup
    data = ''
  end

  tmp[2, 2] = [@limit-4].pack('v')

  # Strip out chunks of 2080/8224 bytes +4 for the header.
  while (data.length > @limit)
    header  = [record, @limit].pack("vv")
    tmp     = tmp + header + data[0, @limit]
    data[0, @limit] = ''
  end

  # Mop up the last of the data
  header  = [record, data.length].pack("vv")
  tmp     = tmp + header + data

  return tmp
end

#add_mso_generic(type, version, instance, data, length = nil) ⇒ Object

_add_mso_generic()

my $type        = $_[0];
my $version     = $_[1];
my $instance    = $_[2];
my $data        = $_[3];

Create a mso structure that is part of an Escher drawing object. These are are used for images, comments and filters. This generic method is used by other methods to create specific mso records.

Returns the packed record.



248
249
250
251
252
253
254
255
256
257
# File 'lib/WriteExcel/biffwriter.rb', line 248

def add_mso_generic(type, version, instance, data, length = nil)
  length  = length.nil? ? data.length : length

  # The header contains version and instance info packed into 2 bytes.
  header  = version | (instance << 4)

  record  = [header, type, length].pack('vvV') + data

  return record
end

#append(*args) ⇒ Object

_append($data)

General storage function



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/WriteExcel/biffwriter.rb', line 95

def append(*args)
  d = args.join
  # Add CONTINUE records if necessary
  d = add_continue(d) if d.length > @limit
  if @using_tmpfile
    @filehandle.write d
    @datasize += d.length
  else
    @datasize += d.length
    @data      = @data + d
  end
#print "apend\n"
#print d.unpack('C*').map! {|c| sprintf("%02X", c) }.join(' ') + "\n\n"
  return d
end

#get_dataObject

get_data().

Retrieves data from memory in one chunk, or from disk in $buffer sized chunks.



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/WriteExcel/biffwriter.rb', line 118

def get_data
  buflen = 4096

  # Return data stored in memory
  unless @data.nil?
    tmp   = @data
    @data = nil
    if @using_tmpfile
      @filehandle.open
      @filehandle.binmode
    end
    return tmp
  end

  # Return data stored on disk
  if @using_tmpfile
    return @filehandle.read(buflen)
  end

  # No data to return
  return nil
end

#prepend(*args) ⇒ Object

_prepend($data)

General storage function



77
78
79
80
81
82
83
84
85
86
87
# File 'lib/WriteExcel/biffwriter.rb', line 77

def prepend(*args)
  d = args.join
  d = add_continue(d) if d.length > @limit

  @datasize += d.length
  @data      = d + @data

#print "prepend\n"
#print d.unpack('C*').map! {|c| sprintf("%02X", c) }.join(' ') + "\n\n"
  return d
end

#set_byte_orderObject

_set_byte_order()

Determine the byte order and store it as class data to avoid recalculating it for each call to new().



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/WriteExcel/biffwriter.rb', line 52

def set_byte_order
  # Check if "pack" gives the required IEEE 64bit float
  teststr = [1.2345].pack("d")
  hexdata = [0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F]
  number  = hexdata.pack("C8")

  if number == teststr
    @byte_order = 0    # Little Endian
  elsif number == teststr.reverse
    @byte_order = 1    # Big Endian
  else
    # Give up. I'll fix this in a later version.
    raise( "Required floating point format not supported "  +
    "on this platform. See the portability section " +
    "of the documentation."
    )
  end
end

#store_bof(type = 0x0005) ⇒ Object

_store_bof($type)

$type = 0x0005, Workbook $type = 0x0010, Worksheet

Writes Excel BOF record to indicate the beginning of a stream or sub-stream in the BIFF file.



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/WriteExcel/biffwriter.rb', line 151

def store_bof(type = 0x0005)
  record  = 0x0809      # Record identifier
  length  = 0x0010      # Number of bytes to follow

  # According to the SDK $build and $year should be set to zero.
  # However, this throws a warning in Excel 5. So, use these
  # magic numbers.
  build   = 0x0DBB
  year    = 0x07CC

  bfh     = 0x00000041
  sfo     = 0x00000006

  header  = [record,length].pack("vv")
  data    = [BIFF_Version,type,build,year,bfh,sfo].pack("vvvvVV")

  prepend(header, data)
end

#store_eofObject

_store_eof()

Writes Excel EOF record to indicate the end of a BIFF stream.



176
177
178
179
180
181
182
# File 'lib/WriteExcel/biffwriter.rb', line 176

def store_eof
  record = 0x000A
  length = 0x0000
  header = [record,length].pack("vv")

  append(header)
end