| package Spreadsheet::WriteExcel::BIFFwriter; |
| |
| ############################################################################### |
| # |
| # BIFFwriter - An abstract base class for Excel workbooks and worksheets. |
| # |
| # |
| # Used in conjunction with Spreadsheet::WriteExcel |
| # |
| # Copyright 2000-2010, John McNamara, jmcnamara@cpan.org |
| # |
| # Documentation after __END__ |
| # |
| |
| use Exporter; |
| use strict; |
| |
| |
| |
| |
| |
| |
| |
| use vars qw($VERSION @ISA); |
| @ISA = qw(Exporter); |
| |
| $VERSION = '2.37'; |
| |
| ############################################################################### |
| # |
| # Class data. |
| # |
| my $byte_order = ''; |
| my $BIFF_version = 0x0600; |
| |
| |
| ############################################################################### |
| # |
| # new() |
| # |
| # Constructor |
| # |
| sub new { |
| |
| my $class = $_[0]; |
| |
| my $self = { |
| _byte_order => '', |
| _data => '', |
| _datasize => 0, |
| _limit => 8224, |
| _ignore_continue => 0, |
| }; |
| |
| bless $self, $class; |
| $self->_set_byte_order(); |
| return $self; |
| } |
| |
| |
| ############################################################################### |
| # |
| # _set_byte_order() |
| # |
| # Determine the byte order and store it as class data to avoid |
| # recalculating it for each call to new(). |
| # |
| sub _set_byte_order { |
| |
| my $self = shift; |
| |
| if ($byte_order eq ''){ |
| # Check if "pack" gives the required IEEE 64bit float |
| my $teststr = pack "d", 1.2345; |
| my @hexdata =(0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F); |
| my $number = pack "C8", @hexdata; |
| |
| if ($number eq $teststr) { |
| $byte_order = 0; # Little Endian |
| } |
| elsif ($number eq reverse($teststr)){ |
| $byte_order = 1; # Big Endian |
| } |
| else { |
| # Give up. I'll fix this in a later version. |
| croak ( "Required floating point format not supported " . |
| "on this platform. See the portability section " . |
| "of the documentation." |
| ); |
| } |
| } |
| $self->{_byte_order} = $byte_order; |
| } |
| |
| |
| ############################################################################### |
| # |
| # _prepend($data) |
| # |
| # General storage function |
| # |
| sub _prepend { |
| |
| my $self = shift; |
| my $data = join('', @_); |
| |
| $data = $self->_add_continue($data) if length($data) > $self->{_limit}; |
| |
| $self->{_data} = $data . $self->{_data}; |
| $self->{_datasize} += length($data); |
| |
| return $data; |
| } |
| |
| |
| ############################################################################### |
| # |
| # _append($data) |
| # |
| # General storage function |
| # |
| sub _append { |
| |
| my $self = shift; |
| my $data = join('', @_); |
| |
| $data = $self->_add_continue($data) if length($data) > $self->{_limit}; |
| |
| $self->{_data} = $self->{_data} . $data; |
| $self->{_datasize} += length($data); |
| |
| return $data; |
| } |
| |
| |
| ############################################################################### |
| # |
| # _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. |
| # |
| sub _store_bof { |
| |
| my $self = shift; |
| my $record = 0x0809; # Record identifier |
| my $length = 0x0010; # Number of bytes to follow |
| |
| my $version = $BIFF_version; |
| my $type = $_[0]; |
| |
| # 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. |
| my $build = 0x0DBB; |
| my $year = 0x07CC; |
| |
| my $bfh = 0x00000041; |
| my $sfo = 0x00000006; |
| |
| my $header = pack("vv", $record, $length); |
| my $data = pack("vvvvVV", $version, $type, $build, $year, $bfh, $sfo); |
| |
| $self->_prepend($header, $data); |
| } |
| |
| |
| ############################################################################### |
| # |
| # _store_eof() |
| # |
| # Writes Excel EOF record to indicate the end of a BIFF stream. |
| # |
| sub _store_eof { |
| |
| my $self = shift; |
| my $record = 0x000A; # Record identifier |
| my $length = 0x0000; # Number of bytes to follow |
| |
| my $header = pack("vv", $record, $length); |
| |
| $self->_append($header); |
| } |
| |
| |
| ############################################################################### |
| # |
| # _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. |
| # |
| sub _add_continue { |
| |
| my $self = shift; |
| my $data = $_[0]; |
| my $limit = $self->{_limit}; |
| my $record = 0x003C; # Record identifier |
| my $header; |
| my $tmp; |
| |
| # Skip this if another method handles the continue blocks. |
| return $data if $self->{_ignore_continue}; |
| |
| # The first 2080/8224 bytes remain intact. However, we have to change |
| # the length field of the record. |
| # |
| $tmp = substr($data, 0, $limit, ""); |
| substr($tmp, 2, 2, pack("v", $limit-4)); |
| |
| # Strip out chunks of 2080/8224 bytes +4 for the header. |
| while (length($data) > $limit) { |
| $header = pack("vv", $record, $limit); |
| $tmp .= $header; |
| $tmp .= substr($data, 0, $limit, ""); |
| } |
| |
| # Mop up the last of the data |
| $header = pack("vv", $record, length($data)); |
| $tmp .= $header; |
| $tmp .= $data; |
| |
| return $tmp ; |
| } |
| |
| |
| ############################################################################### |
| # |
| # _add_mso_generic() |
| # |
| # 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. |
| # |
| sub _add_mso_generic { |
| |
| my $self = shift; |
| my $type = $_[0]; |
| my $version = $_[1]; |
| my $instance = $_[2]; |
| my $data = $_[3]; |
| my $length = defined $_[4] ? $_[4] : length($data); |
| |
| # The header contains version and instance info packed into 2 bytes. |
| my $header = $version | ($instance << 4); |
| |
| my $record = pack "vvV", $header, $type, $length; |
| $record .= $data; |
| |
| return $record; |
| } |
| |
| |
| ############################################################################### |
| # |
| # For debugging |
| # |
| sub _hexout { |
| |
| my $self = shift; |
| |
| print +(caller(1))[3], "\n"; |
| |
| my $data = join '', @_; |
| |
| my @bytes = unpack("H*", $data) =~ /../g; |
| |
| while (@bytes > 16) { |
| print join " ", splice @bytes, 0, 16; |
| print "\n"; |
| } |
| print join " ", @bytes, "\n\n"; |
| } |
| |
| |
| |
| 1; |
| |
| |
| __END__ |
| |
| |
| =head1 NAME |
| |
| BIFFwriter - An abstract base class for Excel workbooks and worksheets. |
| |
| =head1 SYNOPSIS |
| |
| See the documentation for Spreadsheet::WriteExcel |
| |
| =head1 DESCRIPTION |
| |
| This module is used in conjunction with Spreadsheet::WriteExcel. |
| |
| =head1 AUTHOR |
| |
| John McNamara jmcnamara@cpan.org |
| |
| =head1 COPYRIGHT |
| |
| © MM-MMX, John McNamara. |
| |
| All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself. |