BIFFwriter.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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. require_once 'PEAR.php';
  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. class Spreadsheet_Excel_Writer_BIFFwriter extends PEAR
  52. {
  53. /**
  54. * The BIFF/Excel version (5).
  55. * @var integer
  56. */
  57. var $_BIFF_version = 0x0500;
  58. /**
  59. * The byte order of this architecture. 0 => little endian, 1 => big endian
  60. * @var integer
  61. */
  62. var $_byte_order;
  63. /**
  64. * The string containing the data of the BIFF stream
  65. * @var string
  66. */
  67. var $_data;
  68. /**
  69. * The size of the data in bytes. Should be the same as strlen($this->_data)
  70. * @var integer
  71. */
  72. var $_datasize;
  73. /**
  74. * The maximun length for a BIFF record. See _addContinue()
  75. * @var integer
  76. * @see _addContinue()
  77. */
  78. var $_limit;
  79. /**
  80. * The temporary dir for storing the OLE file
  81. * @var string
  82. */
  83. var $_tmp_dir;
  84. /**
  85. * Constructor
  86. *
  87. * @access public
  88. */
  89. function Spreadsheet_Excel_Writer_BIFFwriter()
  90. {
  91. $this->_byte_order = '';
  92. $this->_data = '';
  93. $this->_datasize = 0;
  94. $this->_limit = 2080;
  95. $this->_tmp_dir = '';
  96. // Set the byte order
  97. $this->_setByteOrder();
  98. }
  99. /**
  100. * Determine the byte order and store it as class data to avoid
  101. * recalculating it for each call to new().
  102. *
  103. * @access private
  104. */
  105. function _setByteOrder()
  106. {
  107. // Check if "pack" gives the required IEEE 64bit float
  108. $teststr = pack("d", 1.2345);
  109. $number = pack("C8", 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F);
  110. if ($number == $teststr) {
  111. $byte_order = 0; // Little Endian
  112. } elseif ($number == strrev($teststr)){
  113. $byte_order = 1; // Big Endian
  114. } else {
  115. // Give up. I'll fix this in a later version.
  116. return $this->raiseError("Required floating point format ".
  117. "not supported on this platform.");
  118. }
  119. $this->_byte_order = $byte_order;
  120. }
  121. /**
  122. * General storage function
  123. *
  124. * @param string $data binary data to prepend
  125. * @access private
  126. */
  127. function _prepend($data)
  128. {
  129. if (strlen($data) > $this->_limit) {
  130. $data = $this->_addContinue($data);
  131. }
  132. $this->_data = $data.$this->_data;
  133. $this->_datasize += strlen($data);
  134. }
  135. /**
  136. * General storage function
  137. *
  138. * @param string $data binary data to append
  139. * @access private
  140. */
  141. function _append($data)
  142. {
  143. if (strlen($data) > $this->_limit) {
  144. $data = $this->_addContinue($data);
  145. }
  146. $this->_data = $this->_data.$data;
  147. $this->_datasize += strlen($data);
  148. }
  149. /**
  150. * Writes Excel BOF record to indicate the beginning of a stream or
  151. * sub-stream in the BIFF file.
  152. *
  153. * @param integer $type Type of BIFF file to write: 0x0005 Workbook,
  154. * 0x0010 Worksheet.
  155. * @access private
  156. */
  157. function _storeBof($type)
  158. {
  159. $record = 0x0809; // Record identifier
  160. // According to the SDK $build and $year should be set to zero.
  161. // However, this throws a warning in Excel 5. So, use magic numbers.
  162. if ($this->_BIFF_version == 0x0500) {
  163. $length = 0x0008;
  164. $unknown = '';
  165. $build = 0x096C;
  166. $year = 0x07C9;
  167. } elseif ($this->_BIFF_version == 0x0600) {
  168. $length = 0x0010;
  169. $unknown = pack("VV", 0x00000041, 0x00000006); //unknown last 8 bytes for BIFF8
  170. $build = 0x0DBB;
  171. $year = 0x07CC;
  172. }
  173. $version = $this->_BIFF_version;
  174. $header = pack("vv", $record, $length);
  175. $data = pack("vvvv", $version, $type, $build, $year);
  176. $this->_prepend($header . $data . $unknown);
  177. }
  178. /**
  179. * Writes Excel EOF record to indicate the end of a BIFF stream.
  180. *
  181. * @access private
  182. */
  183. function _storeEof()
  184. {
  185. $record = 0x000A; // Record identifier
  186. $length = 0x0000; // Number of bytes to follow
  187. $header = pack("vv", $record, $length);
  188. $this->_append($header);
  189. }
  190. /**
  191. * Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In
  192. * Excel 97 the limit is 8228 bytes. Records that are longer than these limits
  193. * must be split up into CONTINUE blocks.
  194. *
  195. * This function takes a long BIFF record and inserts CONTINUE records as
  196. * necessary.
  197. *
  198. * @param string $data The original binary data to be written
  199. * @return string A very convenient string of continue blocks
  200. * @access private
  201. */
  202. function _addContinue($data)
  203. {
  204. $limit = $this->_limit;
  205. $record = 0x003C; // Record identifier
  206. // The first 2080/8224 bytes remain intact. However, we have to change
  207. // the length field of the record.
  208. $tmp = substr($data, 0, 2).pack("v", $limit-4).substr($data, 4, $limit - 4);
  209. $header = pack("vv", $record, $limit); // Headers for continue records
  210. // Retrieve chunks of 2080/8224 bytes +4 for the header.
  211. $data_length = strlen($data);
  212. for ($i = $limit; $i < ($data_length - $limit); $i += $limit) {
  213. $tmp .= $header;
  214. $tmp .= substr($data, $i, $limit);
  215. }
  216. // Retrieve the last chunk of data
  217. $header = pack("vv", $record, strlen($data) - $i);
  218. $tmp .= $header;
  219. $tmp .= substr($data, $i, strlen($data) - $i);
  220. return $tmp;
  221. }
  222. /**
  223. * Sets the temp dir used for storing the OLE file
  224. *
  225. * @access public
  226. * @param string $dir The dir to be used as temp dir
  227. * @return true if given dir is valid, false otherwise
  228. */
  229. function setTempDir($dir)
  230. {
  231. if (is_dir($dir)) {
  232. $this->_tmp_dir = $dir;
  233. return true;
  234. }
  235. return false;
  236. }
  237. }
  238. ?>