// * // * The majority of this is _NOT_ my code. I simply ported it from the // * PERL Spreadsheet::WriteExcel module. // * // * The author of the Spreadsheet::WriteExcel module is John McNamara // * // * // * I _DO_ maintain this code, and John McNamara has nothing to do with the // * porting of this code to PHP. Any questions directly related to this // * class library should be directed to me. // * // * License Information: // * // * Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets // * Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com // * // * This library is free software; you can redistribute it and/or // * modify it under the terms of the GNU Lesser General Public // * License as published by the Free Software Foundation; either // * version 2.1 of the License, or (at your option) any later version. // * // * This library is distributed in the hope that it will be useful, // * but WITHOUT ANY WARRANTY; without even the implied warranty of // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // * Lesser General Public License for more details. // * // * You should have received a copy of the GNU Lesser General Public // * License along with this library; if not, write to the Free Software // * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // */ class BIFFwriter { /** * The byte order of this architecture. 0 => little endian, 1 => big endian. * * @var int */ private static $byteOrder; /** * The string containing the data of the BIFF stream. * * @var null|string */ public $_data; /** * The size of the data in bytes. Should be the same as strlen($this->_data). * * @var int */ public $_datasize; /** * The maximum length for a BIFF record (excluding record header and length field). See addContinue(). * * @var int * * @see addContinue() */ private $limit = 8224; /** * Constructor. */ public function __construct() { $this->_data = ''; $this->_datasize = 0; } /** * Determine the byte order and store it as class data to avoid * recalculating it for each call to new(). * * @return int */ public static function getByteOrder() { if (!isset(self::$byteOrder)) { // Check if "pack" gives the required IEEE 64bit float $teststr = pack('d', 1.2345); $number = pack('C8', 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F); if ($number == $teststr) { $byte_order = 0; // Little Endian } elseif ($number == strrev($teststr)) { $byte_order = 1; // Big Endian } else { // Give up. I'll fix this in a later version. throw new WriterException('Required floating point format not supported on this platform.'); } self::$byteOrder = $byte_order; } return self::$byteOrder; } /** * General storage function. * * @param string $data binary data to append */ protected function append($data): void { if (strlen($data) - 4 > $this->limit) { $data = $this->addContinue($data); } $this->_data .= $data; $this->_datasize += strlen($data); } /** * General storage function like append, but returns string instead of modifying $this->_data. * * @param string $data binary data to write * * @return string */ public function writeData($data) { if (strlen($data) - 4 > $this->limit) { $data = $this->addContinue($data); } $this->_datasize += strlen($data); return $data; } /** * Writes Excel BOF record to indicate the beginning of a stream or * sub-stream in the BIFF file. * * @param int $type type of BIFF file to write: 0x0005 Workbook, * 0x0010 Worksheet */ protected function storeBof($type): void { $record = 0x0809; // Record identifier (BIFF5-BIFF8) $length = 0x0010; // by inspection of real files, MS Office Excel 2007 writes the following $unknown = pack('VV', 0x000100D1, 0x00000406); $build = 0x0DBB; // Excel 97 $year = 0x07CC; // Excel 97 $version = 0x0600; // BIFF8 $header = pack('vv', $record, $length); $data = pack('vvvv', $version, $type, $build, $year); $this->append($header . $data . $unknown); } /** * Writes Excel EOF record to indicate the end of a BIFF stream. */ protected function storeEof(): void { $record = 0x000A; // Record identifier $length = 0x0000; // Number of bytes to follow $header = pack('vv', $record, $length); $this->append($header); } /** * Writes Excel EOF record to indicate the end of a BIFF stream. */ public function writeEof() { $record = 0x000A; // Record identifier $length = 0x0000; // Number of bytes to follow $header = pack('vv', $record, $length); return $this->writeData($header); } /** * 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 takes a long BIFF record and inserts CONTINUE records as * necessary. * * @param string $data The original binary data to be written * * @return string A very convenient string of continue blocks */ private function addContinue($data) { $limit = $this->limit; $record = 0x003C; // Record identifier // The first 2080/8224 bytes remain intact. However, we have to change // the length field of the record. $tmp = substr($data, 0, 2) . pack('v', $limit) . substr($data, 4, $limit); $header = pack('vv', $record, $limit); // Headers for continue records // Retrieve chunks of 2080/8224 bytes +4 for the header. $data_length = strlen($data); for ($i = $limit + 4; $i < ($data_length - $limit); $i += $limit) { $tmp .= $header; $tmp .= substr($data, $i, $limit); } // Retrieve the last chunk of data $header = pack('vv', $record, strlen($data) - $i); $tmp .= $header; $tmp .= substr($data, $i); return $tmp; } }