BIFFwriter.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
  3. use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
  4. // Original file header of PEAR::Spreadsheet_Excel_Writer_BIFFwriter (used as the base for this class):
  5. // -----------------------------------------------------------------------------------------
  6. // * Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
  7. // *
  8. // * The majority of this is _NOT_ my code. I simply ported it from the
  9. // * PERL Spreadsheet::WriteExcel module.
  10. // *
  11. // * The author of the Spreadsheet::WriteExcel module is John McNamara
  12. // * <jmcnamara@cpan.org>
  13. // *
  14. // * I _DO_ maintain this code, and John McNamara has nothing to do with the
  15. // * porting of this code to PHP. Any questions directly related to this
  16. // * class library should be directed to me.
  17. // *
  18. // * License Information:
  19. // *
  20. // * Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
  21. // * Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
  22. // *
  23. // * This library is free software; you can redistribute it and/or
  24. // * modify it under the terms of the GNU Lesser General Public
  25. // * License as published by the Free Software Foundation; either
  26. // * version 2.1 of the License, or (at your option) any later version.
  27. // *
  28. // * This library is distributed in the hope that it will be useful,
  29. // * but WITHOUT ANY WARRANTY; without even the implied warranty of
  30. // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  31. // * Lesser General Public License for more details.
  32. // *
  33. // * You should have received a copy of the GNU Lesser General Public
  34. // * License along with this library; if not, write to the Free Software
  35. // * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  36. // */
  37. class BIFFwriter
  38. {
  39. /**
  40. * The byte order of this architecture. 0 => little endian, 1 => big endian.
  41. *
  42. * @var int
  43. */
  44. private static $byteOrder;
  45. /**
  46. * The string containing the data of the BIFF stream.
  47. *
  48. * @var string
  49. */
  50. public $_data;
  51. /**
  52. * The size of the data in bytes. Should be the same as strlen($this->_data).
  53. *
  54. * @var int
  55. */
  56. public $_datasize;
  57. /**
  58. * The maximum length for a BIFF record (excluding record header and length field). See addContinue().
  59. *
  60. * @var int
  61. *
  62. * @see addContinue()
  63. */
  64. private $limit = 8224;
  65. /**
  66. * Constructor.
  67. */
  68. public function __construct()
  69. {
  70. $this->_data = '';
  71. $this->_datasize = 0;
  72. }
  73. /**
  74. * Determine the byte order and store it as class data to avoid
  75. * recalculating it for each call to new().
  76. *
  77. * @return int
  78. */
  79. public static function getByteOrder()
  80. {
  81. if (!isset(self::$byteOrder)) {
  82. // Check if "pack" gives the required IEEE 64bit float
  83. $teststr = pack('d', 1.2345);
  84. $number = pack('C8', 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F);
  85. if ($number == $teststr) {
  86. $byte_order = 0; // Little Endian
  87. } elseif ($number == strrev($teststr)) {
  88. $byte_order = 1; // Big Endian
  89. } else {
  90. // Give up. I'll fix this in a later version.
  91. throw new WriterException('Required floating point format not supported on this platform.');
  92. }
  93. self::$byteOrder = $byte_order;
  94. }
  95. return self::$byteOrder;
  96. }
  97. /**
  98. * General storage function.
  99. *
  100. * @param string $data binary data to append
  101. */
  102. protected function append($data)
  103. {
  104. if (strlen($data) - 4 > $this->limit) {
  105. $data = $this->addContinue($data);
  106. }
  107. $this->_data .= $data;
  108. $this->_datasize += strlen($data);
  109. }
  110. /**
  111. * General storage function like append, but returns string instead of modifying $this->_data.
  112. *
  113. * @param string $data binary data to write
  114. *
  115. * @return string
  116. */
  117. public function writeData($data)
  118. {
  119. if (strlen($data) - 4 > $this->limit) {
  120. $data = $this->addContinue($data);
  121. }
  122. $this->_datasize += strlen($data);
  123. return $data;
  124. }
  125. /**
  126. * Writes Excel BOF record to indicate the beginning of a stream or
  127. * sub-stream in the BIFF file.
  128. *
  129. * @param int $type type of BIFF file to write: 0x0005 Workbook,
  130. * 0x0010 Worksheet
  131. */
  132. protected function storeBof($type)
  133. {
  134. $record = 0x0809; // Record identifier (BIFF5-BIFF8)
  135. $length = 0x0010;
  136. // by inspection of real files, MS Office Excel 2007 writes the following
  137. $unknown = pack('VV', 0x000100D1, 0x00000406);
  138. $build = 0x0DBB; // Excel 97
  139. $year = 0x07CC; // Excel 97
  140. $version = 0x0600; // BIFF8
  141. $header = pack('vv', $record, $length);
  142. $data = pack('vvvv', $version, $type, $build, $year);
  143. $this->append($header . $data . $unknown);
  144. }
  145. /**
  146. * Writes Excel EOF record to indicate the end of a BIFF stream.
  147. */
  148. protected function storeEof()
  149. {
  150. $record = 0x000A; // Record identifier
  151. $length = 0x0000; // Number of bytes to follow
  152. $header = pack('vv', $record, $length);
  153. $this->append($header);
  154. }
  155. /**
  156. * Writes Excel EOF record to indicate the end of a BIFF stream.
  157. */
  158. public function writeEof()
  159. {
  160. $record = 0x000A; // Record identifier
  161. $length = 0x0000; // Number of bytes to follow
  162. $header = pack('vv', $record, $length);
  163. return $this->writeData($header);
  164. }
  165. /**
  166. * Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In
  167. * Excel 97 the limit is 8228 bytes. Records that are longer than these limits
  168. * must be split up into CONTINUE blocks.
  169. *
  170. * This function takes a long BIFF record and inserts CONTINUE records as
  171. * necessary.
  172. *
  173. * @param string $data The original binary data to be written
  174. *
  175. * @return string A very convenient string of continue blocks
  176. */
  177. private function addContinue($data)
  178. {
  179. $limit = $this->limit;
  180. $record = 0x003C; // Record identifier
  181. // The first 2080/8224 bytes remain intact. However, we have to change
  182. // the length field of the record.
  183. $tmp = substr($data, 0, 2) . pack('v', $limit) . substr($data, 4, $limit);
  184. $header = pack('vv', $record, $limit); // Headers for continue records
  185. // Retrieve chunks of 2080/8224 bytes +4 for the header.
  186. $data_length = strlen($data);
  187. for ($i = $limit + 4; $i < ($data_length - $limit); $i += $limit) {
  188. $tmp .= $header;
  189. $tmp .= substr($data, $i, $limit);
  190. }
  191. // Retrieve the last chunk of data
  192. $header = pack('vv', $record, strlen($data) - $i);
  193. $tmp .= $header;
  194. $tmp .= substr($data, $i);
  195. return $tmp;
  196. }
  197. }