PageRenderTime 65ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/branches/v1.6.5/Classes/PHPExcel/Writer/Excel5/Workbook.php

#
PHP | 1734 lines | 927 code | 216 blank | 591 comment | 131 complexity | 4504ed90e17a46816ad2b8a54e48db81 MD5 | raw file
Possible License(s): AGPL-1.0, LGPL-2.0, LGPL-2.1, 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. * PHPExcel_Writer_Excel5_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 'PHPExcel/Writer/Excel5/Format.php';
  35. require_once 'PHPExcel/Writer/Excel5/BIFFwriter.php';
  36. require_once 'PHPExcel/Writer/Excel5/Worksheet.php';
  37. require_once 'PHPExcel/Writer/Excel5/Parser.php';
  38. require_once 'PHPExcel/Shared/Date.php';
  39. require_once 'PHPExcel/Shared/OLE/OLE_Root.php';
  40. require_once 'PHPExcel/Shared/OLE/OLE_File.php';
  41. require_once 'PHPExcel/Shared/String.php';
  42. /**
  43. * Class for generating Excel Spreadsheets
  44. *
  45. * @author Xavier Noguer <xnoguer@rezebra.com>
  46. * @category PHPExcel
  47. * @package PHPExcel_Writer_Excel5
  48. */
  49. class PHPExcel_Writer_Excel5_Workbook extends PHPExcel_Writer_Excel5_BIFFwriter
  50. {
  51. /**
  52. * Filename for the Workbook
  53. * @var string
  54. */
  55. var $_filename;
  56. /**
  57. * Formula parser
  58. * @var object Parser
  59. */
  60. var $_parser;
  61. /**
  62. * The active worksheet of the workbook (0 indexed)
  63. * @var integer
  64. */
  65. var $_activesheet;
  66. /**
  67. * 1st displayed worksheet in the workbook (0 indexed)
  68. * @var integer
  69. */
  70. var $_firstsheet;
  71. /**
  72. * Number of workbook tabs selected
  73. * @var integer
  74. */
  75. var $_selected;
  76. /**
  77. * Index for creating adding new formats to the workbook
  78. * @var integer
  79. */
  80. var $_xf_index;
  81. /**
  82. * Flag for preventing close from being called twice.
  83. * @var integer
  84. * @see close()
  85. */
  86. var $_fileclosed;
  87. /**
  88. * The BIFF file size for the workbook.
  89. * @var integer
  90. * @see _calcSheetOffsets()
  91. */
  92. var $_biffsize;
  93. /**
  94. * The default sheetname for all sheets created.
  95. * @var string
  96. */
  97. var $_sheetname;
  98. /**
  99. * The default XF format.
  100. * @var object Format
  101. */
  102. var $_tmp_format;
  103. /**
  104. * Array containing references to all of this workbook's worksheets
  105. * @var array
  106. */
  107. var $_worksheets;
  108. /**
  109. * Array of sheetnames for creating the EXTERNSHEET records
  110. * @var array
  111. */
  112. var $_sheetnames;
  113. /**
  114. * Array containing references to all of this workbook's formats
  115. * @var array
  116. */
  117. var $_formats;
  118. /**
  119. * Array containing the colour palette
  120. * @var array
  121. */
  122. var $_palette;
  123. /**
  124. * The default format for URLs.
  125. * @var object Format
  126. */
  127. var $_url_format;
  128. /**
  129. * The codepage indicates the text encoding used for strings
  130. * @var integer
  131. */
  132. var $_codepage;
  133. /**
  134. * The country code used for localization
  135. * @var integer
  136. */
  137. var $_country_code;
  138. /**
  139. * The temporary dir for storing the OLE file
  140. * @var string
  141. */
  142. var $_tmp_dir;
  143. /**
  144. * number of bytes for sizeinfo of strings
  145. * @var integer
  146. */
  147. var $_string_sizeinfo_size;
  148. /**
  149. * Workbook
  150. * @var PHPExcel
  151. */
  152. private $_phpExcel;
  153. /**
  154. * Class constructor
  155. *
  156. * @param string filename for storing the workbook. "-" for writing to stdout.
  157. * @param PHPExcel $phpExcel The Workbook
  158. * @access public
  159. */
  160. function PHPExcel_Writer_Excel5_Workbook($filename, $phpExcel)
  161. {
  162. // It needs to call its parent's constructor explicitly
  163. $this->PHPExcel_Writer_Excel5_BIFFwriter();
  164. $this->_filename = $filename;
  165. $this->_parser = new PHPExcel_Writer_Excel5_Parser($this->_byte_order, $this->_BIFF_version);
  166. $this->_activesheet = 0;
  167. $this->_firstsheet = 0;
  168. $this->_selected = 0;
  169. $this->_xf_index = 16; // 15 style XF's and 1 cell XF.
  170. $this->_fileclosed = 0;
  171. $this->_biffsize = 0;
  172. $this->_sheetname = 'Sheet';
  173. $this->_tmp_format = new PHPExcel_Writer_Excel5_Format($this->_BIFF_version);
  174. $this->_tmp_format->setFontFamily($phpExcel->getSheet(0)->getDefaultStyle()->getFont()->getName());
  175. $this->_tmp_format->setSize($phpExcel->getSheet(0)->getDefaultStyle()->getFont()->getSize());
  176. $this->_worksheets = array();
  177. $this->_sheetnames = array();
  178. $this->_formats = array();
  179. $this->_palette = array();
  180. $this->_codepage = 0x04E4; // FIXME: should change for BIFF8
  181. $this->_country_code = -1;
  182. $this->_string_sizeinfo = 3;
  183. // Add the default format for hyperlinks
  184. $this->_url_format =& $this->addFormat(array('color' => 'blue', 'underline' => 1));
  185. $this->_str_total = 0;
  186. $this->_str_unique = 0;
  187. $this->_str_table = array();
  188. $this->_setPaletteXl97();
  189. $this->_tmp_dir = '';
  190. $this->_phpExcel = $phpExcel;
  191. }
  192. /**
  193. * Calls finalization methods.
  194. * This method should always be the last one to be called on every workbook
  195. *
  196. * @access public
  197. * @return mixed true on success
  198. */
  199. function close()
  200. {
  201. if ($this->_fileclosed) { // Prevent close() from being called twice.
  202. return true;
  203. }
  204. $res = $this->_storeWorkbook();
  205. foreach ($this->_worksheets as $sheet) {
  206. $sheet->cleanup();
  207. }
  208. $this->_fileclosed = 1;
  209. return true;
  210. }
  211. /**
  212. * An accessor for the _worksheets[] array
  213. * Returns an array of the worksheet objects in a workbook
  214. * It actually calls to worksheets()
  215. *
  216. * @access public
  217. * @see worksheets()
  218. * @return array
  219. */
  220. function sheets()
  221. {
  222. return $this->worksheets();
  223. }
  224. /**
  225. * An accessor for the _worksheets[] array.
  226. * Returns an array of the worksheet objects in a workbook
  227. *
  228. * @access public
  229. * @return array
  230. */
  231. function worksheets()
  232. {
  233. return $this->_worksheets;
  234. }
  235. /**
  236. * Sets the BIFF version.
  237. * This method exists just to access experimental functionality
  238. * from BIFF8. It will be deprecated !
  239. * Only possible value is 8 (Excel 97/2000).
  240. * For any other value it fails silently.
  241. *
  242. * @access public
  243. * @param integer $version The BIFF version
  244. */
  245. function setVersion($version)
  246. {
  247. if ($version == 8) { // only accept version 8
  248. $version = 0x0600;
  249. $this->_BIFF_version = $version;
  250. // change BIFFwriter limit for CONTINUE records
  251. $this->_limit = 8228;
  252. $this->_tmp_format->_BIFF_version = $version;
  253. $this->_url_format->_BIFF_version = $version;
  254. $this->_parser->_BIFF_version = $version;
  255. $this->_codepage = 0x04B0;
  256. $total_worksheets = count($this->_worksheets);
  257. // change version for all worksheets too
  258. for ($i = 0; $i < $total_worksheets; ++$i) {
  259. $this->_worksheets[$i]->_BIFF_version = $version;
  260. }
  261. $total_formats = count($this->_formats);
  262. // change version for all formats too
  263. for ($i = 0; $i < $total_formats; ++$i) {
  264. $this->_formats[$i]->_BIFF_version = $version;
  265. }
  266. }
  267. }
  268. /**
  269. * Set the country identifier for the workbook
  270. *
  271. * @access public
  272. * @param integer $code Is the international calling country code for the
  273. * chosen country.
  274. */
  275. function setCountry($code)
  276. {
  277. $this->_country_code = $code;
  278. }
  279. /**
  280. * Add a new worksheet to the Excel workbook.
  281. * If no name is given the name of the worksheet will be Sheeti$i, with
  282. * $i in [1..].
  283. *
  284. * @access public
  285. * @param string $name the optional name of the worksheet
  286. * @param PHPExcel_Worksheet $phpSheet
  287. * @return mixed reference to a worksheet object on success
  288. */
  289. function &addWorksheet($name = '', $phpSheet = null)
  290. {
  291. $index = count($this->_worksheets);
  292. $sheetname = $this->_sheetname;
  293. if ($name == '') {
  294. $name = $sheetname.($index+1);
  295. }
  296. // Check that sheetname is <= 31 chars (Excel limit before BIFF8).
  297. if ($this->_BIFF_version != 0x0600)
  298. {
  299. if (strlen($name) > 31) {
  300. throw new Exception("Sheetname $name must be <= 31 chars");
  301. }
  302. }
  303. // Check that the worksheet name doesn't already exist: a fatal Excel error.
  304. $total_worksheets = count($this->_worksheets);
  305. for ($i = 0; $i < $total_worksheets; ++$i) {
  306. if ($this->_worksheets[$i]->getName() == $name) {
  307. throw new Exception("Worksheet '$name' already exists");
  308. }
  309. }
  310. $worksheet = new PHPExcel_Writer_Excel5_Worksheet($this->_BIFF_version,
  311. $name, $index,
  312. $this->_activesheet, $this->_firstsheet,
  313. $this->_str_total, $this->_str_unique,
  314. $this->_str_table, $this->_url_format,
  315. $this->_parser, $this->_tmp_dir,
  316. $phpSheet);
  317. $this->_worksheets[$index] = &$worksheet; // Store ref for iterator
  318. $this->_sheetnames[$index] = $name; // Store EXTERNSHEET names
  319. $this->_parser->setExtSheet($name, $index); // Register worksheet name with parser
  320. // for BIFF8
  321. if ($this->_BIFF_version == 0x0600) {
  322. $supbook_index = 0x00;
  323. $ref = pack('vvv', $supbook_index, $total_worksheets, $total_worksheets);
  324. $this->_parser->_references[] = $ref; // Register reference with parser
  325. }
  326. return $worksheet;
  327. }
  328. /**
  329. * Add a new format to the Excel workbook.
  330. * Also, pass any properties to the Format constructor.
  331. *
  332. * @access public
  333. * @param array $properties array with properties for initializing the format.
  334. * @return &PHPExcel_Writer_Excel5_Format reference to an Excel Format
  335. */
  336. function &addFormat($properties = array())
  337. {
  338. $format = new PHPExcel_Writer_Excel5_Format($this->_BIFF_version, $this->_xf_index, $properties);
  339. $this->_xf_index += 1;
  340. $this->_formats[] = &$format;
  341. return $format;
  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. throw new Exception("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. throw new Exception("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
  448. */
  449. function _storeWorkbook()
  450. {
  451. if (count($this->_worksheets) == 0) {
  452. return true;
  453. }
  454. // Ensure that at least one worksheet has been selected.
  455. if ($this->_activesheet == 0) {
  456. $this->_worksheets[0]->selected = 1;
  457. }
  458. // Calculate the number of selected worksheet tabs and call the finalization
  459. // methods for each worksheet
  460. $total_worksheets = count($this->_worksheets);
  461. for ($i = 0; $i < $total_worksheets; ++$i) {
  462. if ($this->_worksheets[$i]->selected) {
  463. $this->_selected++;
  464. }
  465. $this->_worksheets[$i]->close($this->_sheetnames);
  466. }
  467. // Add part 1 of the Workbook globals, what goes before the SHEET records
  468. $this->_storeBof(0x0005);
  469. $this->_storeCodepage();
  470. if ($this->_BIFF_version == 0x0600) {
  471. $this->_storeWindow1();
  472. }
  473. if ($this->_BIFF_version == 0x0500) {
  474. $this->_storeExterns(); // For print area and repeat rows
  475. $this->_storeNames(); // For print area and repeat rows
  476. }
  477. if ($this->_BIFF_version == 0x0500) {
  478. $this->_storeWindow1();
  479. }
  480. $this->_storeDatemode();
  481. $this->_storeAllFonts();
  482. $this->_storeAllNumFormats();
  483. $this->_storeAllXfs();
  484. $this->_storeAllStyles();
  485. $this->_storePalette();
  486. $this->_calculateSharedStringsSizes();
  487. // Prepare part 3 of the workbook global stream, what goes after the SHEET records
  488. $part3 = '';
  489. if ($this->_country_code != -1) {
  490. $part3 .= $this->writeCountry();
  491. }
  492. if ($this->_BIFF_version == 0x0600) {
  493. $part3 .= $this->writeSupbookInternal();
  494. /* TODO: store external SUPBOOK records and XCT and CRN records
  495. in case of external references for BIFF8 */
  496. $part3 .= $this->writeExternsheetBiff8();
  497. $part3 .= $this->writeAllDefinedNamesBiff8();
  498. $part3 .= $this->writeSharedStringsTable();
  499. }
  500. $part3 .= $this->writeEof();
  501. // Add part 2 of the Workbook globals, the SHEET records
  502. $this->_calcSheetOffsets();
  503. for ($i = 0; $i < $total_worksheets; ++$i) {
  504. $this->_storeBoundsheet($this->_worksheets[$i]->name,$this->_worksheets[$i]->offset);
  505. }
  506. // Add part 3 of the Workbook globals
  507. $this->_data .= $part3;
  508. // Store the workbook in an OLE container
  509. $res = $this->_storeOLEFile();
  510. return true;
  511. }
  512. /**
  513. * Sets the temp dir used for storing the OLE file
  514. *
  515. * @access public
  516. * @param string $dir The dir to be used as temp dir
  517. * @return true if given dir is valid, false otherwise
  518. */
  519. function setTempDir($dir)
  520. {
  521. if (is_dir($dir)) {
  522. $this->_tmp_dir = $dir;
  523. return true;
  524. }
  525. return false;
  526. }
  527. /**
  528. * Store the workbook in an OLE container
  529. *
  530. * @access private
  531. * @return mixed true on success
  532. */
  533. function _storeOLEFile()
  534. {
  535. $OLE = new PHPExcel_Shared_OLE_PPS_File(PHPExcel_Shared_OLE::Asc2Ucs('Book'));
  536. if ($this->_tmp_dir != '') {
  537. $OLE->setTempDir($this->_tmp_dir);
  538. }
  539. $res = $OLE->init();
  540. $OLE->append($this->_data);
  541. $total_worksheets = count($this->_worksheets);
  542. for ($i = 0; $i < $total_worksheets; ++$i) {
  543. while ($tmp = $this->_worksheets[$i]->getData()) {
  544. $OLE->append($tmp);
  545. }
  546. }
  547. $root = new PHPExcel_Shared_OLE_PPS_Root(time(), time(), array($OLE));
  548. if ($this->_tmp_dir != '') {
  549. $root->setTempDir($this->_tmp_dir);
  550. }
  551. $res = $root->save($this->_filename);
  552. return true;
  553. }
  554. /**
  555. * Calculate offsets for Worksheet BOF records.
  556. *
  557. * @access private
  558. */
  559. function _calcSheetOffsets()
  560. {
  561. if ($this->_BIFF_version == 0x0600) {
  562. $boundsheet_length = 12; // fixed length for a BOUNDSHEET record
  563. } else {
  564. $boundsheet_length = 11;
  565. }
  566. // size of Workbook globals part 1 + 3
  567. $offset = $this->_datasize;
  568. // add size of Workbook globals part 2, the length of the SHEET records
  569. $total_worksheets = count($this->_worksheets);
  570. for ($i = 0; $i < $total_worksheets; ++$i) {
  571. if ($this->_BIFF_version == 0x0600) {
  572. if (function_exists('mb_strlen') and function_exists('mb_convert_encoding')) {
  573. // sheet name is stored in uncompressed notation
  574. $offset += $boundsheet_length + 2 * mb_strlen($this->_worksheets[$i]->name, 'UTF-8');
  575. } else {
  576. // sheet name is stored in compressed notation, and ASCII is assumed
  577. $offset += $boundsheet_length + strlen($this->_worksheets[$i]->name);
  578. }
  579. } else {
  580. $offset += $boundsheet_length + strlen($this->_worksheets[$i]->name);
  581. }
  582. }
  583. // add the sizes of each of the Sheet substreams, respectively
  584. for ($i = 0; $i < $total_worksheets; ++$i) {
  585. $this->_worksheets[$i]->offset = $offset;
  586. $offset += $this->_worksheets[$i]->_datasize;
  587. }
  588. $this->_biffsize = $offset;
  589. }
  590. /**
  591. * Store the Excel FONT records.
  592. *
  593. * @access private
  594. */
  595. function _storeAllFonts()
  596. {
  597. // tmp_format is added by the constructor. We use this to write the default XF's
  598. $format = $this->_tmp_format;
  599. $font = $format->getFont();
  600. // Note: Fonts are 0-indexed. According to the SDK there is no index 4,
  601. // so the following fonts are 0, 1, 2, 3, 5
  602. //
  603. for ($i = 1; $i <= 5; ++$i){
  604. $this->_append($font);
  605. }
  606. // Iterate through the XF objects and write a FONT record if it isn't the
  607. // same as the default FONT and if it hasn't already been used.
  608. //
  609. $fonts = array();
  610. $index = 6; // The first user defined FONT
  611. $key = $format->getFontKey(); // The default font from _tmp_format
  612. $fonts[$key] = 0; // Index of the default font
  613. $total_formats = count($this->_formats);
  614. for ($i = 0; $i < $total_formats; ++$i) {
  615. $key = $this->_formats[$i]->getFontKey();
  616. if (isset($fonts[$key])) {
  617. // FONT has already been used
  618. $this->_formats[$i]->font_index = $fonts[$key];
  619. } else {
  620. // Add a new FONT record
  621. $fonts[$key] = $index;
  622. $this->_formats[$i]->font_index = $index;
  623. ++$index;
  624. $font = $this->_formats[$i]->getFont();
  625. $this->_append($font);
  626. }
  627. }
  628. }
  629. /**
  630. * Store user defined numerical formats i.e. FORMAT records
  631. *
  632. * @access private
  633. */
  634. function _storeAllNumFormats()
  635. {
  636. // Leaning num_format syndrome
  637. $hash_num_formats = array();
  638. $num_formats = array();
  639. $index = 164;
  640. // Iterate through the XF objects and write a FORMAT record if it isn't a
  641. // built-in format type and if the FORMAT string hasn't already been used.
  642. $total_formats = count($this->_formats);
  643. for ($i = 0; $i < $total_formats; ++$i) {
  644. $num_format = $this->_formats[$i]->_num_format;
  645. //////////////////////////////////////////////////////////////////////////////////////////
  646. // Removing this block for now. No true support for built-in number formats in PHPExcel //
  647. //////////////////////////////////////////////////////////////////////////////////////////
  648. /**
  649. // Check if $num_format is an index to a built-in format.
  650. // Also check for a string of zeros, which is a valid format string
  651. // but would evaluate to zero.
  652. //
  653. if (!preg_match("/^0+\d/", $num_format)) {
  654. if (preg_match("/^\d+$/", $num_format)) { // built-in format
  655. continue;
  656. }
  657. }
  658. **/
  659. if (isset($hash_num_formats[$num_format])) {
  660. // FORMAT has already been used
  661. $this->_formats[$i]->_num_format = $hash_num_formats[$num_format];
  662. } else{
  663. // Add a new FORMAT
  664. $hash_num_formats[$num_format] = $index;
  665. $this->_formats[$i]->_num_format = $index;
  666. $num_formats[] = $num_format;
  667. ++$index;
  668. }
  669. }
  670. // Write the new FORMAT records starting from 0xA4
  671. $index = 164;
  672. foreach ($num_formats as $num_format) {
  673. $this->_storeNumFormat($num_format,$index);
  674. ++$index;
  675. }
  676. }
  677. /**
  678. * Write all XF records.
  679. *
  680. * @access private
  681. */
  682. function _storeAllXfs()
  683. {
  684. // _tmp_format is added by the constructor. We use this to write the default XF's
  685. // The default font index is 0
  686. //
  687. $format = $this->_tmp_format;
  688. for ($i = 0; $i <= 14; ++$i) {
  689. $xf = $format->getXf('style'); // Style XF
  690. $this->_append($xf);
  691. }
  692. $xf = $format->getXf('cell'); // Cell XF
  693. $this->_append($xf);
  694. // User defined XFs
  695. $total_formats = count($this->_formats);
  696. for ($i = 0; $i < $total_formats; ++$i) {
  697. $xf = $this->_formats[$i]->getXf('cell');
  698. $this->_append($xf);
  699. }
  700. }
  701. /**
  702. * Write all STYLE records.
  703. *
  704. * @access private
  705. */
  706. function _storeAllStyles()
  707. {
  708. $this->_storeStyle();
  709. }
  710. /**
  711. * Write the EXTERNCOUNT and EXTERNSHEET records. These are used as indexes for
  712. * the NAME records.
  713. *
  714. * @access private
  715. */
  716. function _storeExterns()
  717. {
  718. // Create EXTERNCOUNT with number of worksheets
  719. $this->_storeExterncount(count($this->_worksheets));
  720. // Create EXTERNSHEET for each worksheet
  721. foreach ($this->_sheetnames as $sheetname) {
  722. $this->_storeExternsheet($sheetname);
  723. }
  724. }
  725. /**
  726. * Write the NAME record to define the print area and the repeat rows and cols.
  727. *
  728. * @access private
  729. */
  730. function _storeNames()
  731. {
  732. // Create the print area NAME records
  733. $total_worksheets = count($this->_worksheets);
  734. for ($i = 0; $i < $total_worksheets; ++$i) {
  735. // Write a Name record if the print area has been defined
  736. if (isset($this->_worksheets[$i]->print_rowmin)) {
  737. $this->_storeNameShort(
  738. $this->_worksheets[$i]->index,
  739. 0x06, // NAME type
  740. $this->_worksheets[$i]->print_rowmin,
  741. $this->_worksheets[$i]->print_rowmax,
  742. $this->_worksheets[$i]->print_colmin,
  743. $this->_worksheets[$i]->print_colmax
  744. );
  745. }
  746. }
  747. // Create the print title NAME records
  748. $total_worksheets = count($this->_worksheets);
  749. for ($i = 0; $i < $total_worksheets; ++$i) {
  750. $rowmin = $this->_worksheets[$i]->title_rowmin;
  751. $rowmax = $this->_worksheets[$i]->title_rowmax;
  752. $colmin = $this->_worksheets[$i]->title_colmin;
  753. $colmax = $this->_worksheets[$i]->title_colmax;
  754. // Determine if row + col, row, col or nothing has been defined
  755. // and write the appropriate record
  756. //
  757. if (isset($rowmin) && isset($colmin)) {
  758. // Row and column titles have been defined.
  759. // Row title has been defined.
  760. $this->_storeNameLong(
  761. $this->_worksheets[$i]->index,
  762. 0x07, // NAME type
  763. $rowmin,
  764. $rowmax,
  765. $colmin,
  766. $colmax
  767. );
  768. } elseif (isset($rowmin)) {
  769. // Row title has been defined.
  770. $this->_storeNameShort(
  771. $this->_worksheets[$i]->index,
  772. 0x07, // NAME type
  773. $rowmin,
  774. $rowmax,
  775. 0x00,
  776. 0xff
  777. );
  778. } elseif (isset($colmin)) {
  779. // Column title has been defined.
  780. $this->_storeNameShort(
  781. $this->_worksheets[$i]->index,
  782. 0x07, // NAME type
  783. 0x0000,
  784. 0x3fff,
  785. $colmin,
  786. $colmax
  787. );
  788. } else {
  789. // Print title hasn't been defined.
  790. }
  791. }
  792. }
  793. /**
  794. * Writes all the DEFINEDNAME records (BIFF8).
  795. * So far this is only used for repeating rows/columns (print titles) and print areas
  796. */
  797. public function writeAllDefinedNamesBiff8()
  798. {
  799. $chunk = '';
  800. // write the print titles (repeating rows, columns), if any
  801. $total_worksheets = count($this->_worksheets);
  802. for ($i = 0; $i < $total_worksheets; ++$i) {
  803. // repeatColumns / repeatRows
  804. if ($this->_phpExcel->getSheet($i)->getPageSetup()->isColumnsToRepeatAtLeftSet() || $this->_phpExcel->getSheet($i)->getPageSetup()->isRowsToRepeatAtTopSet()) {
  805. // Row and column titles have been defined
  806. // Columns to repeat
  807. if ($this->_phpExcel->getSheet($i)->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
  808. $repeat = $this->_phpExcel->getSheet($i)->getPageSetup()->getColumnsToRepeatAtLeft();
  809. $colmin = PHPExcel_Cell::columnIndexFromString($repeat[0]) - 1;
  810. $colmax = PHPExcel_Cell::columnIndexFromString($repeat[1]) - 1;
  811. } else {
  812. $colmin = 0;
  813. $colmax = 255;
  814. }
  815. // Rows to repeat
  816. if ($this->_phpExcel->getSheet($i)->getPageSetup()->isRowsToRepeatAtTopSet()) {
  817. $repeat = $this->_phpExcel->getSheet($i)->getPageSetup()->getRowsToRepeatAtTop();
  818. $rowmin = $repeat[0] - 1;
  819. $rowmax = $repeat[1] - 1;
  820. } else {
  821. $rowmin = 0;
  822. $rowmax = 65535;
  823. }
  824. // construct formula data manually because parser does not recognize absolute 3d cell references
  825. $formulaData = pack('Cvvvvv', 0x3B, $i, $rowmin, $rowmax, $colmin, $colmax);
  826. // store the DEFINEDNAME record
  827. $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x07), $formulaData, $i + 1, true));
  828. }
  829. }
  830. // write the print areas, if any
  831. for ($i = 0; $i < $total_worksheets; ++$i) {
  832. if ($this->_phpExcel->getSheet($i)->getPageSetup()->isPrintAreaSet()) {
  833. // Print area
  834. $printArea = PHPExcel_Cell::splitRange($this->_phpExcel->getSheet($i)->getPageSetup()->getPrintArea());
  835. $printArea[0] = PHPExcel_Cell::coordinateFromString($printArea[0]);
  836. $printArea[1] = PHPExcel_Cell::coordinateFromString($printArea[1]);
  837. $print_rowmin = $printArea[0][1] - 1;
  838. $print_rowmax = $printArea[1][1] - 1;
  839. $print_colmin = PHPExcel_Cell::columnIndexFromString($printArea[0][0]) - 1;
  840. $print_colmax = PHPExcel_Cell::columnIndexFromString($printArea[1][0]) - 1;
  841. // construct formula data manually because parser does not recognize absolute 3d cell references
  842. $formulaData = pack('Cvvvvv', 0x3B, $i, $print_rowmin, $print_rowmax, $print_colmin, $print_colmax);
  843. // store the DEFINEDNAME record
  844. $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x06), $formulaData, $i + 1, true));
  845. }
  846. }
  847. return $chunk;
  848. }
  849. /**
  850. * Write a DEFINEDNAME record for BIFF8 using explicit binary formula data
  851. *
  852. * @param string $name The name in UTF-8
  853. * @param string $formulaData The binary formula data
  854. * @param string $sheetIndex 1-based sheet index the defined name applies to. 0 = global
  855. * @param boolean $isBuiltIn Built-in name?
  856. * @return string Complete binary record data
  857. */
  858. public function writeDefinedNameBiff8($name, $formulaData, $sheetIndex = 0, $isBuiltIn = false)
  859. {
  860. $record = 0x0018;
  861. // option flags
  862. $options = $isBuiltIn ? 0x20 : 0x00;
  863. // length of the name, character count
  864. $nlen = function_exists('mb_strlen') ?
  865. mb_strlen($name, 'UTF8') : strlen($name);
  866. // name with stripped length field
  867. $name = substr(PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($name), 2);
  868. // size of the formula (in bytes)
  869. $sz = strlen($formulaData);
  870. // combine the parts
  871. $data = pack('vCCvvvCCCC', $options, 0, $nlen, $sz, 0, $sheetIndex, 0, 0, 0, 0)
  872. . $name . $formulaData;
  873. $length = strlen($data);
  874. $header = pack('vv', $record, $length);
  875. return $header . $data;
  876. }
  877. /******************************************************************************
  878. *
  879. * BIFF RECORDS
  880. *
  881. */
  882. /**
  883. * Stores the CODEPAGE biff record.
  884. *
  885. * @access private
  886. */
  887. function _storeCodepage()
  888. {
  889. $record = 0x0042; // Record identifier
  890. $length = 0x0002; // Number of bytes to follow
  891. $cv = $this->_codepage; // The code page
  892. $header = pack('vv', $record, $length);
  893. $data = pack('v', $cv);
  894. $this->_append($header . $data);
  895. }
  896. /**
  897. * Write Excel BIFF WINDOW1 record.
  898. *
  899. * @access private
  900. */
  901. function _storeWindow1()
  902. {
  903. $record = 0x003D; // Record identifier
  904. $length = 0x0012; // Number of bytes to follow
  905. $xWn = 0x0000; // Horizontal position of window
  906. $yWn = 0x0000; // Vertical position of window
  907. $dxWn = 0x25BC; // Width of window
  908. $dyWn = 0x1572; // Height of window
  909. $grbit = 0x0038; // Option flags
  910. $ctabsel = $this->_selected; // Number of workbook tabs selected
  911. $wTabRatio = 0x0258; // Tab to scrollbar ratio
  912. $itabFirst = $this->_firstsheet; // 1st displayed worksheet
  913. $itabCur = $this->_activesheet; // Active worksheet
  914. $header = pack("vv", $record, $length);
  915. $data = pack("vvvvvvvvv", $xWn, $yWn, $dxWn, $dyWn,
  916. $grbit,
  917. $itabCur, $itabFirst,
  918. $ctabsel, $wTabRatio);
  919. $this->_append($header . $data);
  920. }
  921. /**
  922. * Writes Excel BIFF BOUNDSHEET record.
  923. * FIXME: inconsistent with BIFF documentation
  924. *
  925. * @param string $sheetname Worksheet name
  926. * @param integer $offset Location of worksheet BOF
  927. * @access private
  928. */
  929. function _storeBoundsheet($sheetname,$offset)
  930. {
  931. $record = 0x0085; // Record identifier
  932. if ($this->_BIFF_version == 0x0600) {
  933. //$recordData = $this->_writeUnicodeDataShort($sheetname);
  934. $recordData = PHPExcel_Shared_String::UTF8toBIFF8UnicodeShort($sheetname);
  935. $length = 0x06 + strlen($recordData); // Number of bytes to follow
  936. } else {
  937. $length = 0x07 + strlen($sheetname); // Number of bytes to follow
  938. }
  939. $grbit = 0x0000; // Visibility and sheet type
  940. $header = pack("vv", $record, $length);
  941. if ($this->_BIFF_version == 0x0600) {
  942. $data = pack("Vv", $offset, $grbit);
  943. $this->_append($header.$data.$recordData);
  944. } else {
  945. $cch = strlen($sheetname); // Length of sheet name
  946. $data = pack("VvC", $offset, $grbit, $cch);
  947. $this->_append($header.$data.$sheetname);
  948. }
  949. }
  950. /**
  951. * Write Internal SUPBOOK record
  952. *
  953. * @access private
  954. */
  955. public function writeSupbookInternal()
  956. {
  957. $record = 0x01AE; // Record identifier
  958. $length = 0x0004; // Bytes to follow
  959. $header = pack("vv", $record, $length);
  960. //$data = pack("vv", count($this->_worksheets), 0x0104);
  961. $data = pack("vv", count($this->_worksheets), 0x0401);
  962. //$this->_append($header . $data);
  963. return $this->writeData($header . $data);
  964. }
  965. /**
  966. * Writes the Excel BIFF EXTERNSHEET record. These references are used by
  967. * formulas.
  968. *
  969. * @param string $sheetname Worksheet name
  970. * @access private
  971. */
  972. public function writeExternsheetBiff8()
  973. {
  974. $total_references = count($this->_parser->_references);
  975. $record = 0x0017; // Record identifier
  976. $length = 2 + 6 * $total_references; // Number of bytes to follow
  977. $supbook_index = 0; // FIXME: only using internal SUPBOOK record
  978. $header = pack("vv", $record, $length);
  979. $data = pack('v', $total_references);
  980. for ($i = 0; $i < $total_references; ++$i) {
  981. $data .= $this->_parser->_references[$i];
  982. }
  983. //$this->_append($header . $data);
  984. return $this->writeData($header . $data);
  985. }
  986. /**
  987. * Write Excel BIFF STYLE records.
  988. *
  989. * @access private
  990. */
  991. function _storeStyle()
  992. {
  993. $record = 0x0293; // Record identifier
  994. $length = 0x0004; // Bytes to follow
  995. $ixfe = 0x8000; // Index to style XF
  996. $BuiltIn = 0x00; // Built-in style
  997. $iLevel = 0xff; // Outline style level
  998. $header = pack("vv", $record, $length);
  999. $data = pack("vCC", $ixfe, $BuiltIn, $iLevel);
  1000. $this->_append($header . $data);
  1001. }
  1002. /**
  1003. * Writes Excel FORMAT record for non "built-in" numerical formats.
  1004. *
  1005. * @param string $format Custom format string
  1006. * @param integer $ifmt Format index code
  1007. * @access private
  1008. */
  1009. function _storeNumFormat($format, $ifmt)
  1010. {
  1011. $record = 0x041E; // Record identifier
  1012. if ($this->_BIFF_version == 0x0600) {
  1013. //$numberFormatString = $this->_writeUnicodeDataLong($format);
  1014. $numberFormatString = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($format);
  1015. $length = 2 + strlen($numberFormatString); // Number of bytes to follow
  1016. } elseif ($this->_BIFF_version == 0x0500) {
  1017. $length = 3 + strlen($format); // Number of bytes to follow
  1018. }
  1019. $header = pack("vv", $record, $length);
  1020. if ($this->_BIFF_version == 0x0600) {
  1021. $data = pack("v", $ifmt) . $numberFormatString;
  1022. $this->_append($header . $data);
  1023. } elseif ($this->_BIFF_version == 0x0500) {
  1024. $cch = strlen($format); // Length of format string
  1025. $data = pack("vC", $ifmt, $cch);
  1026. $this->_append($header . $data . $format);
  1027. }
  1028. }
  1029. /**
  1030. * Write DATEMODE record to indicate the date system in use (1904 or 1900).
  1031. *
  1032. * @access private
  1033. */
  1034. function _storeDatemode()
  1035. {
  1036. $record = 0x0022; // Record identifier
  1037. $length = 0x0002; // Bytes to follow
  1038. $f1904 = (PHPExcel_Shared_Date::getExcelCalendar() == PHPExcel_Shared_Date::CALENDAR_MAC_1904) ?
  1039. 1 : 0; // Flag for 1904 date system
  1040. $header = pack("vv", $record, $length);
  1041. $data = pack("v", $f1904);
  1042. $this->_append($header . $data);
  1043. }
  1044. /**
  1045. * Write BIFF record EXTERNCOUNT to indicate the number of external sheet
  1046. * references in the workbook.
  1047. *
  1048. * Excel only stores references to external sheets that are used in NAME.
  1049. * The workbook NAME record is required to define the print area and the repeat
  1050. * rows and columns.
  1051. *
  1052. * A similar method is used in Worksheet.php for a slightly different purpose.
  1053. *
  1054. * @param integer $cxals Number of external references
  1055. * @access private
  1056. */
  1057. function _storeExterncount($cxals)
  1058. {
  1059. $record = 0x0016; // Record identifier
  1060. $length = 0x0002; // Number of bytes to follow
  1061. $header = pack("vv", $record, $length);
  1062. $data = pack("v", $cxals);
  1063. $this->_append($header . $data);
  1064. }
  1065. /**
  1066. * Writes the Excel BIFF EXTERNSHEET record. These references are used by
  1067. * formulas. NAME record is required to define the print area and the repeat
  1068. * rows and columns.
  1069. *
  1070. * A similar method is used in Worksheet.php for a slightly different purpose.
  1071. *
  1072. * @param string $sheetname Worksheet name
  1073. * @access private
  1074. */
  1075. function _storeExternsheet($sheetname)
  1076. {
  1077. $record = 0x0017; // Record identifier
  1078. $length = 0x02 + strlen($sheetname); // Number of bytes to follow
  1079. $cch = strlen($sheetname); // Length of sheet name
  1080. $rgch = 0x03; // Filename encoding
  1081. $header = pack("vv", $record, $length);
  1082. $data = pack("CC", $cch, $rgch);
  1083. $this->_append($header . $data . $sheetname);
  1084. }
  1085. /**
  1086. * Store the NAME record in the short format that is used for storing the print
  1087. * area, repeat rows only and repeat columns only.
  1088. *
  1089. * @param integer $index Sheet index
  1090. * @param integer $type Built-in name type
  1091. * @param integer $rowmin Start row
  1092. * @param integer $rowmax End row
  1093. * @param integer $colmin Start colum
  1094. * @param integer $colmax End column
  1095. * @access private
  1096. */
  1097. function _storeNameShort($index, $type, $rowmin, $rowmax, $colmin, $colmax)
  1098. {
  1099. $record = 0x0018; // Record identifier
  1100. $length = 0x0024; // Number of bytes to follow
  1101. $grbit = 0x0020; // Option flags
  1102. $chKey = 0x00; // Keyboard shortcut
  1103. $cch = 0x01; // Length of text name
  1104. $cce = 0x0015; // Length of text definition
  1105. $ixals = $index + 1; // Sheet index
  1106. $itab = $ixals; // Equal to ixals
  1107. $cchCustMenu = 0x00; // Length of cust menu text
  1108. $cchDescription = 0x00; // Length of description text
  1109. $cchHelptopic = 0x00; // Length of help topic text
  1110. $cchStatustext = 0x00; // Length of status bar text
  1111. $rgch = $type; // Built-in name type
  1112. $unknown03 = 0x3b;
  1113. $unknown04 = 0xffff-$index;
  1114. $unknown05 = 0x0000;
  1115. $unknown06 = 0x0000;
  1116. $unknown07 = 0x1087;
  1117. $unknown08 = 0x8005;
  1118. $header = pack("vv", $record, $length);
  1119. $data = pack("v", $grbit);
  1120. $data .= pack("C", $chKey);
  1121. $data .= pack("C", $cch);
  1122. $data .= pack("v", $cce);
  1123. $data .= pack("v", $ixals);
  1124. $data .= pack("v", $itab);
  1125. $data .= pack("C", $cchCustMenu);
  1126. $data .= pack("C", $cchDescription);
  1127. $data .= pack("C", $cchHelptopic);
  1128. $data .= pack("C", $cchStatustext);
  1129. $data .= pack("C", $rgch);
  1130. $data .= pack("C", $unknown03);
  1131. $data .= pack("v", $unknown04);
  1132. $data .= pack("v", $unknown05);
  1133. $data .= pack("v", $unknown06);
  1134. $data .= pack("v", $unknown07);
  1135. $data .= pack("v", $unknown08);
  1136. $data .= pack("v", $index);
  1137. $data .= pack("v", $index);
  1138. $data .= pack("v", $rowmin);
  1139. $data .= pack("v", $rowmax);
  1140. $data .= pack("C", $colmin);
  1141. $data .= pack("C", $colmax);
  1142. $this->_append($header . $data);
  1143. }
  1144. /**
  1145. * Store the NAME record in the long format that is used for storing the repeat
  1146. * rows and columns when both are specified. This shares a lot of code with
  1147. * _storeNameShort() but we use a separate method to keep the code clean.
  1148. * Code abstraction for reuse can be carried too far, and I should know. ;-)
  1149. *
  1150. * @param integer $index Sheet index
  1151. * @param integer $type Built-in name type
  1152. * @param integer $rowmin Start row
  1153. * @param integer $rowmax End row
  1154. * @param integer $colmin Start colum
  1155. * @param integer $colmax End column
  1156. * @access private
  1157. */
  1158. function _storeNameLong($index, $type, $rowmin, $rowmax, $colmin, $colmax)
  1159. {
  1160. $record = 0x0018; // Record identifier
  1161. $length = 0x003d; // Number of bytes to follow
  1162. $grbit = 0x0020; // Option flags
  1163. $chKey = 0x00; // Keyboard shortcut
  1164. $cch = 0x01; // Length of text name
  1165. $cce = 0x002e; // Length of text definition
  1166. $ixals = $index + 1; // Sheet index
  1167. $itab = $ixals; // Equal to ixals
  1168. $cchCustMenu = 0x00; // Length of cust menu text
  1169. $cchDescription = 0x00; // Length of description text
  1170. $cchHelptopic = 0x00; // Length of help topic text
  1171. $cchStatustext = 0x00; // Length of status bar text
  1172. $rgch = $type; // Built-in name type
  1173. $unknown01 = 0x29;
  1174. $unknown02 = 0x002b;
  1175. $unknown03 = 0x3b;
  1176. $unknown04 = 0xffff-$index;
  1177. $unknown05 = 0x0000;
  1178. $unknown06 = 0x0000;
  1179. $unknown07 = 0x1087;
  1180. $unknown08 = 0x8008;
  1181. $header = pack("vv", $record, $length);
  1182. $data = pack("v", $grbit);
  1183. $data .= pack("C", $chKey);
  1184. $data .= pack("C", $cch);
  1185. $data .= pack("v", $cce);
  1186. $data .= pack("v", $ixals);
  1187. $data .= pack("v", $itab);
  1188. $data .= pack("C", $cchCustMenu);
  1189. $data .= pack("C", $cchDescription);
  1190. $data .= pack("C", $cchHelptopic);
  1191. $data .= pack("C", $cchStatustext);
  1192. $data .= pack("C", $rgch);
  1193. $data .= pack("C", $unknown01);
  1194. $data .= pack("v", $unknown02);
  1195. // Column definition
  1196. $data .= pack("C", $unknown03);
  1197. $data .= pack("v", $unknown04);
  1198. $data .= pack("v", $unknown05);
  1199. $data .= pack("v", $unknown06);
  1200. $data .= pack("v", $unknown07);
  1201. $data .= pack("v", $unknown08);
  1202. $data .= pack("v", $index);
  1203. $data .= pack("v", $index);
  1204. $data .= pack("v", 0x0000);
  1205. $data .= pack("v", 0x3fff);
  1206. $data .= pack("C", $colmin);
  1207. $data .= pack("C", $colmax);
  1208. // Row definition
  1209. $data .= pack("C", $unknown03);
  1210. $data .= pack("v", $unknown04);
  1211. $data .= pack("v", $unknown05);
  1212. $data .= pack("v", $unknown06);
  1213. $data .= pack("v", $unknown07);
  1214. $data .= pack("v", $unknown08);
  1215. $data .= pack("v", $index);
  1216. $data .= pack("v", $index);
  1217. $data .= pack("v", $rowmin);
  1218. $data .= pack("v", $rowmax);
  1219. $data .= pack("C", 0x00);
  1220. $data .= pack("C", 0xff);
  1221. // End of data
  1222. $data .= pack("C", 0x10);
  1223. $this->_append($header . $data);
  1224. }
  1225. /**
  1226. * Stores the COUNTRY record for localization
  1227. *
  1228. * @return string
  1229. */
  1230. public function writeCountry()
  1231. {
  1232. $record = 0x008C; // Record identifier
  1233. $length = 4; // Number of bytes to follow
  1234. $header = pack('vv', $record, $length);
  1235. /* using the same country code always for simplicity */
  1236. $data = pack('vv', $this->_country_code, $this->_country_code);
  1237. //$this->_append($header . $data);
  1238. return $this->writeData($header . $data);
  1239. }
  1240. /**
  1241. * Stores the PALETTE biff record.
  1242. *
  1243. * @access private
  1244. */
  1245. function _storePalette()
  1246. {
  1247. $aref = $this->_palette;
  1248. $record = 0x0092; // Record identifier
  1249. $length = 2 + 4 * count($aref); // Number of bytes to follow
  1250. $ccv = count($aref); // Number of RGB values to follow
  1251. $data = ''; // The RGB data
  1252. // Pack the RGB data
  1253. foreach ($aref as $color) {
  1254. foreach ($color as $byte) {
  1255. $data .= pack("C",$byte);
  1256. }
  1257. }
  1258. $header = pack("vvv", $record, $length, $ccv);
  1259. $this->_append($header . $data);
  1260. }
  1261. /**
  1262. * Calculate
  1263. * Handling of the SST continue blocks is complicated by the need to include an
  1264. * additional continuation byte depending on whether the string is …

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