Dan's fix from MDL-7263
[moodle.git] / lib / excel / BIFFwriter.php
blobbe245069a3279dcf53d8aa9a5fc8de1a78d02e52
1 <?php
2 /*
3 * Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
5 * The majority of this is _NOT_ my code. I simply ported it from the
6 * PERL Spreadsheet::WriteExcel module.
8 * The author of the Spreadsheet::WriteExcel module is John McNamara
9 * <jmcnamara@cpan.org>
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.
15 * License Information:
17 * Spreadsheet::WriteExcel: A library for generating Excel Spreadsheets
18 * Copyright (C) 2002 Xavier Noguer xnoguer@rezebra.com
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.
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.
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
35 /**
36 * Class for writing Excel BIFF records.
38 * From "MICROSOFT EXCEL BINARY FILE FORMAT" by Mark O'Brien (Microsoft Corporation):
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.
47 * @author Xavier Noguer <xnoguer@rezebra.com>
48 * @package Spreadsheet_WriteExcel
51 class BIFFWriter
53 var $_BIFF_version = 0x0500;
55 /**
56 * Constructor
58 * @access public
60 function BIFFwriter()
62 // The byte order of this architecture. 0 => little endian, 1 => big endian
63 $this->_byte_order = '';
64 // The string containing the data of the BIFF stream
65 $this->_data = '';
66 // Should be the same as strlen($this->_data)
67 $this->_datasize = 0;
68 // The maximun length for a BIFF record. See _add_continue()
69 $this->_limit = 2080;
70 // Set the byte order
71 $this->_set_byte_order();
74 /**
75 * Determine the byte order and store it as class data to avoid
76 * recalculating it for each call to new().
78 * @access private
80 function _set_byte_order()
82 if ($this->_byte_order == '')
84 // Check if "pack" gives the required IEEE 64bit float
85 $teststr = pack("d", 1.2345);
86 $number = pack("C8", 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F);
87 if ($number == $teststr) {
88 $byte_order = 0; // Little Endian
90 elseif ($number == strrev($teststr)){
91 $byte_order = 1; // Big Endian
93 else {
94 // Give up. I'll fix this in a later version.
95 die("Required floating point format not supported ".
96 "on this platform. See the portability section ".
97 "of the documentation."
101 $this->_byte_order = $byte_order;
105 * General storage function
107 * @param string $data binary data to prepend
108 * @access private
110 function _prepend($data)
112 if (strlen($data) > $this->_limit) {
113 $data = $this->_add_continue($data);
115 $this->_data = $data.$this->_data;
116 $this->_datasize += strlen($data);
120 * General storage function
122 * @param string $data binary data to append
123 * @access private
125 function _append($data)
127 if (strlen($data) > $this->_limit) {
128 $data = $this->_add_continue($data);
130 $this->_data = $this->_data.$data;
131 $this->_datasize += strlen($data);
135 * Writes Excel BOF record to indicate the beginning of a stream or
136 * sub-stream in the BIFF file.
138 * @param integer $type type of BIFF file to write: 0x0005 Workbook, 0x0010 Worksheet.
139 * @access private
141 function _store_bof($type)
143 $record = 0x0809; // Record identifier
144 $length = 0x0008; // Number of bytes to follow
145 $version = $this->_BIFF_version;
147 // According to the SDK $build and $year should be set to zero.
148 // However, this throws a warning in Excel 5. So, use these
149 // magic numbers.
150 $build = 0x096C;
151 $year = 0x07C9;
153 $header = pack("vv", $record, $length);
154 $data = pack("vvvv", $version, $type, $build, $year);
155 $this->_prepend($header.$data);
159 * Writes Excel EOF record to indicate the end of a BIFF stream.
161 * @access private
163 function _store_eof()
165 $record = 0x000A; // Record identifier
166 $length = 0x0000; // Number of bytes to follow
167 $header = pack("vv", $record, $length);
168 $this->_append($header);
172 * Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In
173 * Excel 97 the limit is 8228 bytes. Records that are longer than these limits
174 * must be split up into CONTINUE blocks.
176 * This function takes a long BIFF record and inserts CONTINUE records as
177 * necessary.
179 * @param string $data The original binary data to be written
180 * @return string A very convenient string of continue blocks
181 * @access private
183 function _add_continue($data)
185 $limit = $this->_limit;
186 $record = 0x003C; // Record identifier
188 // The first 2080/8224 bytes remain intact. However, we have to change
189 // the length field of the record.
190 $tmp = substr($data, 0, 2).pack("v", $limit-4).substr($data, 4, $limit - 4);
192 $header = pack("vv", $record, $limit); // Headers for continue records
194 // Retrieve chunks of 2080/8224 bytes +4 for the header.
195 for($i = $limit; $i < strlen($data) - $limit; $i += $limit)
197 $tmp .= $header;
198 $tmp .= substr($data, $i, $limit);
201 // Retrieve the last chunk of data
202 $header = pack("vv", $record, strlen($data) - $i);
203 $tmp .= $header;
204 $tmp .= substr($data,$i,strlen($data) - $i);
206 return($tmp);