1package Spreadsheet::WriteExcel::BIFFwriter; 2 3############################################################################### 4# 5# BIFFwriter - An abstract base class for Excel workbooks and worksheets. 6# 7# 8# Used in conjunction with Spreadsheet::WriteExcel 9# 10# Copyright 2000-2010, John McNamara, jmcnamara@cpan.org 11# 12# Documentation after __END__ 13# 14 15use Exporter; 16use strict; 17 18 19 20 21 22 23 24use vars qw($VERSION @ISA); 25@ISA = qw(Exporter); 26 27$VERSION = '2.40'; 28 29############################################################################### 30# 31# Class data. 32# 33my $byte_order = ''; 34my $BIFF_version = 0x0600; 35 36 37############################################################################### 38# 39# new() 40# 41# Constructor 42# 43sub new { 44 45 my $class = $_[0]; 46 47 my $self = { 48 _byte_order => '', 49 _data => '', 50 _datasize => 0, 51 _limit => 8224, 52 _ignore_continue => 0, 53 }; 54 55 bless $self, $class; 56 $self->_set_byte_order(); 57 return $self; 58} 59 60 61############################################################################### 62# 63# _set_byte_order() 64# 65# Determine the byte order and store it as class data to avoid 66# recalculating it for each call to new(). 67# 68sub _set_byte_order { 69 70 my $self = shift; 71 72 if ($byte_order eq ''){ 73 # Check if "pack" gives the required IEEE 64bit float 74 my $teststr = pack "d", 1.2345; 75 my @hexdata =(0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F); 76 my $number = pack "C8", @hexdata; 77 78 if ($number eq $teststr) { 79 $byte_order = 0; # Little Endian 80 } 81 elsif ($number eq reverse($teststr)){ 82 $byte_order = 1; # Big Endian 83 } 84 else { 85 # Give up. I'll fix this in a later version. 86 croak ( "Required floating point format not supported " . 87 "on this platform. See the portability section " . 88 "of the documentation." 89 ); 90 } 91 } 92 $self->{_byte_order} = $byte_order; 93} 94 95 96############################################################################### 97# 98# _prepend($data) 99# 100# General storage function 101# 102sub _prepend { 103 104 my $self = shift; 105 my $data = join('', @_); 106 107 $data = $self->_add_continue($data) if length($data) > $self->{_limit}; 108 109 $self->{_data} = $data . $self->{_data}; 110 $self->{_datasize} += length($data); 111 112 return $data; 113} 114 115 116############################################################################### 117# 118# _append($data) 119# 120# General storage function 121# 122sub _append { 123 124 my $self = shift; 125 my $data = join('', @_); 126 127 $data = $self->_add_continue($data) if length($data) > $self->{_limit}; 128 129 $self->{_data} = $self->{_data} . $data; 130 $self->{_datasize} += length($data); 131 132 return $data; 133} 134 135 136############################################################################### 137# 138# _store_bof($type) 139# 140# $type = 0x0005, Workbook 141# $type = 0x0010, Worksheet 142# $type = 0x0020, Chart 143# 144# Writes Excel BOF record to indicate the beginning of a stream or 145# sub-stream in the BIFF file. 146# 147sub _store_bof { 148 149 my $self = shift; 150 my $record = 0x0809; # Record identifier 151 my $length = 0x0010; # Number of bytes to follow 152 153 my $version = $BIFF_version; 154 my $type = $_[0]; 155 156 # According to the SDK $build and $year should be set to zero. 157 # However, this throws a warning in Excel 5. So, use these 158 # magic numbers. 159 my $build = 0x0DBB; 160 my $year = 0x07CC; 161 162 my $bfh = 0x00000041; 163 my $sfo = 0x00000006; 164 165 my $header = pack("vv", $record, $length); 166 my $data = pack("vvvvVV", $version, $type, $build, $year, $bfh, $sfo); 167 168 $self->_prepend($header, $data); 169} 170 171 172############################################################################### 173# 174# _store_eof() 175# 176# Writes Excel EOF record to indicate the end of a BIFF stream. 177# 178sub _store_eof { 179 180 my $self = shift; 181 my $record = 0x000A; # Record identifier 182 my $length = 0x0000; # Number of bytes to follow 183 184 my $header = pack("vv", $record, $length); 185 186 $self->_append($header); 187} 188 189 190############################################################################### 191# 192# _add_continue() 193# 194# Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In 195# Excel 97 the limit is 8228 bytes. Records that are longer than these limits 196# must be split up into CONTINUE blocks. 197# 198# This function take a long BIFF record and inserts CONTINUE records as 199# necessary. 200# 201# Some records have their own specialised Continue blocks so there is also an 202# option to bypass this function. 203# 204sub _add_continue { 205 206 my $self = shift; 207 my $data = $_[0]; 208 my $limit = $self->{_limit}; 209 my $record = 0x003C; # Record identifier 210 my $header; 211 my $tmp; 212 213 # Skip this if another method handles the continue blocks. 214 return $data if $self->{_ignore_continue}; 215 216 # The first 2080/8224 bytes remain intact. However, we have to change 217 # the length field of the record. 218 # 219 $tmp = substr($data, 0, $limit, ""); 220 substr($tmp, 2, 2, pack("v", $limit-4)); 221 222 # Strip out chunks of 2080/8224 bytes +4 for the header. 223 while (length($data) > $limit) { 224 $header = pack("vv", $record, $limit); 225 $tmp .= $header; 226 $tmp .= substr($data, 0, $limit, ""); 227 } 228 229 # Mop up the last of the data 230 $header = pack("vv", $record, length($data)); 231 $tmp .= $header; 232 $tmp .= $data; 233 234 return $tmp ; 235} 236 237 238############################################################################### 239# 240# _add_mso_generic() 241# 242# Create a mso structure that is part of an Escher drawing object. These are 243# are used for images, comments and filters. This generic method is used by 244# other methods to create specific mso records. 245# 246# Returns the packed record. 247# 248sub _add_mso_generic { 249 250 my $self = shift; 251 my $type = $_[0]; 252 my $version = $_[1]; 253 my $instance = $_[2]; 254 my $data = $_[3]; 255 my $length = defined $_[4] ? $_[4] : length($data); 256 257 # The header contains version and instance info packed into 2 bytes. 258 my $header = $version | ($instance << 4); 259 260 my $record = pack "vvV", $header, $type, $length; 261 $record .= $data; 262 263 return $record; 264} 265 266 267############################################################################### 268# 269# For debugging 270# 271sub _hexout { 272 273 my $self = shift; 274 275 print +(caller(1))[3], "\n"; 276 277 my $data = join '', @_; 278 279 my @bytes = unpack("H*", $data) =~ /../g; 280 281 while (@bytes > 16) { 282 print join " ", splice @bytes, 0, 16; 283 print "\n"; 284 } 285 print join " ", @bytes, "\n\n"; 286} 287 288 289 2901; 291 292 293__END__ 294 295=encoding latin1 296 297=head1 NAME 298 299BIFFwriter - An abstract base class for Excel workbooks and worksheets. 300 301=head1 SYNOPSIS 302 303See the documentation for Spreadsheet::WriteExcel 304 305=head1 DESCRIPTION 306 307This module is used in conjunction with Spreadsheet::WriteExcel. 308 309=head1 AUTHOR 310 311John McNamara jmcnamara@cpan.org 312 313=head1 COPYRIGHT 314 315Copyright MM-MMX, John McNamara. 316 317All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself. 318