1<?php 2/* 3* Module written/ported by Xavier Noguer <xnoguer@php.net> 4* 5* The majority of this is _NOT_ my code. I simply ported it from the 6* PERL Spreadsheet::WriteExcel module. 7* 8* The author of the Spreadsheet::WriteExcel module is John McNamara 9* <jmcnamara@cpan.org> 10* 11* I _DO_ maintain this code, and John McNamara has nothing to do with the 12* porting of this code to PHP. Any questions directly related to this 13* class library should be directed to me. 14* 15* License Information: 16* 17* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets 18* Copyright (c) 2002-2003 Xavier Noguer xnoguer@php.net 19* 20* This library is free software; you can redistribute it and/or 21* modify it under the terms of the GNU Lesser General Public 22* License as published by the Free Software Foundation; either 23* version 2.1 of the License, or (at your option) any later version. 24* 25* This library is distributed in the hope that it will be useful, 26* but WITHOUT ANY WARRANTY; without even the implied warranty of 27* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 28* Lesser General Public License for more details. 29* 30* You should have received a copy of the GNU Lesser General Public 31* License along with this library; if not, write to the Free Software 32* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 33*/ 34 35/** 36* Class for writing Excel BIFF records. 37* 38* From "MICROSOFT EXCEL BINARY FILE FORMAT" by Mark O'Brien (Microsoft Corporation): 39* 40* BIFF (BInary File Format) is the file format in which Excel documents are 41* saved on disk. A BIFF file is a complete description of an Excel document. 42* BIFF files consist of sequences of variable-length records. There are many 43* different types of BIFF records. For example, one record type describes a 44* formula entered into a cell; one describes the size and location of a 45* window into a document; another describes a picture format. 46* 47* @author Xavier Noguer <xnoguer@php.net> 48* @category FileFormats 49* @package Spreadsheet_Excel_Writer 50*/ 51 52class Spreadsheet_Excel_Writer_BIFFwriter extends PEAR 53{ 54 /** 55 * The BIFF/Excel version (5). 56 * @var integer 57 */ 58 var $_BIFF_version = 0x0500; 59 60 /** 61 * The byte order of this architecture. 0 => little endian, 1 => big endian 62 * @var integer 63 */ 64 var $_byte_order; 65 66 /** 67 * The string containing the data of the BIFF stream 68 * @var string 69 */ 70 var $_data; 71 72 /** 73 * The size of the data in bytes. Should be the same as strlen($this->_data) 74 * @var integer 75 */ 76 var $_datasize; 77 78 /** 79 * The maximun length for a BIFF record. See _addContinue() 80 * @var integer 81 * @see _addContinue() 82 */ 83 var $_limit; 84 85 /** 86 * Constructor 87 * 88 * @access public 89 */ 90 function __construct() 91 { 92 $this->_byte_order = ''; 93 $this->_data = ''; 94 $this->_datasize = 0; 95 $this->_limit = 2080; 96 // Set the byte order 97 $this->_setByteOrder(); 98 } 99 100 /** 101 * Determine the byte order and store it as class data to avoid 102 * recalculating it for each call to new(). 103 * 104 * @access private 105 */ 106 function _setByteOrder() 107 { 108 // Check if "pack" gives the required IEEE 64bit float 109 $teststr = pack("d", 1.2345); 110 $number = pack("C8", 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F); 111 if ($number == $teststr) { 112 $byte_order = 0; // Little Endian 113 } 114 elseif ($number == strrev($teststr)){ 115 $byte_order = 1; // Big Endian 116 } 117 else { 118 // Give up. I'll fix this in a later version. 119 return $this->raiseError("Required floating point format ". 120 "not supported on this platform."); 121 } 122 $this->_byte_order = $byte_order; 123 } 124 125 /** 126 * General storage function 127 * 128 * @param string $data binary data to prepend 129 * @access private 130 */ 131 function _prepend($data) 132 { 133 if (strlen($data) > $this->_limit) { 134 $data = $this->_addContinue($data); 135 } 136 $this->_data = $data.$this->_data; 137 $this->_datasize += strlen($data); 138 } 139 140 /** 141 * General storage function 142 * 143 * @param string $data binary data to append 144 * @access private 145 */ 146 function _append($data) 147 { 148 if (strlen($data) > $this->_limit) { 149 $data = $this->_addContinue($data); 150 } 151 $this->_data = $this->_data.$data; 152 $this->_datasize += strlen($data); 153 } 154 155 /** 156 * Writes Excel BOF record to indicate the beginning of a stream or 157 * sub-stream in the BIFF file. 158 * 159 * @param integer $type Type of BIFF file to write: 0x0005 Workbook, 160 * 0x0010 Worksheet. 161 * @access private 162 */ 163 function _storeBof($type) 164 { 165 $record = 0x0809; // Record identifier 166 167 // According to the SDK $build and $year should be set to zero. 168 // However, this throws a warning in Excel 5. So, use magic numbers. 169 if ($this->_BIFF_version == 0x0500) { 170 $length = 0x0008; 171 $unknown = ''; 172 $build = 0x096C; 173 $year = 0x07C9; 174 } 175 elseif ($this->_BIFF_version == 0x0600) { 176 $length = 0x0010; 177 $unknown = pack("VV", 0x00000041, 0x00000006); //unknown last 8 bytes for BIFF8 178 $build = 0x0DBB; 179 $year = 0x07CC; 180 } 181 $version = $this->_BIFF_version; 182 183 $header = pack("vv", $record, $length); 184 $data = pack("vvvv", $version, $type, $build, $year); 185 $this->_prepend($header.$data.$unknown); 186 } 187 188 /** 189 * Writes Excel EOF record to indicate the end of a BIFF stream. 190 * 191 * @access private 192 */ 193 function _storeEof() 194 { 195 $record = 0x000A; // Record identifier 196 $length = 0x0000; // Number of bytes to follow 197 $header = pack("vv", $record, $length); 198 $this->_append($header); 199 } 200 201 /** 202 * Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In 203 * Excel 97 the limit is 8228 bytes. Records that are longer than these limits 204 * must be split up into CONTINUE blocks. 205 * 206 * This function takes a long BIFF record and inserts CONTINUE records as 207 * necessary. 208 * 209 * @param string $data The original binary data to be written 210 * @return string A very convenient string of continue blocks 211 * @access private 212 */ 213 function _addContinue($data) 214 { 215 $limit = $this->_limit; 216 $record = 0x003C; // Record identifier 217 218 // The first 2080/8224 bytes remain intact. However, we have to change 219 // the length field of the record. 220 $tmp = substr($data, 0, 2).pack("v", $limit-4).substr($data, 4, $limit - 4); 221 222 $header = pack("vv", $record, $limit); // Headers for continue records 223 224 // Retrieve chunks of 2080/8224 bytes +4 for the header. 225 for($i = $limit; $i < strlen($data) - $limit; $i += $limit) 226 { 227 $tmp .= $header; 228 $tmp .= substr($data, $i, $limit); 229 } 230 231 // Retrieve the last chunk of data 232 $header = pack("vv", $record, strlen($data) - $i); 233 $tmp .= $header; 234 $tmp .= substr($data,$i,strlen($data) - $i); 235 236 return $tmp; 237 } 238} 239