Class: BIFFWriter

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

Overview

:nodoc:

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.



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

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.



21
22
23
# File 'lib/writeexcel/biffwriter.rb', line 21

def byte_order
  @byte_order
end

#dataObject (readonly)

Returns the value of attribute data.



21
22
23
# File 'lib/writeexcel/biffwriter.rb', line 21

def data
  @data
end

#datasizeObject (readonly)

Returns the value of attribute datasize.



21
22
23
# File 'lib/writeexcel/biffwriter.rb', line 21

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.



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/writeexcel/biffwriter.rb', line 203

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.



253
254
255
256
257
258
259
260
261
262
# File 'lib/writeexcel/biffwriter.rb', line 253

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



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

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 "append\n" if defined?($debug)
  print d.unpack('C*').map! {|c| sprintf("%02X", c) }.join(' ') + "\n\n" if defined?($debug)
  return d
end

#clear_data_for_testObject

:nodoc:



270
271
272
# File 'lib/writeexcel/biffwriter.rb', line 270

def clear_data_for_test # :nodoc:

  @data = ''
end

#get_dataObject

get_data().

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



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

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

#not_using_tmpfileObject



264
265
266
267
268
# File 'lib/writeexcel/biffwriter.rb', line 264

def not_using_tmpfile
  @filehandle.close(true) if @filehandle
  @filehandle = nil
  @using_tmpfile = nil
end

#prepend(*args) ⇒ Object

_prepend($data)

General storage function



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

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

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

  print "prepend\n" if defined?($debug)
  print d.unpack('C*').map! {|c| sprintf("%02X", c) }.join(' ') + "\n\n" if defined?($debug)
  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().



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

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 $type = 0x0020, Chart

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



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/writeexcel/biffwriter.rb', line 154

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")

  print "store_bof in #{__FILE__}\n" if defined?($debug)
  prepend(header, data)
end

#store_eofObject

_store_eof()

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



180
181
182
183
184
185
186
187
# File 'lib/writeexcel/biffwriter.rb', line 180

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

  print "store_eof in #{__FILE__}\n" if defined?($debug)
  append(header)
end