PageRenderTime 59ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/application/libraries/admin/pear/Spreadsheet/Excel/Writer/Workbook.php

https://bitbucket.org/sammousa/valuematchbv-ls2
PHP | 1606 lines | 869 code | 189 blank | 548 comment | 123 complexity | 9e2bc28580629b300ccadcb53a393d66 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause, GPL-3.0, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. * Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
  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@rezebra.com
  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. if (isset($_REQUEST['homedir'])) {die('You cannot start this script directly');}
  35. require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Format.php';
  36. require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'BIFFwriter.php';
  37. require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Worksheet.php';
  38. require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Parser.php';
  39. require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'OLE'.DIRECTORY_SEPARATOR.'PPS'.DIRECTORY_SEPARATOR.'Root.php';
  40. require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'OLE'.DIRECTORY_SEPARATOR.'PPS'.DIRECTORY_SEPARATOR.'File.php';
  41. /**
  42. * Class for generating Excel Spreadsheets
  43. *
  44. * @author Xavier Noguer <xnoguer@rezebra.com>
  45. * @category FileFormats
  46. * @package Spreadsheet_Excel_Writer
  47. */
  48. class Spreadsheet_Excel_Writer_Workbook extends Spreadsheet_Excel_Writer_BIFFwriter
  49. {
  50. /**
  51. * Filename for the Workbook
  52. * @var string
  53. */
  54. var $_filename;
  55. /**
  56. * Formula parser
  57. * @var object Parser
  58. */
  59. var $_parser;
  60. /**
  61. * Flag for 1904 date system (0 => base date is 1900, 1 => base date is 1904)
  62. * @var integer
  63. */
  64. var $_1904;
  65. /**
  66. * The active worksheet of the workbook (0 indexed)
  67. * @var integer
  68. */
  69. var $_activesheet;
  70. /**
  71. * 1st displayed worksheet in the workbook (0 indexed)
  72. * @var integer
  73. */
  74. var $_firstsheet;
  75. /**
  76. * Number of workbook tabs selected
  77. * @var integer
  78. */
  79. var $_selected;
  80. /**
  81. * Index for creating adding new formats to the workbook
  82. * @var integer
  83. */
  84. var $_xf_index;
  85. /**
  86. * Flag for preventing close from being called twice.
  87. * @var integer
  88. * @see close()
  89. */
  90. var $_fileclosed;
  91. /**
  92. * The BIFF file size for the workbook.
  93. * @var integer
  94. * @see _calcSheetOffsets()
  95. */
  96. var $_biffsize;
  97. /**
  98. * The default sheetname for all sheets created.
  99. * @var string
  100. */
  101. var $_sheetname;
  102. /**
  103. * The default XF format.
  104. * @var object Format
  105. */
  106. var $_tmp_format;
  107. /**
  108. * Array containing references to all of this workbook's worksheets
  109. * @var array
  110. */
  111. var $_worksheets;
  112. /**
  113. * Array of sheetnames for creating the EXTERNSHEET records
  114. * @var array
  115. */
  116. var $_sheetnames;
  117. /**
  118. * Array containing references to all of this workbook's formats
  119. * @var array
  120. */
  121. var $_formats;
  122. /**
  123. * Array containing the colour palette
  124. * @var array
  125. */
  126. var $_palette;
  127. /**
  128. * The default format for URLs.
  129. * @var object Format
  130. */
  131. var $_url_format;
  132. /**
  133. * The codepage indicates the text encoding used for strings
  134. * @var integer
  135. */
  136. var $_codepage;
  137. /**
  138. * The country code used for localization
  139. * @var integer
  140. */
  141. var $_country_code;
  142. /**
  143. * The temporary dir for storing the OLE file
  144. * @var string
  145. */
  146. var $_tmp_dir;
  147. /**
  148. * number of bytes for sizeinfo of strings
  149. * @var integer
  150. */
  151. var $_string_sizeinfo_size;
  152. /**
  153. * Class constructor
  154. *
  155. * @param string filename for storing the workbook. "-" for writing to stdout.
  156. * @access public
  157. */
  158. function Spreadsheet_Excel_Writer_Workbook($filename)
  159. {
  160. // It needs to call its parent's constructor explicitly
  161. $this->Spreadsheet_Excel_Writer_BIFFwriter();
  162. $this->_filename = $filename;
  163. $this->_parser = new Spreadsheet_Excel_Writer_Parser($this->_byte_order, $this->_BIFF_version);
  164. $this->_1904 = 0;
  165. $this->_activesheet = 0;
  166. $this->_firstsheet = 0;
  167. $this->_selected = 0;
  168. $this->_xf_index = 16; // 15 style XF's and 1 cell XF.
  169. $this->_fileclosed = 0;
  170. $this->_biffsize = 0;
  171. $this->_sheetname = 'Sheet';
  172. $this->_tmp_format = new Spreadsheet_Excel_Writer_Format($this->_BIFF_version);
  173. $this->_worksheets = array();
  174. $this->_sheetnames = array();
  175. $this->_formats = array();
  176. $this->_palette = array();
  177. $this->_codepage = 0x04E4; // FIXME: should change for BIFF8
  178. $this->_country_code = -1;
  179. $this->_string_sizeinfo = 3;
  180. // Add the default format for hyperlinks
  181. $this->_url_format =& $this->addFormat(array('color' => 'blue', 'underline' => 1));
  182. $this->_str_total = 0;
  183. $this->_str_unique = 0;
  184. $this->_str_table = array();
  185. $this->_setPaletteXl97();
  186. $this->_tmp_dir = '';
  187. }
  188. /**
  189. * Calls finalization methods.
  190. * This method should always be the last one to be called on every workbook
  191. *
  192. * @access public
  193. * @return mixed true on success. PEAR_Error on failure
  194. */
  195. function close()
  196. {
  197. if ($this->_fileclosed) { // Prevent close() from being called twice.
  198. return true;
  199. }
  200. $res = $this->_storeWorkbook();
  201. if ($this->isError($res)) {
  202. return $this->raiseError($res->getMessage());
  203. }
  204. $this->_fileclosed = 1;
  205. return true;
  206. }
  207. /**
  208. * An accessor for the _worksheets[] array
  209. * Returns an array of the worksheet objects in a workbook
  210. * It actually calls to worksheets()
  211. *
  212. * @access public
  213. * @see worksheets()
  214. * @return array
  215. */
  216. function sheets()
  217. {
  218. return $this->worksheets();
  219. }
  220. /**
  221. * An accessor for the _worksheets[] array.
  222. * Returns an array of the worksheet objects in a workbook
  223. *
  224. * @access public
  225. * @return array
  226. */
  227. function worksheets()
  228. {
  229. return $this->_worksheets;
  230. }
  231. /**
  232. * Sets the BIFF version.
  233. * This method exists just to access experimental functionality
  234. * from BIFF8. It will be deprecated !
  235. * Only possible value is 8 (Excel 97/2000).
  236. * For any other value it fails silently.
  237. *
  238. * @access public
  239. * @param integer $version The BIFF version
  240. */
  241. function setVersion($version)
  242. {
  243. if ($version == 8) { // only accept version 8
  244. $version = 0x0600;
  245. $this->_BIFF_version = $version;
  246. // change BIFFwriter limit for CONTINUE records
  247. $this->_limit = 8228;
  248. $this->_tmp_format->_BIFF_version = $version;
  249. $this->_url_format->_BIFF_version = $version;
  250. $this->_parser->_BIFF_version = $version;
  251. $total_worksheets = count($this->_worksheets);
  252. // change version for all worksheets too
  253. for ($i = 0; $i < $total_worksheets; $i++) {
  254. $this->_worksheets[$i]->_BIFF_version = $version;
  255. }
  256. $total_formats = count($this->_formats);
  257. // change version for all formats too
  258. for ($i = 0; $i < $total_formats; $i++) {
  259. $this->_formats[$i]->_BIFF_version = $version;
  260. }
  261. }
  262. }
  263. /**
  264. * Set the country identifier for the workbook
  265. *
  266. * @access public
  267. * @param integer $code Is the international calling country code for the
  268. * chosen country.
  269. */
  270. function setCountry($code)
  271. {
  272. $this->_country_code = $code;
  273. }
  274. /**
  275. * Add a new worksheet to the Excel workbook.
  276. * If no name is given the name of the worksheet will be Sheeti$i, with
  277. * $i in [1..].
  278. *
  279. * @access public
  280. * @param string $name the optional name of the worksheet
  281. * @return mixed reference to a worksheet object on success, PEAR_Error
  282. * on failure
  283. */
  284. function &addWorksheet($name = '')
  285. {
  286. $index = count($this->_worksheets);
  287. $sheetname = $this->_sheetname;
  288. if ($name == '') {
  289. $name = $sheetname.($index+1);
  290. }
  291. // Check that sheetname is <= 31 chars (Excel limit before BIFF8).
  292. if ($this->_BIFF_version != 0x0600)
  293. {
  294. if (strlen($name) > 31) {
  295. return $this->raiseError("Sheetname $name must be <= 31 chars");
  296. }
  297. }
  298. // Check that the worksheet name doesn't already exist: a fatal Excel error.
  299. $total_worksheets = count($this->_worksheets);
  300. for ($i = 0; $i < $total_worksheets; $i++) {
  301. if ($this->_worksheets[$i]->getName() == $name) {
  302. return $this->raiseError("Worksheet '$name' already exists");
  303. }
  304. }
  305. $worksheet = new Spreadsheet_Excel_Writer_Worksheet($this->_BIFF_version,
  306. $name, $index,
  307. $this->_activesheet, $this->_firstsheet,
  308. $this->_str_total, $this->_str_unique,
  309. $this->_str_table, $this->_url_format,
  310. $this->_parser);
  311. $this->_worksheets[$index] = &$worksheet; // Store ref for iterator
  312. $this->_sheetnames[$index] = $name; // Store EXTERNSHEET names
  313. $this->_parser->setExtSheet($name, $index); // Register worksheet name with parser
  314. return $worksheet;
  315. }
  316. /**
  317. * Add a new format to the Excel workbook.
  318. * Also, pass any properties to the Format constructor.
  319. *
  320. * @access public
  321. * @param array $properties array with properties for initializing the format.
  322. * @return &Spreadsheet_Excel_Writer_Format reference to an Excel Format
  323. */
  324. function &addFormat($properties = array())
  325. {
  326. $format = new Spreadsheet_Excel_Writer_Format($this->_BIFF_version, $this->_xf_index, $properties);
  327. $this->_xf_index += 1;
  328. $this->_formats[] = &$format;
  329. return $format;
  330. }
  331. /**
  332. * Create new validator.
  333. *
  334. * @access public
  335. * @return &Spreadsheet_Excel_Writer_Validator reference to a Validator
  336. */
  337. function &addValidator()
  338. {
  339. include_once 'Spreadsheet/Excel/Writer/Validator.php';
  340. /* FIXME: check for successful inclusion*/
  341. $valid = new Spreadsheet_Excel_Writer_Validator($this->_parser);
  342. return $valid;
  343. }
  344. /**
  345. * Change the RGB components of the elements in the colour palette.
  346. *
  347. * @access public
  348. * @param integer $index colour index
  349. * @param integer $red red RGB value [0-255]
  350. * @param integer $green green RGB value [0-255]
  351. * @param integer $blue blue RGB value [0-255]
  352. * @return integer The palette index for the custom color
  353. */
  354. function setCustomColor($index, $red, $green, $blue)
  355. {
  356. // Match a HTML #xxyyzz style parameter
  357. /*if (defined $_[1] and $_[1] =~ /^#(\w\w)(\w\w)(\w\w)/ ) {
  358. @_ = ($_[0], hex $1, hex $2, hex $3);
  359. }*/
  360. // Check that the colour index is the right range
  361. if ($index < 8 or $index > 64) {
  362. // TODO: assign real error codes
  363. return $this->raiseError("Color index $index outside range: 8 <= index <= 64");
  364. }
  365. // Check that the colour components are in the right range
  366. if (($red < 0 or $red > 255) ||
  367. ($green < 0 or $green > 255) ||
  368. ($blue < 0 or $blue > 255))
  369. {
  370. return $this->raiseError("Color component outside range: 0 <= color <= 255");
  371. }
  372. $index -= 8; // Adjust colour index (wingless dragonfly)
  373. // Set the RGB value
  374. $this->_palette[$index] = array($red, $green, $blue, 0);
  375. return($index + 8);
  376. }
  377. /**
  378. * Sets the colour palette to the Excel 97+ default.
  379. *
  380. * @access private
  381. */
  382. function _setPaletteXl97()
  383. {
  384. $this->_palette = array(
  385. array(0x00, 0x00, 0x00, 0x00), // 8
  386. array(0xff, 0xff, 0xff, 0x00), // 9
  387. array(0xff, 0x00, 0x00, 0x00), // 10
  388. array(0x00, 0xff, 0x00, 0x00), // 11
  389. array(0x00, 0x00, 0xff, 0x00), // 12
  390. array(0xff, 0xff, 0x00, 0x00), // 13
  391. array(0xff, 0x00, 0xff, 0x00), // 14
  392. array(0x00, 0xff, 0xff, 0x00), // 15
  393. array(0x80, 0x00, 0x00, 0x00), // 16
  394. array(0x00, 0x80, 0x00, 0x00), // 17
  395. array(0x00, 0x00, 0x80, 0x00), // 18
  396. array(0x80, 0x80, 0x00, 0x00), // 19
  397. array(0x80, 0x00, 0x80, 0x00), // 20
  398. array(0x00, 0x80, 0x80, 0x00), // 21
  399. array(0xc0, 0xc0, 0xc0, 0x00), // 22
  400. array(0x80, 0x80, 0x80, 0x00), // 23
  401. array(0x99, 0x99, 0xff, 0x00), // 24
  402. array(0x99, 0x33, 0x66, 0x00), // 25
  403. array(0xff, 0xff, 0xcc, 0x00), // 26
  404. array(0xcc, 0xff, 0xff, 0x00), // 27
  405. array(0x66, 0x00, 0x66, 0x00), // 28
  406. array(0xff, 0x80, 0x80, 0x00), // 29
  407. array(0x00, 0x66, 0xcc, 0x00), // 30
  408. array(0xcc, 0xcc, 0xff, 0x00), // 31
  409. array(0x00, 0x00, 0x80, 0x00), // 32
  410. array(0xff, 0x00, 0xff, 0x00), // 33
  411. array(0xff, 0xff, 0x00, 0x00), // 34
  412. array(0x00, 0xff, 0xff, 0x00), // 35
  413. array(0x80, 0x00, 0x80, 0x00), // 36
  414. array(0x80, 0x00, 0x00, 0x00), // 37
  415. array(0x00, 0x80, 0x80, 0x00), // 38
  416. array(0x00, 0x00, 0xff, 0x00), // 39
  417. array(0x00, 0xcc, 0xff, 0x00), // 40
  418. array(0xcc, 0xff, 0xff, 0x00), // 41
  419. array(0xcc, 0xff, 0xcc, 0x00), // 42
  420. array(0xff, 0xff, 0x99, 0x00), // 43
  421. array(0x99, 0xcc, 0xff, 0x00), // 44
  422. array(0xff, 0x99, 0xcc, 0x00), // 45
  423. array(0xcc, 0x99, 0xff, 0x00), // 46
  424. array(0xff, 0xcc, 0x99, 0x00), // 47
  425. array(0x33, 0x66, 0xff, 0x00), // 48
  426. array(0x33, 0xcc, 0xcc, 0x00), // 49
  427. array(0x99, 0xcc, 0x00, 0x00), // 50
  428. array(0xff, 0xcc, 0x00, 0x00), // 51
  429. array(0xff, 0x99, 0x00, 0x00), // 52
  430. array(0xff, 0x66, 0x00, 0x00), // 53
  431. array(0x66, 0x66, 0x99, 0x00), // 54
  432. array(0x96, 0x96, 0x96, 0x00), // 55
  433. array(0x00, 0x33, 0x66, 0x00), // 56
  434. array(0x33, 0x99, 0x66, 0x00), // 57
  435. array(0x00, 0x33, 0x00, 0x00), // 58
  436. array(0x33, 0x33, 0x00, 0x00), // 59
  437. array(0x99, 0x33, 0x00, 0x00), // 60
  438. array(0x99, 0x33, 0x66, 0x00), // 61
  439. array(0x33, 0x33, 0x99, 0x00), // 62
  440. array(0x33, 0x33, 0x33, 0x00), // 63
  441. );
  442. }
  443. /**
  444. * Assemble worksheets into a workbook and send the BIFF data to an OLE
  445. * storage.
  446. *
  447. * @access private
  448. * @return mixed true on success. PEAR_Error on failure
  449. */
  450. function _storeWorkbook()
  451. {
  452. // Ensure that at least one worksheet has been selected.
  453. if ($this->_activesheet == 0) {
  454. $this->_worksheets[0]->selected = 1;
  455. }
  456. // Calculate the number of selected worksheet tabs and call the finalization
  457. // methods for each worksheet
  458. $total_worksheets = count($this->_worksheets);
  459. for ($i = 0; $i < $total_worksheets; $i++) {
  460. if ($this->_worksheets[$i]->selected) {
  461. $this->_selected++;
  462. }
  463. $this->_worksheets[$i]->close($this->_sheetnames);
  464. }
  465. // Add Workbook globals
  466. $this->_storeBof(0x0005);
  467. $this->_storeCodepage();
  468. if ($this->_BIFF_version == 0x0600) {
  469. $this->_storeWindow1();
  470. }
  471. if ($this->_BIFF_version == 0x0500) {
  472. $this->_storeExterns(); // For print area and repeat rows
  473. }
  474. $this->_storeNames(); // For print area and repeat rows
  475. if ($this->_BIFF_version == 0x0500) {
  476. $this->_storeWindow1();
  477. }
  478. $this->_storeDatemode();
  479. $this->_storeAllFonts();
  480. $this->_storeAllNumFormats();
  481. $this->_storeAllXfs();
  482. $this->_storeAllStyles();
  483. $this->_storePalette();
  484. $this->_calcSheetOffsets();
  485. // Add BOUNDSHEET records
  486. for ($i = 0; $i < $total_worksheets; $i++) {
  487. $this->_storeBoundsheet($this->_worksheets[$i]->name,$this->_worksheets[$i]->offset);
  488. }
  489. if ($this->_country_code != -1) {
  490. $this->_storeCountry();
  491. }
  492. if ($this->_BIFF_version == 0x0600) {
  493. //$this->_storeSupbookInternal();
  494. /* TODO: store external SUPBOOK records and XCT and CRN records
  495. in case of external references for BIFF8 */
  496. //$this->_storeExternsheetBiff8();
  497. $this->_storeSharedStringsTable();
  498. }
  499. // End Workbook globals
  500. $this->_storeEof();
  501. // Store the workbook in an OLE container
  502. $res = $this->_storeOLEFile();
  503. if ($this->isError($res)) {
  504. return $this->raiseError($res->getMessage());
  505. }
  506. return true;
  507. }
  508. /**
  509. * Sets the temp dir used for storing the OLE file
  510. *
  511. * @access public
  512. * @param string $dir The dir to be used as temp dir
  513. * @return true if given dir is valid, false otherwise
  514. */
  515. function setTempDir($dir)
  516. {
  517. if (is_dir($dir)) {
  518. $this->_tmp_dir = $dir;
  519. return true;
  520. }
  521. return false;
  522. }
  523. /**
  524. * Store the workbook in an OLE container
  525. *
  526. * @access private
  527. * @return mixed true on success. PEAR_Error on failure
  528. */
  529. function _storeOLEFile()
  530. {
  531. $OLE = new OLE_PPS_File(OLE::Asc2Ucs('Book'));
  532. if ($this->_tmp_dir != '') {
  533. $OLE->setTempDir($this->_tmp_dir);
  534. }
  535. $res = $OLE->init();
  536. if ($this->isError($res)) {
  537. return $this->raiseError("OLE Error: ".$res->getMessage());
  538. }
  539. $OLE->append($this->_data);
  540. $total_worksheets = count($this->_worksheets);
  541. for ($i = 0; $i < $total_worksheets; $i++) {
  542. while ($tmp = $this->_worksheets[$i]->getData()) {
  543. $OLE->append($tmp);
  544. }
  545. }
  546. $root = new OLE_PPS_Root(time(), time(), array($OLE));
  547. if ($this->_tmp_dir != '') {
  548. $root->setTempDir($this->_tmp_dir);
  549. }
  550. $res = $root->save($this->_filename);
  551. if ($this->isError($res)) {
  552. return $this->raiseError("OLE Error: ".$res->getMessage());
  553. }
  554. return true;
  555. }
  556. /**
  557. * Calculate offsets for Worksheet BOF records.
  558. *
  559. * @access private
  560. */
  561. function _calcSheetOffsets()
  562. {
  563. if ($this->_BIFF_version == 0x0600) {
  564. $boundsheet_length = 12; // fixed length for a BOUNDSHEET record
  565. } else {
  566. $boundsheet_length = 11;
  567. }
  568. $EOF = 4;
  569. $offset = $this->_datasize;
  570. if ($this->_BIFF_version == 0x0600) {
  571. // add the length of the SST
  572. /* TODO: check this works for a lot of strings (> 8224 bytes) */
  573. $offset += $this->_calculateSharedStringsSizes();
  574. if ($this->_country_code != -1) {
  575. $offset += 8; // adding COUNTRY record
  576. }
  577. // add the lenght of SUPBOOK, EXTERNSHEET and NAME records
  578. //$offset += 8; // FIXME: calculate real value when storing the records
  579. }
  580. $total_worksheets = count($this->_worksheets);
  581. // add the length of the BOUNDSHEET records
  582. for ($i = 0; $i < $total_worksheets; $i++) {
  583. $offset += $boundsheet_length + strlen($this->_worksheets[$i]->name);
  584. }
  585. $offset += $EOF;
  586. for ($i = 0; $i < $total_worksheets; $i++) {
  587. $this->_worksheets[$i]->offset = $offset;
  588. $offset += $this->_worksheets[$i]->_datasize;
  589. }
  590. $this->_biffsize = $offset;
  591. }
  592. /**
  593. * Store the Excel FONT records.
  594. *
  595. * @access private
  596. */
  597. function _storeAllFonts()
  598. {
  599. // tmp_format is added by the constructor. We use this to write the default XF's
  600. $format = $this->_tmp_format;
  601. $font = $format->getFont();
  602. // Note: Fonts are 0-indexed. According to the SDK there is no index 4,
  603. // so the following fonts are 0, 1, 2, 3, 5
  604. //
  605. for ($i = 1; $i <= 5; $i++){
  606. $this->_append($font);
  607. }
  608. // Iterate through the XF objects and write a FONT record if it isn't the
  609. // same as the default FONT and if it hasn't already been used.
  610. //
  611. $fonts = array();
  612. $index = 6; // The first user defined FONT
  613. $key = $format->getFontKey(); // The default font from _tmp_format
  614. $fonts[$key] = 0; // Index of the default font
  615. $total_formats = count($this->_formats);
  616. for ($i = 0; $i < $total_formats; $i++) {
  617. $key = $this->_formats[$i]->getFontKey();
  618. if (isset($fonts[$key])) {
  619. // FONT has already been used
  620. $this->_formats[$i]->font_index = $fonts[$key];
  621. } else {
  622. // Add a new FONT record
  623. $fonts[$key] = $index;
  624. $this->_formats[$i]->font_index = $index;
  625. $index++;
  626. $font = $this->_formats[$i]->getFont();
  627. $this->_append($font);
  628. }
  629. }
  630. }
  631. /**
  632. * Store user defined numerical formats i.e. FORMAT records
  633. *
  634. * @access private
  635. */
  636. function _storeAllNumFormats()
  637. {
  638. // Leaning num_format syndrome
  639. $hash_num_formats = array();
  640. $num_formats = array();
  641. $index = 164;
  642. // Iterate through the XF objects and write a FORMAT record if it isn't a
  643. // built-in format type and if the FORMAT string hasn't already been used.
  644. $total_formats = count($this->_formats);
  645. for ($i = 0; $i < $total_formats; $i++) {
  646. $num_format = $this->_formats[$i]->_num_format;
  647. // Check if $num_format is an index to a built-in format.
  648. // Also check for a string of zeros, which is a valid format string
  649. // but would evaluate to zero.
  650. //
  651. if (!preg_match("/^0+\d/", $num_format)) {
  652. if (preg_match("/^\d+$/", $num_format)) { // built-in format
  653. continue;
  654. }
  655. }
  656. if (isset($hash_num_formats[$num_format])) {
  657. // FORMAT has already been used
  658. $this->_formats[$i]->_num_format = $hash_num_formats[$num_format];
  659. } else{
  660. // Add a new FORMAT
  661. $hash_num_formats[$num_format] = $index;
  662. $this->_formats[$i]->_num_format = $index;
  663. array_push($num_formats,$num_format);
  664. $index++;
  665. }
  666. }
  667. // Write the new FORMAT records starting from 0xA4
  668. $index = 164;
  669. foreach ($num_formats as $num_format) {
  670. $this->_storeNumFormat($num_format,$index);
  671. $index++;
  672. }
  673. }
  674. /**
  675. * Write all XF records.
  676. *
  677. * @access private
  678. */
  679. function _storeAllXfs()
  680. {
  681. // _tmp_format is added by the constructor. We use this to write the default XF's
  682. // The default font index is 0
  683. //
  684. $format = $this->_tmp_format;
  685. for ($i = 0; $i <= 14; $i++) {
  686. $xf = $format->getXf('style'); // Style XF
  687. $this->_append($xf);
  688. }
  689. $xf = $format->getXf('cell'); // Cell XF
  690. $this->_append($xf);
  691. // User defined XFs
  692. $total_formats = count($this->_formats);
  693. for ($i = 0; $i < $total_formats; $i++) {
  694. $xf = $this->_formats[$i]->getXf('cell');
  695. $this->_append($xf);
  696. }
  697. }
  698. /**
  699. * Write all STYLE records.
  700. *
  701. * @access private
  702. */
  703. function _storeAllStyles()
  704. {
  705. $this->_storeStyle();
  706. }
  707. /**
  708. * Write the EXTERNCOUNT and EXTERNSHEET records. These are used as indexes for
  709. * the NAME records.
  710. *
  711. * @access private
  712. */
  713. function _storeExterns()
  714. {
  715. // Create EXTERNCOUNT with number of worksheets
  716. $this->_storeExterncount(count($this->_worksheets));
  717. // Create EXTERNSHEET for each worksheet
  718. foreach ($this->_sheetnames as $sheetname) {
  719. $this->_storeExternsheet($sheetname);
  720. }
  721. }
  722. /**
  723. * Write the NAME record to define the print area and the repeat rows and cols.
  724. *
  725. * @access private
  726. */
  727. function _storeNames()
  728. {
  729. // Create the print area NAME records
  730. $total_worksheets = count($this->_worksheets);
  731. for ($i = 0; $i < $total_worksheets; $i++) {
  732. // Write a Name record if the print area has been defined
  733. if (isset($this->_worksheets[$i]->print_rowmin)) {
  734. $this->_storeNameShort(
  735. $this->_worksheets[$i]->index,
  736. 0x06, // NAME type
  737. $this->_worksheets[$i]->print_rowmin,
  738. $this->_worksheets[$i]->print_rowmax,
  739. $this->_worksheets[$i]->print_colmin,
  740. $this->_worksheets[$i]->print_colmax
  741. );
  742. }
  743. }
  744. // Create the print title NAME records
  745. $total_worksheets = count($this->_worksheets);
  746. for ($i = 0; $i < $total_worksheets; $i++) {
  747. $rowmin = $this->_worksheets[$i]->title_rowmin;
  748. $rowmax = $this->_worksheets[$i]->title_rowmax;
  749. $colmin = $this->_worksheets[$i]->title_colmin;
  750. $colmax = $this->_worksheets[$i]->title_colmax;
  751. // Determine if row + col, row, col or nothing has been defined
  752. // and write the appropriate record
  753. //
  754. if (isset($rowmin) && isset($colmin)) {
  755. // Row and column titles have been defined.
  756. // Row title has been defined.
  757. $this->_storeNameLong(
  758. $this->_worksheets[$i]->index,
  759. 0x07, // NAME type
  760. $rowmin,
  761. $rowmax,
  762. $colmin,
  763. $colmax
  764. );
  765. } elseif (isset($rowmin)) {
  766. // Row title has been defined.
  767. $this->_storeNameShort(
  768. $this->_worksheets[$i]->index,
  769. 0x07, // NAME type
  770. $rowmin,
  771. $rowmax,
  772. 0x00,
  773. 0xff
  774. );
  775. } elseif (isset($colmin)) {
  776. // Column title has been defined.
  777. $this->_storeNameShort(
  778. $this->_worksheets[$i]->index,
  779. 0x07, // NAME type
  780. 0x0000,
  781. 0x3fff,
  782. $colmin,
  783. $colmax
  784. );
  785. } else {
  786. // Print title hasn't been defined.
  787. }
  788. }
  789. }
  790. /******************************************************************************
  791. *
  792. * BIFF RECORDS
  793. *
  794. */
  795. /**
  796. * Stores the CODEPAGE biff record.
  797. *
  798. * @access private
  799. */
  800. function _storeCodepage()
  801. {
  802. $record = 0x0042; // Record identifier
  803. $length = 0x0002; // Number of bytes to follow
  804. $cv = $this->_codepage; // The code page
  805. $header = pack('vv', $record, $length);
  806. $data = pack('v', $cv);
  807. $this->_append($header . $data);
  808. }
  809. /**
  810. * Write Excel BIFF WINDOW1 record.
  811. *
  812. * @access private
  813. */
  814. function _storeWindow1()
  815. {
  816. $record = 0x003D; // Record identifier
  817. $length = 0x0012; // Number of bytes to follow
  818. $xWn = 0x0000; // Horizontal position of window
  819. $yWn = 0x0000; // Vertical position of window
  820. $dxWn = 0x25BC; // Width of window
  821. $dyWn = 0x1572; // Height of window
  822. $grbit = 0x0038; // Option flags
  823. $ctabsel = $this->_selected; // Number of workbook tabs selected
  824. $wTabRatio = 0x0258; // Tab to scrollbar ratio
  825. $itabFirst = $this->_firstsheet; // 1st displayed worksheet
  826. $itabCur = $this->_activesheet; // Active worksheet
  827. $header = pack("vv", $record, $length);
  828. $data = pack("vvvvvvvvv", $xWn, $yWn, $dxWn, $dyWn,
  829. $grbit,
  830. $itabCur, $itabFirst,
  831. $ctabsel, $wTabRatio);
  832. $this->_append($header . $data);
  833. }
  834. /**
  835. * Writes Excel BIFF BOUNDSHEET record.
  836. * FIXME: inconsistent with BIFF documentation
  837. *
  838. * @param string $sheetname Worksheet name
  839. * @param integer $offset Location of worksheet BOF
  840. * @access private
  841. */
  842. function _storeBoundsheet($sheetname,$offset)
  843. {
  844. $record = 0x0085; // Record identifier
  845. if ($this->_BIFF_version == 0x0600) {
  846. $length = 0x08 + strlen($sheetname); // Number of bytes to follow
  847. } else {
  848. $length = 0x07 + strlen($sheetname); // Number of bytes to follow
  849. }
  850. $grbit = 0x0000; // Visibility and sheet type
  851. $cch = strlen($sheetname); // Length of sheet name
  852. $header = pack("vv", $record, $length);
  853. if ($this->_BIFF_version == 0x0600) {
  854. $data = pack("Vvv", $offset, $grbit, $cch);
  855. } else {
  856. $data = pack("VvC", $offset, $grbit, $cch);
  857. }
  858. $this->_append($header.$data.$sheetname);
  859. }
  860. /**
  861. * Write Internal SUPBOOK record
  862. *
  863. * @access private
  864. */
  865. function _storeSupbookInternal()
  866. {
  867. $record = 0x01AE; // Record identifier
  868. $length = 0x0004; // Bytes to follow
  869. $header = pack("vv", $record, $length);
  870. $data = pack("vv", count($this->_worksheets), 0x0104);
  871. $this->_append($header . $data);
  872. }
  873. /**
  874. * Writes the Excel BIFF EXTERNSHEET record. These references are used by
  875. * formulas.
  876. *
  877. * @param string $sheetname Worksheet name
  878. * @access private
  879. */
  880. function _storeExternsheetBiff8()
  881. {
  882. $total_references = count($this->_parser->_references);
  883. $record = 0x0017; // Record identifier
  884. $length = 2 + 6 * $total_references; // Number of bytes to follow
  885. $supbook_index = 0; // FIXME: only using internal SUPBOOK record
  886. $header = pack("vv", $record, $length);
  887. $data = pack('v', $total_references);
  888. for ($i = 0; $i < $total_references; $i++) {
  889. $data .= $this->_parser->_references[$i];
  890. }
  891. $this->_append($header . $data);
  892. }
  893. /**
  894. * Write Excel BIFF STYLE records.
  895. *
  896. * @access private
  897. */
  898. function _storeStyle()
  899. {
  900. $record = 0x0293; // Record identifier
  901. $length = 0x0004; // Bytes to follow
  902. $ixfe = 0x8000; // Index to style XF
  903. $BuiltIn = 0x00; // Built-in style
  904. $iLevel = 0xff; // Outline style level
  905. $header = pack("vv", $record, $length);
  906. $data = pack("vCC", $ixfe, $BuiltIn, $iLevel);
  907. $this->_append($header . $data);
  908. }
  909. /**
  910. * Writes Excel FORMAT record for non "built-in" numerical formats.
  911. *
  912. * @param string $format Custom format string
  913. * @param integer $ifmt Format index code
  914. * @access private
  915. */
  916. function _storeNumFormat($format, $ifmt)
  917. {
  918. $record = 0x041E; // Record identifier
  919. if ($this->_BIFF_version == 0x0600) {
  920. $length = 5 + strlen($format); // Number of bytes to follow
  921. $encoding = 0x0;
  922. } elseif ($this->_BIFF_version == 0x0500) {
  923. $length = 3 + strlen($format); // Number of bytes to follow
  924. }
  925. $cch = strlen($format); // Length of format string
  926. $header = pack("vv", $record, $length);
  927. if ($this->_BIFF_version == 0x0600) {
  928. $data = pack("vvC", $ifmt, $cch, $encoding);
  929. } elseif ($this->_BIFF_version == 0x0500) {
  930. $data = pack("vC", $ifmt, $cch);
  931. }
  932. $this->_append($header . $data . $format);
  933. }
  934. /**
  935. * Write DATEMODE record to indicate the date system in use (1904 or 1900).
  936. *
  937. * @access private
  938. */
  939. function _storeDatemode()
  940. {
  941. $record = 0x0022; // Record identifier
  942. $length = 0x0002; // Bytes to follow
  943. $f1904 = $this->_1904; // Flag for 1904 date system
  944. $header = pack("vv", $record, $length);
  945. $data = pack("v", $f1904);
  946. $this->_append($header . $data);
  947. }
  948. /**
  949. * Write BIFF record EXTERNCOUNT to indicate the number of external sheet
  950. * references in the workbook.
  951. *
  952. * Excel only stores references to external sheets that are used in NAME.
  953. * The workbook NAME record is required to define the print area and the repeat
  954. * rows and columns.
  955. *
  956. * A similar method is used in Worksheet.php for a slightly different purpose.
  957. *
  958. * @param integer $cxals Number of external references
  959. * @access private
  960. */
  961. function _storeExterncount($cxals)
  962. {
  963. $record = 0x0016; // Record identifier
  964. $length = 0x0002; // Number of bytes to follow
  965. $header = pack("vv", $record, $length);
  966. $data = pack("v", $cxals);
  967. $this->_append($header . $data);
  968. }
  969. /**
  970. * Writes the Excel BIFF EXTERNSHEET record. These references are used by
  971. * formulas. NAME record is required to define the print area and the repeat
  972. * rows and columns.
  973. *
  974. * A similar method is used in Worksheet.php for a slightly different purpose.
  975. *
  976. * @param string $sheetname Worksheet name
  977. * @access private
  978. */
  979. function _storeExternsheet($sheetname)
  980. {
  981. $record = 0x0017; // Record identifier
  982. $length = 0x02 + strlen($sheetname); // Number of bytes to follow
  983. $cch = strlen($sheetname); // Length of sheet name
  984. $rgch = 0x03; // Filename encoding
  985. $header = pack("vv", $record, $length);
  986. $data = pack("CC", $cch, $rgch);
  987. $this->_append($header . $data . $sheetname);
  988. }
  989. /**
  990. * Store the NAME record in the short format that is used for storing the print
  991. * area, repeat rows only and repeat columns only.
  992. *
  993. * @param integer $index Sheet index
  994. * @param integer $type Built-in name type
  995. * @param integer $rowmin Start row
  996. * @param integer $rowmax End row
  997. * @param integer $colmin Start colum
  998. * @param integer $colmax End column
  999. * @access private
  1000. */
  1001. function _storeNameShort($index, $type, $rowmin, $rowmax, $colmin, $colmax)
  1002. {
  1003. $record = 0x0018; // Record identifier
  1004. $length = 0x0024; // Number of bytes to follow
  1005. $grbit = 0x0020; // Option flags
  1006. $chKey = 0x00; // Keyboard shortcut
  1007. $cch = 0x01; // Length of text name
  1008. $cce = 0x0015; // Length of text definition
  1009. $ixals = $index + 1; // Sheet index
  1010. $itab = $ixals; // Equal to ixals
  1011. $cchCustMenu = 0x00; // Length of cust menu text
  1012. $cchDescription = 0x00; // Length of description text
  1013. $cchHelptopic = 0x00; // Length of help topic text
  1014. $cchStatustext = 0x00; // Length of status bar text
  1015. $rgch = $type; // Built-in name type
  1016. $unknown03 = 0x3b;
  1017. $unknown04 = 0xffff-$index;
  1018. $unknown05 = 0x0000;
  1019. $unknown06 = 0x0000;
  1020. $unknown07 = 0x1087;
  1021. $unknown08 = 0x8005;
  1022. $header = pack("vv", $record, $length);
  1023. $data = pack("v", $grbit);
  1024. $data .= pack("C", $chKey);
  1025. $data .= pack("C", $cch);
  1026. $data .= pack("v", $cce);
  1027. $data .= pack("v", $ixals);
  1028. $data .= pack("v", $itab);
  1029. $data .= pack("C", $cchCustMenu);
  1030. $data .= pack("C", $cchDescription);
  1031. $data .= pack("C", $cchHelptopic);
  1032. $data .= pack("C", $cchStatustext);
  1033. $data .= pack("C", $rgch);
  1034. $data .= pack("C", $unknown03);
  1035. $data .= pack("v", $unknown04);
  1036. $data .= pack("v", $unknown05);
  1037. $data .= pack("v", $unknown06);
  1038. $data .= pack("v", $unknown07);
  1039. $data .= pack("v", $unknown08);
  1040. $data .= pack("v", $index);
  1041. $data .= pack("v", $index);
  1042. $data .= pack("v", $rowmin);
  1043. $data .= pack("v", $rowmax);
  1044. $data .= pack("C", $colmin);
  1045. $data .= pack("C", $colmax);
  1046. $this->_append($header . $data);
  1047. }
  1048. /**
  1049. * Store the NAME record in the long format that is used for storing the repeat
  1050. * rows and columns when both are specified. This shares a lot of code with
  1051. * _storeNameShort() but we use a separate method to keep the code clean.
  1052. * Code abstraction for reuse can be carried too far, and I should know. ;-)
  1053. *
  1054. * @param integer $index Sheet index
  1055. * @param integer $type Built-in name type
  1056. * @param integer $rowmin Start row
  1057. * @param integer $rowmax End row
  1058. * @param integer $colmin Start colum
  1059. * @param integer $colmax End column
  1060. * @access private
  1061. */
  1062. function _storeNameLong($index, $type, $rowmin, $rowmax, $colmin, $colmax)
  1063. {
  1064. $record = 0x0018; // Record identifier
  1065. $length = 0x003d; // Number of bytes to follow
  1066. $grbit = 0x0020; // Option flags
  1067. $chKey = 0x00; // Keyboard shortcut
  1068. $cch = 0x01; // Length of text name
  1069. $cce = 0x002e; // Length of text definition
  1070. $ixals = $index + 1; // Sheet index
  1071. $itab = $ixals; // Equal to ixals
  1072. $cchCustMenu = 0x00; // Length of cust menu text
  1073. $cchDescription = 0x00; // Length of description text
  1074. $cchHelptopic = 0x00; // Length of help topic text
  1075. $cchStatustext = 0x00; // Length of status bar text
  1076. $rgch = $type; // Built-in name type
  1077. $unknown01 = 0x29;
  1078. $unknown02 = 0x002b;
  1079. $unknown03 = 0x3b;
  1080. $unknown04 = 0xffff-$index;
  1081. $unknown05 = 0x0000;
  1082. $unknown06 = 0x0000;
  1083. $unknown07 = 0x1087;
  1084. $unknown08 = 0x8008;
  1085. $header = pack("vv", $record, $length);
  1086. $data = pack("v", $grbit);
  1087. $data .= pack("C", $chKey);
  1088. $data .= pack("C", $cch);
  1089. $data .= pack("v", $cce);
  1090. $data .= pack("v", $ixals);
  1091. $data .= pack("v", $itab);
  1092. $data .= pack("C", $cchCustMenu);
  1093. $data .= pack("C", $cchDescription);
  1094. $data .= pack("C", $cchHelptopic);
  1095. $data .= pack("C", $cchStatustext);
  1096. $data .= pack("C", $rgch);
  1097. $data .= pack("C", $unknown01);
  1098. $data .= pack("v", $unknown02);
  1099. // Column definition
  1100. $data .= pack("C", $unknown03);
  1101. $data .= pack("v", $unknown04);
  1102. $data .= pack("v", $unknown05);
  1103. $data .= pack("v", $unknown06);
  1104. $data .= pack("v", $unknown07);
  1105. $data .= pack("v", $unknown08);
  1106. $data .= pack("v", $index);
  1107. $data .= pack("v", $index);
  1108. $data .= pack("v", 0x0000);
  1109. $data .= pack("v", 0x3fff);
  1110. $data .= pack("C", $colmin);
  1111. $data .= pack("C", $colmax);
  1112. // Row definition
  1113. $data .= pack("C", $unknown03);
  1114. $data .= pack("v", $unknown04);
  1115. $data .= pack("v", $unknown05);
  1116. $data .= pack("v", $unknown06);
  1117. $data .= pack("v", $unknown07);
  1118. $data .= pack("v", $unknown08);
  1119. $data .= pack("v", $index);
  1120. $data .= pack("v", $index);
  1121. $data .= pack("v", $rowmin);
  1122. $data .= pack("v", $rowmax);
  1123. $data .= pack("C", 0x00);
  1124. $data .= pack("C", 0xff);
  1125. // End of data
  1126. $data .= pack("C", 0x10);
  1127. $this->_append($header . $data);
  1128. }
  1129. /**
  1130. * Stores the COUNTRY record for localization
  1131. *
  1132. * @access private
  1133. */
  1134. function _storeCountry()
  1135. {
  1136. $record = 0x008C; // Record identifier
  1137. $length = 4; // Number of bytes to follow
  1138. $header = pack('vv', $record, $length);
  1139. /* using the same country code always for simplicity */
  1140. $data = pack('vv', $this->_country_code, $this->_country_code);
  1141. $this->_append($header . $data);
  1142. }
  1143. /**
  1144. * Stores the PALETTE biff record.
  1145. *
  1146. * @access private
  1147. */
  1148. function _storePalette()
  1149. {
  1150. $aref = $this->_palette;
  1151. $record = 0x0092; // Record identifier
  1152. $length = 2 + 4 * count($aref); // Number of bytes to follow
  1153. $ccv = count($aref); // Number of RGB values to follow
  1154. $data = ''; // The RGB data
  1155. // Pack the RGB data
  1156. foreach ($aref as $color) {
  1157. foreach ($color as $byte) {
  1158. $data .= pack("C",$byte);
  1159. }
  1160. }
  1161. $header = pack("vvv", $record, $length, $ccv);
  1162. $this->_append($header . $data);
  1163. }
  1164. /**
  1165. * Calculate
  1166. * Handling of the SST continue blocks is complicated by the need to include an
  1167. * additional continuation byte depending on whether the string is split between
  1168. * blocks or whether it starts at the beginning of the block. (There are also
  1169. * additional complications that will arise later when/if Rich Strings are
  1170. * supported).
  1171. *
  1172. * @access private
  1173. */
  1174. function _calculateSharedStringsSizes()
  1175. {
  1176. /* Iterate through the strings to calculate the CONTINUE block sizes.
  1177. For simplicity we use the same size for the SST and CONTINUE records:
  1178. 8228 : Maximum Excel97 block size
  1179. -4 : Length of block header
  1180. -8 : Length of additional SST header information
  1181. -8 : Arbitrary number to keep within _add_continue() limit
  1182. = 8208
  1183. */
  1184. $continue_limit = 8208;
  1185. $block_length = 0;
  1186. $written = 0;
  1187. $this->_block_sizes = array();
  1188. $continue = 0;
  1189. foreach (array_keys($this->_str_table) as $string) {
  1190. $string_length = strlen($string);
  1191. $headerinfo = unpack("vlength/Cencoding", $string);
  1192. $encoding = $headerinfo["encoding"];
  1193. $split_string = 0;
  1194. // Block length is the total length of the strings that will be
  1195. // written out in a single SST or CONTINUE block.
  1196. $block_length += $string_length;
  1197. // We can write the string if it doesn't cross a CONTINUE boundary
  1198. if ($block_length < $continue_limit) {
  1199. $written += $string_length;
  1200. continue;
  1201. }
  1202. // Deal with the cases where the next string to be written will exceed
  1203. // the CONTINUE boundary. If the string is very long it may need to be
  1204. // written in more than one CONTINUE record.
  1205. while ($block_length >= $continue_limit) {
  1206. // We need to avoid the case where a string is continued in the first
  1207. // n bytes that contain the string header information.
  1208. $header_length = 3; // Min string + header size -1
  1209. $space_remaining = $continue_limit - $written - $continue;
  1210. /* TODO: Unicode data should only be split on char (2 byte)
  1211. boundaries. Therefore, in some cases we need to reduce the
  1212. amount of available
  1213. */
  1214. $align = 0;
  1215. # Only applies to Unicode strings
  1216. if ($encoding == 1) {
  1217. # Min string + header size -1
  1218. $header_length = 4;
  1219. if ($space_remaining > $header_length) {
  1220. # String contains 3 byte header => split on odd boundary
  1221. if (!$split_string && $space_remaining % 2 != 1) {
  1222. $space_remaining--;
  1223. $align = 1;
  1224. }
  1225. # Split section without header => split on even boundary
  1226. else if ($split_string && $space_remaining % 2 == 1) {
  1227. $space_remaining--;
  1228. $align = 1;
  1229. }
  1230. $split_string = 1;
  1231. }
  1232. }
  1233. if ($space_remaining > $header_length) {
  1234. // Write as much as possible of the string in the current block
  1235. $written += $space_remaining;
  1236. // Reduce the current block length by the amount written
  1237. $block_length -= $continue_limit - $continue - $align;
  1238. // Store the max size for this block
  1239. $this->_block_sizes[] = $continue_limit - $align;
  1240. // If the current string was split then the next CONTINUE block
  1241. // should have the string continue flag (grbit) set unless the
  1242. // split string fits exactly into the remaining space.
  1243. if ($block_length > 0) {
  1244. $continue = 1;
  1245. } else {
  1246. $continue = 0;
  1247. }
  1248. } else {
  1249. // Store the max size for this block
  1250. $this->_block_sizes[] = $written + $continue;
  1251. // Not enough space to start the string in the current block
  1252. $block_length -= $continue_limit - $space_remaining - $continue;
  1253. $continue = 0;
  1254. }
  1255. // If the string (or substr) is small enough we can write it in the
  1256. // new CONTINUE block. Else, go through the loop again to write it in
  1257. // one or more CONTINUE blocks
  1258. if ($block_length < $continue_limit) {
  1259. $written = $block_length;
  1260. } else {
  1261. $written = 0;
  1262. }
  1263. }
  1264. }
  1265. // Store the max size for the last block unless it is empty
  1266. if ($written + $continue) {
  1267. $this->_block_sizes[] = $written + $continue;
  1268. }
  1269. /* Calculate the total length of the SST and associated CONTINUEs (if any).
  1270. The SST record will have a length even if it contains no strings.
  1271. This length is required to set the offsets in the BOUNDSHEET records since
  1272. they must be written before the SST records
  1273. */
  1274. $tmp_block_sizes = array();
  1275. $tmp_block_sizes = $this->_block_sizes;
  1276. $length = 12;
  1277. if (!empty($tmp_block_sizes)) {
  1278. $length += array_shift($tmp_block_sizes); # SST
  1279. }
  1280. while (!empty($tmp_block_sizes)) {
  1281. $length += 4

Large files files are truncated, but you can click here to view the full file