PageRenderTime 58ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/pear/Spreadsheet/Excel/Writer/Workbook.php

http://github.com/moodle/moodle
PHP | 1616 lines | 868 code | 188 blank | 560 comment | 122 complexity | 23e9ff8f67abea0f10a6c8f50a1e3a78 MD5 | raw file
Possible License(s): MIT, AGPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, Apache-2.0, LGPL-2.1, BSD-3-Clause

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

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