PageRenderTime 86ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/PHPExcel/Writer/Excel5/Worksheet.php

https://github.com/yuweijun/blog
PHP | 4240 lines | 2809 code | 441 blank | 990 comment | 374 complexity | 4cb0aacbca1e5465262e24da720656ab MD5 | raw file
  1. <?php
  2. /**
  3. * PHPExcel_Writer_Excel5_Worksheet
  4. *
  5. * Copyright (c) 2006 - 2015 PHPExcel
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * @category PHPExcel
  22. * @package PHPExcel_Writer_Excel5
  23. * @copyright Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version ##VERSION##, ##DATE##
  26. */
  27. // Original file header of PEAR::Spreadsheet_Excel_Writer_Worksheet (used as the base for this class):
  28. // -----------------------------------------------------------------------------------------
  29. // /*
  30. // * Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
  31. // *
  32. // * The majority of this is _NOT_ my code. I simply ported it from the
  33. // * PERL Spreadsheet::WriteExcel module.
  34. // *
  35. // * The author of the Spreadsheet::WriteExcel module is John McNamara
  36. // * <jmcnamara@cpan.org>
  37. // *
  38. // * I _DO_ maintain this code, and John McNamara has nothing to do with the
  39. // * porting of this code to PHP. Any questions directly related to this
  40. // * class library should be directed to me.
  41. // *
  42. // * License Information:
  43. // *
  44. // * Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
  45. // * Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
  46. // *
  47. // * This library is free software; you can redistribute it and/or
  48. // * modify it under the terms of the GNU Lesser General Public
  49. // * License as published by the Free Software Foundation; either
  50. // * version 2.1 of the License, or (at your option) any later version.
  51. // *
  52. // * This library is distributed in the hope that it will be useful,
  53. // * but WITHOUT ANY WARRANTY; without even the implied warranty of
  54. // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  55. // * Lesser General Public License for more details.
  56. // *
  57. // * You should have received a copy of the GNU Lesser General Public
  58. // * License along with this library; if not, write to the Free Software
  59. // * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  60. // */
  61. class PHPExcel_Writer_Excel5_Worksheet extends PHPExcel_Writer_Excel5_BIFFwriter
  62. {
  63. /**
  64. * Formula parser
  65. *
  66. * @var PHPExcel_Writer_Excel5_Parser
  67. */
  68. private $parser;
  69. /**
  70. * Maximum number of characters for a string (LABEL record in BIFF5)
  71. * @var integer
  72. */
  73. private $xlsStringMaxLength;
  74. /**
  75. * Array containing format information for columns
  76. * @var array
  77. */
  78. private $columnInfo;
  79. /**
  80. * Array containing the selected area for the worksheet
  81. * @var array
  82. */
  83. private $selection;
  84. /**
  85. * The active pane for the worksheet
  86. * @var integer
  87. */
  88. private $activePane;
  89. /**
  90. * Whether to use outline.
  91. * @var integer
  92. */
  93. private $outlineOn;
  94. /**
  95. * Auto outline styles.
  96. * @var bool
  97. */
  98. private $outlineStyle;
  99. /**
  100. * Whether to have outline summary below.
  101. * @var bool
  102. */
  103. private $outlineBelow;
  104. /**
  105. * Whether to have outline summary at the right.
  106. * @var bool
  107. */
  108. private $outlineRight;
  109. /**
  110. * Reference to the total number of strings in the workbook
  111. * @var integer
  112. */
  113. private $stringTotal;
  114. /**
  115. * Reference to the number of unique strings in the workbook
  116. * @var integer
  117. */
  118. private $stringUnique;
  119. /**
  120. * Reference to the array containing all the unique strings in the workbook
  121. * @var array
  122. */
  123. private $stringTable;
  124. /**
  125. * Color cache
  126. */
  127. private $colors;
  128. /**
  129. * Index of first used row (at least 0)
  130. * @var int
  131. */
  132. private $firstRowIndex;
  133. /**
  134. * Index of last used row. (no used rows means -1)
  135. * @var int
  136. */
  137. private $lastRowIndex;
  138. /**
  139. * Index of first used column (at least 0)
  140. * @var int
  141. */
  142. private $firstColumnIndex;
  143. /**
  144. * Index of last used column (no used columns means -1)
  145. * @var int
  146. */
  147. private $lastColumnIndex;
  148. /**
  149. * Sheet object
  150. * @var PHPExcel_Worksheet
  151. */
  152. public $phpSheet;
  153. /**
  154. * Count cell style Xfs
  155. *
  156. * @var int
  157. */
  158. private $countCellStyleXfs;
  159. /**
  160. * Escher object corresponding to MSODRAWING
  161. *
  162. * @var PHPExcel_Shared_Escher
  163. */
  164. private $escher;
  165. /**
  166. * Array of font hashes associated to FONT records index
  167. *
  168. * @var array
  169. */
  170. public $fontHashIndex;
  171. /**
  172. * Constructor
  173. *
  174. * @param int &$str_total Total number of strings
  175. * @param int &$str_unique Total number of unique strings
  176. * @param array &$str_table String Table
  177. * @param array &$colors Colour Table
  178. * @param mixed $parser The formula parser created for the Workbook
  179. * @param boolean $preCalculateFormulas Flag indicating whether formulas should be calculated or just written
  180. * @param string $phpSheet The worksheet to write
  181. * @param PHPExcel_Worksheet $phpSheet
  182. */
  183. public function __construct(&$str_total, &$str_unique, &$str_table, &$colors, $parser, $preCalculateFormulas, $phpSheet)
  184. {
  185. // It needs to call its parent's constructor explicitly
  186. parent::__construct();
  187. // change BIFFwriter limit for CONTINUE records
  188. // $this->_limit = 8224;
  189. $this->_preCalculateFormulas = $preCalculateFormulas;
  190. $this->stringTotal = &$str_total;
  191. $this->stringUnique = &$str_unique;
  192. $this->stringTable = &$str_table;
  193. $this->colors = &$colors;
  194. $this->parser = $parser;
  195. $this->phpSheet = $phpSheet;
  196. //$this->ext_sheets = array();
  197. //$this->offset = 0;
  198. $this->xlsStringMaxLength = 255;
  199. $this->columnInfo = array();
  200. $this->selection = array(0,0,0,0);
  201. $this->activePane = 3;
  202. $this->_print_headers = 0;
  203. $this->outlineStyle = 0;
  204. $this->outlineBelow = 1;
  205. $this->outlineRight = 1;
  206. $this->outlineOn = 1;
  207. $this->fontHashIndex = array();
  208. // calculate values for DIMENSIONS record
  209. $minR = 1;
  210. $minC = 'A';
  211. $maxR = $this->phpSheet->getHighestRow();
  212. $maxC = $this->phpSheet->getHighestColumn();
  213. // Determine lowest and highest column and row
  214. // $this->firstRowIndex = ($minR > 65535) ? 65535 : $minR;
  215. $this->lastRowIndex = ($maxR > 65535) ? 65535 : $maxR ;
  216. $this->firstColumnIndex = PHPExcel_Cell::columnIndexFromString($minC);
  217. $this->lastColumnIndex = PHPExcel_Cell::columnIndexFromString($maxC);
  218. // if ($this->firstColumnIndex > 255) $this->firstColumnIndex = 255;
  219. if ($this->lastColumnIndex > 255) {
  220. $this->lastColumnIndex = 255;
  221. }
  222. $this->countCellStyleXfs = count($phpSheet->getParent()->getCellStyleXfCollection());
  223. }
  224. /**
  225. * Add data to the beginning of the workbook (note the reverse order)
  226. * and to the end of the workbook.
  227. *
  228. * @access public
  229. * @see PHPExcel_Writer_Excel5_Workbook::storeWorkbook()
  230. */
  231. public function close()
  232. {
  233. $phpSheet = $this->phpSheet;
  234. $num_sheets = $phpSheet->getParent()->getSheetCount();
  235. // Write BOF record
  236. $this->storeBof(0x0010);
  237. // Write PRINTHEADERS
  238. $this->writePrintHeaders();
  239. // Write PRINTGRIDLINES
  240. $this->writePrintGridlines();
  241. // Write GRIDSET
  242. $this->writeGridset();
  243. // Calculate column widths
  244. $phpSheet->calculateColumnWidths();
  245. // Column dimensions
  246. if (($defaultWidth = $phpSheet->getDefaultColumnDimension()->getWidth()) < 0) {
  247. $defaultWidth = PHPExcel_Shared_Font::getDefaultColumnWidthByFont($phpSheet->getParent()->getDefaultStyle()->getFont());
  248. }
  249. $columnDimensions = $phpSheet->getColumnDimensions();
  250. $maxCol = $this->lastColumnIndex -1;
  251. for ($i = 0; $i <= $maxCol; ++$i) {
  252. $hidden = 0;
  253. $level = 0;
  254. $xfIndex = 15; // there are 15 cell style Xfs
  255. $width = $defaultWidth;
  256. $columnLetter = PHPExcel_Cell::stringFromColumnIndex($i);
  257. if (isset($columnDimensions[$columnLetter])) {
  258. $columnDimension = $columnDimensions[$columnLetter];
  259. if ($columnDimension->getWidth() >= 0) {
  260. $width = $columnDimension->getWidth();
  261. }
  262. $hidden = $columnDimension->getVisible() ? 0 : 1;
  263. $level = $columnDimension->getOutlineLevel();
  264. $xfIndex = $columnDimension->getXfIndex() + 15; // there are 15 cell style Xfs
  265. }
  266. // Components of columnInfo:
  267. // $firstcol first column on the range
  268. // $lastcol last column on the range
  269. // $width width to set
  270. // $xfIndex The optional cell style Xf index to apply to the columns
  271. // $hidden The optional hidden atribute
  272. // $level The optional outline level
  273. $this->columnInfo[] = array($i, $i, $width, $xfIndex, $hidden, $level);
  274. }
  275. // Write GUTS
  276. $this->writeGuts();
  277. // Write DEFAULTROWHEIGHT
  278. $this->writeDefaultRowHeight();
  279. // Write WSBOOL
  280. $this->writeWsbool();
  281. // Write horizontal and vertical page breaks
  282. $this->writeBreaks();
  283. // Write page header
  284. $this->writeHeader();
  285. // Write page footer
  286. $this->writeFooter();
  287. // Write page horizontal centering
  288. $this->writeHcenter();
  289. // Write page vertical centering
  290. $this->writeVcenter();
  291. // Write left margin
  292. $this->writeMarginLeft();
  293. // Write right margin
  294. $this->writeMarginRight();
  295. // Write top margin
  296. $this->writeMarginTop();
  297. // Write bottom margin
  298. $this->writeMarginBottom();
  299. // Write page setup
  300. $this->writeSetup();
  301. // Write sheet protection
  302. $this->writeProtect();
  303. // Write SCENPROTECT
  304. $this->writeScenProtect();
  305. // Write OBJECTPROTECT
  306. $this->writeObjectProtect();
  307. // Write sheet password
  308. $this->writePassword();
  309. // Write DEFCOLWIDTH record
  310. $this->writeDefcol();
  311. // Write the COLINFO records if they exist
  312. if (!empty($this->columnInfo)) {
  313. $colcount = count($this->columnInfo);
  314. for ($i = 0; $i < $colcount; ++$i) {
  315. $this->writeColinfo($this->columnInfo[$i]);
  316. }
  317. }
  318. $autoFilterRange = $phpSheet->getAutoFilter()->getRange();
  319. if (!empty($autoFilterRange)) {
  320. // Write AUTOFILTERINFO
  321. $this->writeAutoFilterInfo();
  322. }
  323. // Write sheet dimensions
  324. $this->writeDimensions();
  325. // Row dimensions
  326. foreach ($phpSheet->getRowDimensions() as $rowDimension) {
  327. $xfIndex = $rowDimension->getXfIndex() + 15; // there are 15 cellXfs
  328. $this->writeRow($rowDimension->getRowIndex() - 1, $rowDimension->getRowHeight(), $xfIndex, ($rowDimension->getVisible() ? '0' : '1'), $rowDimension->getOutlineLevel());
  329. }
  330. // Write Cells
  331. foreach ($phpSheet->getCellCollection() as $cellID) {
  332. $cell = $phpSheet->getCell($cellID);
  333. $row = $cell->getRow() - 1;
  334. $column = PHPExcel_Cell::columnIndexFromString($cell->getColumn()) - 1;
  335. // Don't break Excel!
  336. // if ($row + 1 > 65536 or $column + 1 > 256) {
  337. if ($row > 65535 || $column > 255) {
  338. break;
  339. }
  340. // Write cell value
  341. $xfIndex = $cell->getXfIndex() + 15; // there are 15 cell style Xfs
  342. $cVal = $cell->getValue();
  343. if ($cVal instanceof PHPExcel_RichText) {
  344. // $this->writeString($row, $column, $cVal->getPlainText(), $xfIndex);
  345. $arrcRun = array();
  346. $str_len = PHPExcel_Shared_String::CountCharacters($cVal->getPlainText(), 'UTF-8');
  347. $str_pos = 0;
  348. $elements = $cVal->getRichTextElements();
  349. foreach ($elements as $element) {
  350. // FONT Index
  351. if ($element instanceof PHPExcel_RichText_Run) {
  352. $str_fontidx = $this->fontHashIndex[$element->getFont()->getHashCode()];
  353. } else {
  354. $str_fontidx = 0;
  355. }
  356. $arrcRun[] = array('strlen' => $str_pos, 'fontidx' => $str_fontidx);
  357. // Position FROM
  358. $str_pos += PHPExcel_Shared_String::CountCharacters($element->getText(), 'UTF-8');
  359. }
  360. $this->writeRichTextString($row, $column, $cVal->getPlainText(), $xfIndex, $arrcRun);
  361. } else {
  362. switch ($cell->getDatatype()) {
  363. case PHPExcel_Cell_DataType::TYPE_STRING:
  364. case PHPExcel_Cell_DataType::TYPE_NULL:
  365. if ($cVal === '' || $cVal === null) {
  366. $this->writeBlank($row, $column, $xfIndex);
  367. } else {
  368. $this->writeString($row, $column, $cVal, $xfIndex);
  369. }
  370. break;
  371. case PHPExcel_Cell_DataType::TYPE_NUMERIC:
  372. $this->writeNumber($row, $column, $cVal, $xfIndex);
  373. break;
  374. case PHPExcel_Cell_DataType::TYPE_FORMULA:
  375. $calculatedValue = $this->_preCalculateFormulas ?
  376. $cell->getCalculatedValue() : null;
  377. $this->writeFormula($row, $column, $cVal, $xfIndex, $calculatedValue);
  378. break;
  379. case PHPExcel_Cell_DataType::TYPE_BOOL:
  380. $this->writeBoolErr($row, $column, $cVal, 0, $xfIndex);
  381. break;
  382. case PHPExcel_Cell_DataType::TYPE_ERROR:
  383. $this->writeBoolErr($row, $column, self::mapErrorCode($cVal), 1, $xfIndex);
  384. break;
  385. }
  386. }
  387. }
  388. // Append
  389. $this->writeMsoDrawing();
  390. // Write WINDOW2 record
  391. $this->writeWindow2();
  392. // Write PLV record
  393. $this->writePageLayoutView();
  394. // Write ZOOM record
  395. $this->writeZoom();
  396. if ($phpSheet->getFreezePane()) {
  397. $this->writePanes();
  398. }
  399. // Write SELECTION record
  400. $this->writeSelection();
  401. // Write MergedCellsTable Record
  402. $this->writeMergedCells();
  403. // Hyperlinks
  404. foreach ($phpSheet->getHyperLinkCollection() as $coordinate => $hyperlink) {
  405. list($column, $row) = PHPExcel_Cell::coordinateFromString($coordinate);
  406. $url = $hyperlink->getUrl();
  407. if (strpos($url, 'sheet://') !== false) {
  408. // internal to current workbook
  409. $url = str_replace('sheet://', 'internal:', $url);
  410. } elseif (preg_match('/^(http:|https:|ftp:|mailto:)/', $url)) {
  411. // URL
  412. // $url = $url;
  413. } else {
  414. // external (local file)
  415. $url = 'external:' . $url;
  416. }
  417. $this->writeUrl($row - 1, PHPExcel_Cell::columnIndexFromString($column) - 1, $url);
  418. }
  419. $this->writeDataValidity();
  420. $this->writeSheetLayout();
  421. // Write SHEETPROTECTION record
  422. $this->writeSheetProtection();
  423. $this->writeRangeProtection();
  424. $arrConditionalStyles = $phpSheet->getConditionalStylesCollection();
  425. if (!empty($arrConditionalStyles)) {
  426. $arrConditional = array();
  427. // @todo CFRule & CFHeader
  428. // Write CFHEADER record
  429. $this->writeCFHeader();
  430. // Write ConditionalFormattingTable records
  431. foreach ($arrConditionalStyles as $cellCoordinate => $conditionalStyles) {
  432. foreach ($conditionalStyles as $conditional) {
  433. if ($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_EXPRESSION
  434. || $conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CELLIS) {
  435. if (!in_array($conditional->getHashCode(), $arrConditional)) {
  436. $arrConditional[] = $conditional->getHashCode();
  437. // Write CFRULE record
  438. $this->writeCFRule($conditional);
  439. }
  440. }
  441. }
  442. }
  443. }
  444. $this->storeEof();
  445. }
  446. /**
  447. * Write a cell range address in BIFF8
  448. * always fixed range
  449. * See section 2.5.14 in OpenOffice.org's Documentation of the Microsoft Excel File Format
  450. *
  451. * @param string $range E.g. 'A1' or 'A1:B6'
  452. * @return string Binary data
  453. */
  454. private function writeBIFF8CellRangeAddressFixed($range = 'A1')
  455. {
  456. $explodes = explode(':', $range);
  457. // extract first cell, e.g. 'A1'
  458. $firstCell = $explodes[0];
  459. // extract last cell, e.g. 'B6'
  460. if (count($explodes) == 1) {
  461. $lastCell = $firstCell;
  462. } else {
  463. $lastCell = $explodes[1];
  464. }
  465. $firstCellCoordinates = PHPExcel_Cell::coordinateFromString($firstCell); // e.g. array(0, 1)
  466. $lastCellCoordinates = PHPExcel_Cell::coordinateFromString($lastCell); // e.g. array(1, 6)
  467. return pack('vvvv', $firstCellCoordinates[1] - 1, $lastCellCoordinates[1] - 1, PHPExcel_Cell::columnIndexFromString($firstCellCoordinates[0]) - 1, PHPExcel_Cell::columnIndexFromString($lastCellCoordinates[0]) - 1);
  468. }
  469. /**
  470. * Retrieves data from memory in one chunk, or from disk in $buffer
  471. * sized chunks.
  472. *
  473. * @return string The data
  474. */
  475. public function getData()
  476. {
  477. $buffer = 4096;
  478. // Return data stored in memory
  479. if (isset($this->_data)) {
  480. $tmp = $this->_data;
  481. unset($this->_data);
  482. return $tmp;
  483. }
  484. // No data to return
  485. return false;
  486. }
  487. /**
  488. * Set the option to print the row and column headers on the printed page.
  489. *
  490. * @access public
  491. * @param integer $print Whether to print the headers or not. Defaults to 1 (print).
  492. */
  493. public function printRowColHeaders($print = 1)
  494. {
  495. $this->_print_headers = $print;
  496. }
  497. /**
  498. * This method sets the properties for outlining and grouping. The defaults
  499. * correspond to Excel's defaults.
  500. *
  501. * @param bool $visible
  502. * @param bool $symbols_below
  503. * @param bool $symbols_right
  504. * @param bool $auto_style
  505. */
  506. public function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false)
  507. {
  508. $this->outlineOn = $visible;
  509. $this->outlineBelow = $symbols_below;
  510. $this->outlineRight = $symbols_right;
  511. $this->outlineStyle = $auto_style;
  512. // Ensure this is a boolean vale for Window2
  513. if ($this->outlineOn) {
  514. $this->outlineOn = 1;
  515. }
  516. }
  517. /**
  518. * Write a double to the specified row and column (zero indexed).
  519. * An integer can be written as a double. Excel will display an
  520. * integer. $format is optional.
  521. *
  522. * Returns 0 : normal termination
  523. * -2 : row or column out of range
  524. *
  525. * @param integer $row Zero indexed row
  526. * @param integer $col Zero indexed column
  527. * @param float $num The number to write
  528. * @param mixed $xfIndex The optional XF format
  529. * @return integer
  530. */
  531. private function writeNumber($row, $col, $num, $xfIndex)
  532. {
  533. $record = 0x0203; // Record identifier
  534. $length = 0x000E; // Number of bytes to follow
  535. $header = pack("vv", $record, $length);
  536. $data = pack("vvv", $row, $col, $xfIndex);
  537. $xl_double = pack("d", $num);
  538. if (self::getByteOrder()) { // if it's Big Endian
  539. $xl_double = strrev($xl_double);
  540. }
  541. $this->append($header.$data.$xl_double);
  542. return(0);
  543. }
  544. /**
  545. * Write a LABELSST record or a LABEL record. Which one depends on BIFF version
  546. *
  547. * @param int $row Row index (0-based)
  548. * @param int $col Column index (0-based)
  549. * @param string $str The string
  550. * @param int $xfIndex Index to XF record
  551. */
  552. private function writeString($row, $col, $str, $xfIndex)
  553. {
  554. $this->writeLabelSst($row, $col, $str, $xfIndex);
  555. }
  556. /**
  557. * Write a LABELSST record or a LABEL record. Which one depends on BIFF version
  558. * It differs from writeString by the writing of rich text strings.
  559. * @param int $row Row index (0-based)
  560. * @param int $col Column index (0-based)
  561. * @param string $str The string
  562. * @param mixed $xfIndex The XF format index for the cell
  563. * @param array $arrcRun Index to Font record and characters beginning
  564. */
  565. private function writeRichTextString($row, $col, $str, $xfIndex, $arrcRun)
  566. {
  567. $record = 0x00FD; // Record identifier
  568. $length = 0x000A; // Bytes to follow
  569. $str = PHPExcel_Shared_String::UTF8toBIFF8UnicodeShort($str, $arrcRun);
  570. /* check if string is already present */
  571. if (!isset($this->stringTable[$str])) {
  572. $this->stringTable[$str] = $this->stringUnique++;
  573. }
  574. $this->stringTotal++;
  575. $header = pack('vv', $record, $length);
  576. $data = pack('vvvV', $row, $col, $xfIndex, $this->stringTable[$str]);
  577. $this->append($header.$data);
  578. }
  579. /**
  580. * Write a string to the specified row and column (zero indexed).
  581. * NOTE: there is an Excel 5 defined limit of 255 characters.
  582. * $format is optional.
  583. * Returns 0 : normal termination
  584. * -2 : row or column out of range
  585. * -3 : long string truncated to 255 chars
  586. *
  587. * @access public
  588. * @param integer $row Zero indexed row
  589. * @param integer $col Zero indexed column
  590. * @param string $str The string to write
  591. * @param mixed $xfIndex The XF format index for the cell
  592. * @return integer
  593. */
  594. private function writeLabel($row, $col, $str, $xfIndex)
  595. {
  596. $strlen = strlen($str);
  597. $record = 0x0204; // Record identifier
  598. $length = 0x0008 + $strlen; // Bytes to follow
  599. $str_error = 0;
  600. if ($strlen > $this->xlsStringMaxLength) { // LABEL must be < 255 chars
  601. $str = substr($str, 0, $this->xlsStringMaxLength);
  602. $length = 0x0008 + $this->xlsStringMaxLength;
  603. $strlen = $this->xlsStringMaxLength;
  604. $str_error = -3;
  605. }
  606. $header = pack("vv", $record, $length);
  607. $data = pack("vvvv", $row, $col, $xfIndex, $strlen);
  608. $this->append($header . $data . $str);
  609. return($str_error);
  610. }
  611. /**
  612. * Write a string to the specified row and column (zero indexed).
  613. * This is the BIFF8 version (no 255 chars limit).
  614. * $format is optional.
  615. * Returns 0 : normal termination
  616. * -2 : row or column out of range
  617. * -3 : long string truncated to 255 chars
  618. *
  619. * @access public
  620. * @param integer $row Zero indexed row
  621. * @param integer $col Zero indexed column
  622. * @param string $str The string to write
  623. * @param mixed $xfIndex The XF format index for the cell
  624. * @return integer
  625. */
  626. private function writeLabelSst($row, $col, $str, $xfIndex)
  627. {
  628. $record = 0x00FD; // Record identifier
  629. $length = 0x000A; // Bytes to follow
  630. $str = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($str);
  631. /* check if string is already present */
  632. if (!isset($this->stringTable[$str])) {
  633. $this->stringTable[$str] = $this->stringUnique++;
  634. }
  635. $this->stringTotal++;
  636. $header = pack('vv', $record, $length);
  637. $data = pack('vvvV', $row, $col, $xfIndex, $this->stringTable[$str]);
  638. $this->append($header.$data);
  639. }
  640. /**
  641. * Writes a note associated with the cell given by the row and column.
  642. * NOTE records don't have a length limit.
  643. *
  644. * @param integer $row Zero indexed row
  645. * @param integer $col Zero indexed column
  646. * @param string $note The note to write
  647. */
  648. private function writeNote($row, $col, $note)
  649. {
  650. $note_length = strlen($note);
  651. $record = 0x001C; // Record identifier
  652. $max_length = 2048; // Maximun length for a NOTE record
  653. // Length for this record is no more than 2048 + 6
  654. $length = 0x0006 + min($note_length, 2048);
  655. $header = pack("vv", $record, $length);
  656. $data = pack("vvv", $row, $col, $note_length);
  657. $this->append($header . $data . substr($note, 0, 2048));
  658. for ($i = $max_length; $i < $note_length; $i += $max_length) {
  659. $chunk = substr($note, $i, $max_length);
  660. $length = 0x0006 + strlen($chunk);
  661. $header = pack("vv", $record, $length);
  662. $data = pack("vvv", -1, 0, strlen($chunk));
  663. $this->append($header.$data.$chunk);
  664. }
  665. return(0);
  666. }
  667. /**
  668. * Write a blank cell to the specified row and column (zero indexed).
  669. * A blank cell is used to specify formatting without adding a string
  670. * or a number.
  671. *
  672. * A blank cell without a format serves no purpose. Therefore, we don't write
  673. * a BLANK record unless a format is specified.
  674. *
  675. * Returns 0 : normal termination (including no format)
  676. * -1 : insufficient number of arguments
  677. * -2 : row or column out of range
  678. *
  679. * @param integer $row Zero indexed row
  680. * @param integer $col Zero indexed column
  681. * @param mixed $xfIndex The XF format index
  682. */
  683. public function writeBlank($row, $col, $xfIndex)
  684. {
  685. $record = 0x0201; // Record identifier
  686. $length = 0x0006; // Number of bytes to follow
  687. $header = pack("vv", $record, $length);
  688. $data = pack("vvv", $row, $col, $xfIndex);
  689. $this->append($header . $data);
  690. return 0;
  691. }
  692. /**
  693. * Write a boolean or an error type to the specified row and column (zero indexed)
  694. *
  695. * @param int $row Row index (0-based)
  696. * @param int $col Column index (0-based)
  697. * @param int $value
  698. * @param boolean $isError Error or Boolean?
  699. * @param int $xfIndex
  700. */
  701. private function writeBoolErr($row, $col, $value, $isError, $xfIndex)
  702. {
  703. $record = 0x0205;
  704. $length = 8;
  705. $header = pack("vv", $record, $length);
  706. $data = pack("vvvCC", $row, $col, $xfIndex, $value, $isError);
  707. $this->append($header . $data);
  708. return 0;
  709. }
  710. /**
  711. * Write a formula to the specified row and column (zero indexed).
  712. * The textual representation of the formula is passed to the parser in
  713. * Parser.php which returns a packed binary string.
  714. *
  715. * Returns 0 : normal termination
  716. * -1 : formula errors (bad formula)
  717. * -2 : row or column out of range
  718. *
  719. * @param integer $row Zero indexed row
  720. * @param integer $col Zero indexed column
  721. * @param string $formula The formula text string
  722. * @param mixed $xfIndex The XF format index
  723. * @param mixed $calculatedValue Calculated value
  724. * @return integer
  725. */
  726. private function writeFormula($row, $col, $formula, $xfIndex, $calculatedValue)
  727. {
  728. $record = 0x0006; // Record identifier
  729. // Initialize possible additional value for STRING record that should be written after the FORMULA record?
  730. $stringValue = null;
  731. // calculated value
  732. if (isset($calculatedValue)) {
  733. // Since we can't yet get the data type of the calculated value,
  734. // we use best effort to determine data type
  735. if (is_bool($calculatedValue)) {
  736. // Boolean value
  737. $num = pack('CCCvCv', 0x01, 0x00, (int)$calculatedValue, 0x00, 0x00, 0xFFFF);
  738. } elseif (is_int($calculatedValue) || is_float($calculatedValue)) {
  739. // Numeric value
  740. $num = pack('d', $calculatedValue);
  741. } elseif (is_string($calculatedValue)) {
  742. if (array_key_exists($calculatedValue, PHPExcel_Cell_DataType::getErrorCodes())) {
  743. // Error value
  744. $num = pack('CCCvCv', 0x02, 0x00, self::mapErrorCode($calculatedValue), 0x00, 0x00, 0xFFFF);
  745. } elseif ($calculatedValue === '') {
  746. // Empty string (and BIFF8)
  747. $num = pack('CCCvCv', 0x03, 0x00, 0x00, 0x00, 0x00, 0xFFFF);
  748. } else {
  749. // Non-empty string value (or empty string BIFF5)
  750. $stringValue = $calculatedValue;
  751. $num = pack('CCCvCv', 0x00, 0x00, 0x00, 0x00, 0x00, 0xFFFF);
  752. }
  753. } else {
  754. // We are really not supposed to reach here
  755. $num = pack('d', 0x00);
  756. }
  757. } else {
  758. $num = pack('d', 0x00);
  759. }
  760. $grbit = 0x03; // Option flags
  761. $unknown = 0x0000; // Must be zero
  762. // Strip the '=' or '@' sign at the beginning of the formula string
  763. if ($formula{0} == '=') {
  764. $formula = substr($formula, 1);
  765. } else {
  766. // Error handling
  767. $this->writeString($row, $col, 'Unrecognised character for formula');
  768. return -1;
  769. }
  770. // Parse the formula using the parser in Parser.php
  771. try {
  772. $error = $this->parser->parse($formula);
  773. $formula = $this->parser->toReversePolish();
  774. $formlen = strlen($formula); // Length of the binary string
  775. $length = 0x16 + $formlen; // Length of the record data
  776. $header = pack("vv", $record, $length);
  777. $data = pack("vvv", $row, $col, $xfIndex)
  778. . $num
  779. . pack("vVv", $grbit, $unknown, $formlen);
  780. $this->append($header . $data . $formula);
  781. // Append also a STRING record if necessary
  782. if ($stringValue !== null) {
  783. $this->writeStringRecord($stringValue);
  784. }
  785. return 0;
  786. } catch (PHPExcel_Exception $e) {
  787. // do nothing
  788. }
  789. }
  790. /**
  791. * Write a STRING record. This
  792. *
  793. * @param string $stringValue
  794. */
  795. private function writeStringRecord($stringValue)
  796. {
  797. $record = 0x0207; // Record identifier
  798. $data = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($stringValue);
  799. $length = strlen($data);
  800. $header = pack('vv', $record, $length);
  801. $this->append($header . $data);
  802. }
  803. /**
  804. * Write a hyperlink.
  805. * This is comprised of two elements: the visible label and
  806. * the invisible link. The visible label is the same as the link unless an
  807. * alternative string is specified. The label is written using the
  808. * writeString() method. Therefore the 255 characters string limit applies.
  809. * $string and $format are optional.
  810. *
  811. * The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external
  812. * directory url.
  813. *
  814. * Returns 0 : normal termination
  815. * -2 : row or column out of range
  816. * -3 : long string truncated to 255 chars
  817. *
  818. * @param integer $row Row
  819. * @param integer $col Column
  820. * @param string $url URL string
  821. * @return integer
  822. */
  823. private function writeUrl($row, $col, $url)
  824. {
  825. // Add start row and col to arg list
  826. return($this->writeUrlRange($row, $col, $row, $col, $url));
  827. }
  828. /**
  829. * This is the more general form of writeUrl(). It allows a hyperlink to be
  830. * written to a range of cells. This function also decides the type of hyperlink
  831. * to be written. These are either, Web (http, ftp, mailto), Internal
  832. * (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1').
  833. *
  834. * @access private
  835. * @see writeUrl()
  836. * @param integer $row1 Start row
  837. * @param integer $col1 Start column
  838. * @param integer $row2 End row
  839. * @param integer $col2 End column
  840. * @param string $url URL string
  841. * @return integer
  842. */
  843. public function writeUrlRange($row1, $col1, $row2, $col2, $url)
  844. {
  845. // Check for internal/external sheet links or default to web link
  846. if (preg_match('[^internal:]', $url)) {
  847. return($this->writeUrlInternal($row1, $col1, $row2, $col2, $url));
  848. }
  849. if (preg_match('[^external:]', $url)) {
  850. return($this->writeUrlExternal($row1, $col1, $row2, $col2, $url));
  851. }
  852. return($this->writeUrlWeb($row1, $col1, $row2, $col2, $url));
  853. }
  854. /**
  855. * Used to write http, ftp and mailto hyperlinks.
  856. * The link type ($options) is 0x03 is the same as absolute dir ref without
  857. * sheet. However it is differentiated by the $unknown2 data stream.
  858. *
  859. * @access private
  860. * @see writeUrl()
  861. * @param integer $row1 Start row
  862. * @param integer $col1 Start column
  863. * @param integer $row2 End row
  864. * @param integer $col2 End column
  865. * @param string $url URL string
  866. * @return integer
  867. */
  868. public function writeUrlWeb($row1, $col1, $row2, $col2, $url)
  869. {
  870. $record = 0x01B8; // Record identifier
  871. $length = 0x00000; // Bytes to follow
  872. // Pack the undocumented parts of the hyperlink stream
  873. $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
  874. $unknown2 = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B");
  875. // Pack the option flags
  876. $options = pack("V", 0x03);
  877. // Convert URL to a null terminated wchar string
  878. $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
  879. $url = $url . "\0\0\0";
  880. // Pack the length of the URL
  881. $url_len = pack("V", strlen($url));
  882. // Calculate the data length
  883. $length = 0x34 + strlen($url);
  884. // Pack the header data
  885. $header = pack("vv", $record, $length);
  886. $data = pack("vvvv", $row1, $row2, $col1, $col2);
  887. // Write the packed data
  888. $this->append($header . $data .
  889. $unknown1 . $options .
  890. $unknown2 . $url_len . $url);
  891. return 0;
  892. }
  893. /**
  894. * Used to write internal reference hyperlinks such as "Sheet1!A1".
  895. *
  896. * @access private
  897. * @see writeUrl()
  898. * @param integer $row1 Start row
  899. * @param integer $col1 Start column
  900. * @param integer $row2 End row
  901. * @param integer $col2 End column
  902. * @param string $url URL string
  903. * @return integer
  904. */
  905. public function writeUrlInternal($row1, $col1, $row2, $col2, $url)
  906. {
  907. $record = 0x01B8; // Record identifier
  908. $length = 0x00000; // Bytes to follow
  909. // Strip URL type
  910. $url = preg_replace('/^internal:/', '', $url);
  911. // Pack the undocumented parts of the hyperlink stream
  912. $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
  913. // Pack the option flags
  914. $options = pack("V", 0x08);
  915. // Convert the URL type and to a null terminated wchar string
  916. $url .= "\0";
  917. // character count
  918. $url_len = PHPExcel_Shared_String::CountCharacters($url);
  919. $url_len = pack('V', $url_len);
  920. $url = PHPExcel_Shared_String::ConvertEncoding($url, 'UTF-16LE', 'UTF-8');
  921. // Calculate the data length
  922. $length = 0x24 + strlen($url);
  923. // Pack the header data
  924. $header = pack("vv", $record, $length);
  925. $data = pack("vvvv", $row1, $row2, $col1, $col2);
  926. // Write the packed data
  927. $this->append($header . $data .
  928. $unknown1 . $options .
  929. $url_len . $url);
  930. return 0;
  931. }
  932. /**
  933. * Write links to external directory names such as 'c:\foo.xls',
  934. * c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'.
  935. *
  936. * Note: Excel writes some relative links with the $dir_long string. We ignore
  937. * these cases for the sake of simpler code.
  938. *
  939. * @access private
  940. * @see writeUrl()
  941. * @param integer $row1 Start row
  942. * @param integer $col1 Start column
  943. * @param integer $row2 End row
  944. * @param integer $col2 End column
  945. * @param string $url URL string
  946. * @return integer
  947. */
  948. public function writeUrlExternal($row1, $col1, $row2, $col2, $url)
  949. {
  950. // Network drives are different. We will handle them separately
  951. // MS/Novell network drives and shares start with \\
  952. if (preg_match('[^external:\\\\]', $url)) {
  953. return; //($this->writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format));
  954. }
  955. $record = 0x01B8; // Record identifier
  956. $length = 0x00000; // Bytes to follow
  957. // Strip URL type and change Unix dir separator to Dos style (if needed)
  958. //
  959. $url = preg_replace('/^external:/', '', $url);
  960. $url = preg_replace('/\//', "\\", $url);
  961. // Determine if the link is relative or absolute:
  962. // relative if link contains no dir separator, "somefile.xls"
  963. // relative if link starts with up-dir, "..\..\somefile.xls"
  964. // otherwise, absolute
  965. $absolute = 0x00; // relative path
  966. if (preg_match('/^[A-Z]:/', $url)) {
  967. $absolute = 0x02; // absolute path on Windows, e.g. C:\...
  968. }
  969. $link_type = 0x01 | $absolute;
  970. // Determine if the link contains a sheet reference and change some of the
  971. // parameters accordingly.
  972. // Split the dir name and sheet name (if it exists)
  973. $dir_long = $url;
  974. if (preg_match("/\#/", $url)) {
  975. $link_type |= 0x08;
  976. }
  977. // Pack the link type
  978. $link_type = pack("V", $link_type);
  979. // Calculate the up-level dir count e.g.. (..\..\..\ == 3)
  980. $up_count = preg_match_all("/\.\.\\\/", $dir_long, $useless);
  981. $up_count = pack("v", $up_count);
  982. // Store the short dos dir name (null terminated)
  983. $dir_short = preg_replace("/\.\.\\\/", '', $dir_long) . "\0";
  984. // Store the long dir name as a wchar string (non-null terminated)
  985. $dir_long = $dir_long . "\0";
  986. // Pack the lengths of the dir strings
  987. $dir_short_len = pack("V", strlen($dir_short));
  988. $dir_long_len = pack("V", strlen($dir_long));
  989. $stream_len = pack("V", 0); //strlen($dir_long) + 0x06);
  990. // Pack the undocumented parts of the hyperlink stream
  991. $unknown1 = pack("H*", 'D0C9EA79F9BACE118C8200AA004BA90B02000000');
  992. $unknown2 = pack("H*", '0303000000000000C000000000000046');
  993. $unknown3 = pack("H*", 'FFFFADDE000000000000000000000000000000000000000');
  994. $unknown4 = pack("v", 0x03);
  995. // Pack the main data stream
  996. $data = pack("vvvv", $row1, $row2, $col1, $col2) .
  997. $unknown1 .
  998. $link_type .
  999. $unknown2 .
  1000. $up_count .
  1001. $dir_short_len.
  1002. $dir_short .
  1003. $unknown3 .
  1004. $stream_len ;/*.
  1005. $dir_long_len .
  1006. $unknown4 .
  1007. $dir_long .
  1008. $sheet_len .
  1009. $sheet ;*/
  1010. // Pack the header data
  1011. $length = strlen($data);
  1012. $header = pack("vv", $record, $length);
  1013. // Write the packed data
  1014. $this->append($header. $data);
  1015. return 0;
  1016. }
  1017. /**
  1018. * This method is used to set the height and format for a row.
  1019. *
  1020. * @param integer $row The row to set
  1021. * @param integer $height Height we are giving to the row.
  1022. * Use null to set XF without setting height
  1023. * @param integer $xfIndex The optional cell style Xf index to apply to the columns
  1024. * @param bool $hidden The optional hidden attribute
  1025. * @param integer $level The optional outline level for row, in range [0,7]
  1026. */
  1027. private function writeRow($row, $height, $xfIndex, $hidden = false, $level = 0)
  1028. {
  1029. $record = 0x0208; // Record identifier
  1030. $length = 0x0010; // Number of bytes to follow
  1031. $colMic = 0x0000; // First defined column
  1032. $colMac = 0x0000; // Last defined column
  1033. $irwMac = 0x0000; // Used by Excel to optimise loading
  1034. $reserved = 0x0000; // Reserved
  1035. $grbit = 0x0000; // Option flags
  1036. $ixfe = $xfIndex;
  1037. if ($height < 0) {
  1038. $height = null;
  1039. }
  1040. // Use writeRow($row, null, $XF) to set XF format without setting height
  1041. if ($height != null) {
  1042. $miyRw = $height * 20; // row height
  1043. } else {
  1044. $miyRw = 0xff; // default row height is 256
  1045. }
  1046. // Set the options flags. fUnsynced is used to show that the font and row
  1047. // heights are not compatible. This is usually the case for WriteExcel.
  1048. // The collapsed flag 0x10 doesn't seem to be used to indicate that a row
  1049. // is collapsed. Instead it is used to indicate that the previous row is
  1050. // collapsed. The zero height flag, 0x20, is used to collapse a row.
  1051. $grbit |= $level;
  1052. if ($hidden) {
  1053. $grbit |= 0x0030;
  1054. }
  1055. if ($height !== null) {
  1056. $grbit |= 0x0040; // fUnsynced
  1057. }
  1058. if ($xfIndex !== 0xF) {
  1059. $grbit |= 0x0080;
  1060. }
  1061. $grbit |= 0x0100;
  1062. $header = pack("vv", $record, $length);
  1063. $data = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw, $irwMac, $reserved, $grbit, $ixfe);
  1064. $this->append($header.$data);
  1065. }
  1066. /**
  1067. * Writes Excel DIMENSIONS to define the area in which there is data.
  1068. */
  1069. private function writeDimensions()
  1070. {
  1071. $record = 0x0200; // Record identifier
  1072. $length = 0x000E;
  1073. $data = pack('VVvvv', $this->firstRowIndex, $this->lastRowIndex + 1, $this->firstColumnIndex, $this->lastColumnIndex + 1, 0x0000); // reserved
  1074. $header = pack("vv", $record, $length);
  1075. $this->append($header.$data);
  1076. }
  1077. /**
  1078. * Write BIFF record Window2.
  1079. */
  1080. private function writeWindow2()
  1081. {
  1082. $record = 0x023E; // Record identifier
  1083. $length = 0x0012;
  1084. $grbit = 0x00B6; // Option flags
  1085. $rwTop = 0x0000; // Top row visible in window
  1086. $colLeft = 0x0000; // Leftmost column visible in window
  1087. // The options flags that comprise $grbit
  1088. $fDspFmla = 0; // 0 - bit
  1089. $fDspGrid = $this->phpSheet->getShowGridlines() ? 1 : 0; // 1
  1090. $fDspRwCol = $this->phpSheet->getShowRowColHeaders() ? 1 : 0; // 2
  1091. $fFrozen = $this->phpSheet->getFreezePane() ? 1 : 0; // 3
  1092. $fDspZeros = 1; // 4
  1093. $fDefaultHdr = 1; // 5
  1094. $fArabic = $this->phpSheet->getRightToLeft() ? 1 : 0; // 6
  1095. $fDspGuts = $this->outlineOn; // 7
  1096. $fFrozenNoSplit = 0; // 0 - bit
  1097. // no support in PHPExcel for selected sheet, therefore sheet is only selected if it is the active sheet
  1098. $fSelected = ($this->phpSheet === $this->phpSheet->getParent()->getActiveSheet()) ? 1 : 0;
  1099. $fPaged = 1; // 2
  1100. $fPageBreakPreview = $this->phpSheet->getSheetView()->getView() === PHPExcel_Worksheet_SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW;
  1101. $grbit = $fDspFmla;
  1102. $grbit |= $fDspGrid << 1;
  1103. $grbit |= $fDspRwCol << 2;
  1104. $grbit |= $fFrozen << 3;
  1105. $grbit |= $fDspZeros << 4;
  1106. $grbit |= $fDefaultHdr << 5;
  1107. $grbit |= $fArabic << 6;
  1108. $grbit |= $fDspGuts << 7;
  1109. $grbit |= $fFrozenNoSplit << 8;
  1110. $grbit |= $fSelected << 9;
  1111. $grbit |= $fPaged << 10;
  1112. $grbit |= $fPageBreakPreview << 11;
  1113. $header = pack("vv", $record, $length);
  1114. $data = pack("vvv", $grbit, $rwTop, $colLeft);
  1115. // FIXME !!!
  1116. $rgbHdr = 0x0040; // Row/column heading and gridline color index
  1117. $zoom_factor_page_break = ($fPageBreakPreview ? $this->phpSheet->getSheetView()->getZoomScale() : 0x0000);
  1118. $zoom_factor_normal = $this->phpSheet->getSheetView()->getZoomScaleNormal();
  1119. $data .= pack("vvvvV", $rgbHdr, 0x0000, $zoom_factor_page_break, $zoom_factor_normal, 0x00000000);
  1120. $this->append($header.$data);
  1121. }
  1122. /**
  1123. * Write BIFF record DEFAULTROWHEIGHT.
  1124. */
  1125. private function writeDefaultRowHeight()
  1126. {
  1127. $defaultRowHeight = $this->phpSheet->getDefaultRowDimension()->getRowHeight();
  1128. if ($defaultRowHeight < 0) {
  1129. return;
  1130. }
  1131. // convert to twips
  1132. $defaultRowHeight = (int) 20 * $defaultRowHeight;
  1133. $record = 0x0225; // Record identifier
  1134. $length = 0x0004; // Number of bytes to follow
  1135. $header = pack("vv", $record, $length);
  1136. $data = pack("vv", 1, $defaultRowHeight);
  1137. $this->append($header . $data);
  1138. }
  1139. /**
  1140. * Write BIFF record DEFCOLWIDTH if COLINFO records are in use.
  1141. */
  1142. private function writeDefcol()
  1143. {
  1144. $defaultColWidth = 8;
  1145. $record = 0x0055; // Record identifier
  1146. $length = 0x0002; // Number of bytes to follow
  1147. $header = pack("vv", $record, $length);
  1148. $data = pack("v", $defaultColWidth);
  1149. $this->append($header . $data);
  1150. }
  1151. /**
  1152. * Write BIFF record COLINFO to define column widths
  1153. *
  1154. * Note: The SDK says the record length is 0x0B but Excel writes a 0x0C
  1155. * length record.
  1156. *
  1157. * @param array $col_array This is the only parameter received and is composed of the following:
  1158. * 0 => First formatted column,
  1159. * 1 => Last formatted column,
  1160. * 2 => Col width (8.43 is Excel default),
  1161. * 3 => The optional XF format of the column,
  1162. * 4 => Option flags.
  1163. * 5 => Optional outline level
  1164. */
  1165. private function writeColinfo($col_array)
  1166. {
  1167. if (isset($col_array[0])) {
  1168. $colFirst = $col_array[0];
  1169. }
  1170. if (isset($col_array[1])) {
  1171. $colLast = $col_array[1];
  1172. }
  1173. if (isset($col_array[2])) {
  1174. $coldx = $col_array[2];
  1175. } else {
  1176. $coldx = 8.43;
  1177. }
  1178. if (isset($col_array[3])) {
  1179. $xfIndex = $col_array[3];
  1180. } else {
  1181. $xfIndex = 15;
  1182. }
  1183. if (isset($col_array[4])) {
  1184. $grbit = $col_array[4];
  1185. } else {
  1186. $grbit = 0;
  1187. }
  1188. if (isset($col_array[5])) {
  1189. $level = $col_array[5];
  1190. } else {
  1191. $level = 0;
  1192. }
  1193. $record = 0x007D; // Record identifier
  1194. $length = 0x000C; // Number of bytes to follow
  1195. $coldx *= 256; // Convert to units of 1/256 of a char
  1196. $ixfe = $xfIndex;
  1197. $reserved = 0x0000; // Reserved
  1198. $level = max(0, min($level, 7));
  1199. $grbit |= $level << 8;
  1200. $header = pack("vv", $record, $length);
  1201. $data = pack("vvvvvv", $colFirst, $colLast, $coldx, $ixfe, $grbit, $reserved);
  1202. $this->append($header.$data);
  1203. }
  1204. /**
  1205. * Write BIFF record SELECTION.
  1206. */
  1207. private function writeSelection()
  1208. {
  1209. // look up the selected cell range
  1210. $selectedCells = $this->phpSheet->getSelectedCells();
  1211. $selectedCells = PHPExcel_Cell::splitRange($this->phpSheet->getSelectedCells());
  1212. $selectedCells = $selectedCells[0];
  1213. if (count($selectedCells) == 2) {
  1214. list($first, $last) = $selectedCells;
  1215. } else {
  1216. $first = $selectedCells[0];
  1217. $last = $selectedCells[0];
  1218. }
  1219. list($colFirst, $rwFirst) = PHPExcel_Cell::coordinateFromString($first);
  1220. $colFirst = PHPExcel_Cell::columnIndexFromString($colFirst) - 1; // base 0 column index
  1221. --$rwFirst; // base 0 row index
  1222. list($colLast, $rwLast) = PHPExcel_Cell::coordinateFromString($last);
  1223. $colLast = PHPExcel_Cell::columnIndexFromString($colLast) - 1; // base 0 column index
  1224. --$rwLast; // base 0 row index
  1225. // make sure we are not out of bounds
  1226. $colFirst = min($colFirst, 255);
  1227. $colLast = min($colLast, 255);
  1228. $rwFirst = min($rwFirst, 65535);
  1229. $rwLast = min($rwLast, 65535);
  1230. $record = 0x001D; // Record identifier
  1231. $length = 0x000F; // Number of bytes to follow
  1232. $pnn = $this->activePane; // Pane position
  1233. $rwAct = $rwFirst; // Active row
  1234. $colAct = $colFirst; // Active column
  1235. $irefAct = 0; // Active cell ref
  1236. $cref = 1; // Number of refs
  1237. if (!isset($rwLast)) {
  1238. $rwLast = $rwFirst; // Last row in reference
  1239. }
  1240. if (!isset($colLast)) {
  1241. $colLast = $colFirst; // Last col in reference
  1242. }
  1243. // Swap last row/col for first row/col as necessary
  1244. if ($rwFirst > $rwLast) {
  1245. list($rwFirst, $rwLast) = array($rwLast, $rwFirst);
  1246. }
  1247. if ($colFirst > $colLast) {
  1248. list($colFirst, $colLast) = array($colLast, $colFirst);
  1249. }
  1250. $header = pack("vv", $record, $length);
  1251. $data = pack("CvvvvvvCC", $pnn, $rwAct, $colAct, $irefAct, $cref, $rwFirst, $rwLast, $colFirst, $colLast);
  1252. $this->append($header . $data);
  1253. }
  1254. /**
  1255. * Store the MERGEDCELLS records for all ranges of merged cells
  1256. */
  1257. private function writeMergedCells()
  1258. {
  1259. $mergeCells = $this->phpSheet->getMergeCells();
  1260. $countMergeCells = count($mergeCells);
  1261. if ($countMergeCells == 0) {
  1262. return;
  1263. }
  1264. // maximum allowed number of merged cells per record
  1265. $maxCountMergeCellsPerRecord = 1027;
  1266. // record identifier
  1267. $record = 0x00E5;
  1268. // counter for total number of merged cells treated so far by the writer
  1269. $i = 0;
  1270. // counter for number of merged cells written in record currently being written
  1271. $j = 0;
  1272. // initialize record data
  1273. $recordData = '';
  1274. // loop through the merged cells
  1275. foreach ($mergeCells as $mergeCell) {
  1276. ++$i;
  1277. ++$j;
  1278. // extract the row and column indexes
  1279. $range = PHPExcel_Cell::splitRange($mergeCell);
  1280. list($first, $last) = $range[0];
  1281. list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($first);
  1282. list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($last);
  1283. $recordData .= pack('vvvv', $firstRow - 1, $lastRow - 1, PHPExcel_Cell::columnIndexFromString($firstColumn) - 1, PHPExcel_Cell::columnIndexFromString($lastColumn) - 1);
  1284. // flush record if we have reached limit for number of merged cells, or reached final merged cell
  1285. if ($j == $maxCountMergeCellsPerRecord or $i == $countMergeCells) {
  1286. $recordData = pack('v', $j) . $recordData;
  1287. $length = strlen($recordData);
  1288. $header = pack('vv', $record, $length);
  1289. $this->append($header . $recordData);
  1290. // initialize for next record, if any
  1291. $recordData = '';
  1292. $j = 0;
  1293. }
  1294. }
  1295. }
  1296. /**
  1297. * Write SHEETLAYOUT record
  1298. */
  1299. private function writeSheetLayout()
  1300. {
  1301. if (!$this->phpSheet->isTabColorSet()) {
  1302. return;
  1303. }
  1304. $recordData = pack(
  1305. 'vvVVVvv',
  1306. 0x0862,
  1307. 0x0000, // unused
  1308. 0x00000000, // unused
  1309. 0x00000000, // unused
  1310. 0x00000014, // size of record data
  1311. $this->colors[$this->phpSheet->getTabColor()->getRGB()], // color index
  1312. 0x0000 // unused
  1313. );
  1314. $length = strlen($recordData);
  1315. $record = 0x0862; // Record identifier
  1316. $header = pack('vv', $record, $length);
  1317. $this->append($header . $recordData);
  1318. }
  1319. /**
  1320. * Write SHEETPROTECTION
  1321. */
  1322. private function writeSheetProtection()
  1323. {
  1324. // record identifier
  1325. $record = 0x0867;
  1326. // prepare options
  1327. $options = (int) !$this->phpSheet->getProtection()->getObjects()
  1328. | (int) !$this->phpSheet->getProtection()->getScenarios() << 1
  1329. | (int) !$this->phpSheet->getProtection()->getFormatCells() << 2
  1330. | (int) !$this->phpSheet->getProtection()->getFormatColumns() << 3
  1331. | (int) !$this->phpSheet->getProtection()->getFormatRows() << 4
  1332. | (int) !$this->phpSheet->getProtection()->getInsertColumns() << 5
  1333. | (int) !$this->phpSheet->getProtection()->getInsertRows() << 6
  1334. | (int) !$this->phpSheet->getProtection()->getInsertHyperlinks() << 7
  1335. | (int) !$this->phpSheet->getProtection()->getDeleteColumns() << 8
  1336. | (int) !$this->phpSheet->getProtection()->getDeleteRows() << 9
  1337. | (int) !$this->phpSheet->getProtection()->getSelectLockedCells() << 10
  1338. | (int) !$this->phpSheet->getProtection()->getSort() << 11
  1339. | (int) !$this->phpSheet->getProtection()->getAutoFilter() << 12
  1340. | (int) !$this->phpSheet->getProtection()->getPivotTables() << 13
  1341. | (int) !$this->phpSheet->getProtection()->getSelectUnlockedCells() << 14 ;
  1342. // record data
  1343. $recordData = pack(
  1344. 'vVVCVVvv',
  1345. 0x0867, // repeated record identifier
  1346. 0x0000, // not used
  1347. 0x0000, // not used
  1348. 0x00, // not used
  1349. 0x01000200, // unknown data
  1350. 0xFFFFFFFF, // unknown data
  1351. $options, // options
  1352. 0x0000 // not used
  1353. );
  1354. $length = strlen($recordData);
  1355. $header = pack('vv', $record, $length);
  1356. $this->append($header . $recordData);
  1357. }
  1358. /**
  1359. * Write BIFF record RANGEPROTECTION
  1360. *
  1361. * Openoffice.org's Documentaion of the Microsoft Excel File Format uses term RANGEPROTECTION for these records
  1362. * Microsoft Office Excel 97-2007 Binary File Format Specification uses term FEAT for these records
  1363. */
  1364. private function writeRangeProtection()
  1365. {
  1366. foreach ($this->phpSheet->getProtectedCells() as $range => $password) {
  1367. // number of ranges, e.g. 'A1:B3 C20:D25'
  1368. $cellRanges = explode(' ', $range);
  1369. $cref = count($cellRanges);
  1370. $recordData = pack(
  1371. 'vvVVvCVvVv',
  1372. 0x0868,
  1373. 0x00,
  1374. 0x0000,
  1375. 0x0000,
  1376. 0x02,
  1377. 0x0,
  1378. 0x0000,
  1379. $cref,
  1380. 0x0000,
  1381. 0x00
  1382. );
  1383. foreach ($cellRanges as $cellRange) {
  1384. $recordData .= $this->writeBIFF8CellRangeAddressFixed($cellRange);
  1385. }
  1386. // the rgbFeat structure
  1387. $recordData .= pack(
  1388. 'VV',
  1389. 0x0000,
  1390. hexdec($password)
  1391. );
  1392. $recordData .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong('p' . md5($recordData));
  1393. $length = strlen($recordData);
  1394. $record = 0x0868; // Record identifier
  1395. $header = pack("vv", $record, $length);
  1396. $this->append($header . $recordData);
  1397. }
  1398. }
  1399. /**
  1400. * Write BIFF record EXTERNCOUNT to indicate the number of external sheet
  1401. * references in a worksheet.
  1402. *
  1403. * Excel only stores references to external sheets that are used in formulas.
  1404. * For simplicity we store references to all the sheets in the workbook
  1405. * regardless of whether they are used or not. This reduces the overall
  1406. * complexity and eliminates the need for a two way dialogue between the formula
  1407. * parser the worksheet objects.
  1408. *
  1409. * @param integer $count The number of external sheet references in this worksheet
  1410. */
  1411. private function writeExterncount($count)
  1412. {
  1413. $record = 0x0016; // Record identifier
  1414. $length = 0x0002; // Number of bytes to follow
  1415. $header = pack("vv", $record, $length);
  1416. $data = pack("v", $count);
  1417. $this->append($header . $data);
  1418. }
  1419. /**
  1420. * Writes the Excel BIFF EXTERNSHEET record. These references are used by
  1421. * formulas. A formula references a sheet name via an index. Since we store a
  1422. * reference to all of the external worksheets the EXTERNSHEET index is the same
  1423. * as the worksheet index.
  1424. *
  1425. * @param string $sheetname The name of a external worksheet
  1426. */
  1427. private function writeExternsheet($sheetname)
  1428. {
  1429. $record = 0x0017; // Record identifier
  1430. // References to the current sheet are encoded differently to references to
  1431. // external sheets.
  1432. //
  1433. if ($this->phpSheet->getTitle() == $sheetname) {
  1434. $sheetname = '';
  1435. $length = 0x02; // The following 2 bytes
  1436. $cch = 1; // The following byte
  1437. $rgch = 0x02; // Self reference
  1438. } else {
  1439. $length = 0x02 + strlen($sheetname);
  1440. $cch = strlen($sheetname);
  1441. $rgch = 0x03; // Reference to a sheet in the current workbook
  1442. }
  1443. $header = pack("vv", $record, $length);
  1444. $data = pack("CC", $cch, $rgch);
  1445. $this->append($header . $data . $sheetname);
  1446. }
  1447. /**
  1448. * Writes the Excel BIFF PANE record.
  1449. * The panes can either be frozen or thawed (unfrozen).
  1450. * Frozen panes are specified in terms of an integer number of rows and columns.
  1451. * Thawed panes are specified in terms of Excel's units for rows and columns.
  1452. */
  1453. private function writePanes()
  1454. {
  1455. $panes = array();
  1456. if ($freezePane = $this->phpSheet->getFreezePane()) {
  1457. list($column, $row) = PHPExcel_Cell::coordinateFromString($freezePane);
  1458. $panes[0] = $row - 1;
  1459. $panes[1] = PHPExcel_Cell::columnIndexFromString($column) - 1;
  1460. } else {
  1461. // thaw panes
  1462. return;
  1463. }
  1464. $y = isset($panes[0]) ? $panes[0] : null;
  1465. $x = isset($panes[1]) ? $panes[1] : null;
  1466. $rwTop = isset($panes[2]) ? $panes[2] : null;
  1467. $colLeft = isset($panes[3]) ? $panes[3] : null;
  1468. if (count($panes) > 4) { // if Active pane was received
  1469. $pnnAct = $panes[4];
  1470. } else {
  1471. $pnnAct = null;
  1472. }
  1473. $record = 0x0041; // Record identifier
  1474. $length = 0x000A; // Number of bytes to follow
  1475. // Code specific to frozen or thawed panes.
  1476. if ($this->phpSheet->getFreezePane()) {
  1477. // Set default values for $rwTop and $colLeft
  1478. if (!isset($rwTop)) {
  1479. $rwTop = $y;
  1480. }
  1481. if (!isset($colLeft)) {
  1482. $colLeft = $x;
  1483. }
  1484. } else {
  1485. // Set default values for $rwTop and $colLeft
  1486. if (!isset($rwTop)) {
  1487. $rwTop = 0;
  1488. }
  1489. if (!isset($colLeft)) {
  1490. $colLeft = 0;
  1491. }
  1492. // Convert Excel's row and column units to the internal units.
  1493. // The default row height is 12.75
  1494. // The default column width is 8.43
  1495. // The following slope and intersection values were interpolated.
  1496. //
  1497. $y = 20*$y + 255;
  1498. $x = 113.879*$x + 390;
  1499. }
  1500. // Determine which pane should be active. There is also the undocumented
  1501. // option to override this should it be necessary: may be removed later.
  1502. //
  1503. if (!isset($pnnAct)) {
  1504. if ($x != 0 && $y != 0) {
  1505. $pnnAct = 0; // Bottom right
  1506. }
  1507. if ($x != 0 && $y == 0) {
  1508. $pnnAct = 1; // Top right
  1509. }
  1510. if ($x == 0 && $y != 0) {
  1511. $pnnAct = 2; // Bottom left
  1512. }
  1513. if ($x == 0 && $y == 0) {
  1514. $pnnAct = 3; // Top left
  1515. }
  1516. }
  1517. $this->activePane = $pnnAct; // Used in writeSelection
  1518. $header = pack("vv", $record, $length);
  1519. $data = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct);
  1520. $this->append($header . $data);
  1521. }
  1522. /**
  1523. * Store the page setup SETUP BIFF record.
  1524. */
  1525. private function writeSetup()
  1526. {
  1527. $record = 0x00A1; // Record identifier
  1528. $length = 0x0022; // Number of bytes to follow
  1529. $iPaperSize = $this->phpSheet->getPageSetup()->getPaperSize(); // Paper size
  1530. $iScale = $this->phpSheet->getPageSetup()->getScale() ?
  1531. $this->phpSheet->getPageSetup()->getScale() : 100; // Print scaling factor
  1532. $iPageStart = 0x01; // Starting page number
  1533. $iFitWidth = (int) $this->phpSheet->getPageSetup()->getFitToWidth(); // Fit to number of pages wide
  1534. $iFitHeight = (int) $this->phpSheet->getPageSetup()->getFitToHeight(); // Fit to number of pages high
  1535. $grbit = 0x00; // Option flags
  1536. $iRes = 0x0258; // Print resolution
  1537. $iVRes = 0x0258; // Vertical print resolution
  1538. $numHdr = $this->phpSheet->getPageMargins()->getHeader(); // Header Margin
  1539. $numFtr = $this->phpSheet->getPageMargins()->getFooter(); // Footer Margin
  1540. $iCopies = 0x01; // Number of copies
  1541. $fLeftToRight = 0x0; // Print over then down
  1542. // Page orientation
  1543. $fLandscape = ($this->phpSheet->getPageSetup()->getOrientation() == PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE) ?
  1544. 0x0 : 0x1;
  1545. $fNoPls = 0x0; // Setup not read from printer
  1546. $fNoColor = 0x0; // Print black and white
  1547. $fDraft = 0x0; // Print draft quality
  1548. $fNotes = 0x0; // Print notes
  1549. $fNoOrient = 0x0; // Orientation not set
  1550. $fUsePage = 0x0; // Use custom starting page
  1551. $grbit = $fLeftToRight;
  1552. $grbit |= $fLandscape << 1;
  1553. $grbit |= $fNoPls << 2;
  1554. $grbit |= $fNoColor << 3;
  1555. $grbit |= $fDraft << 4;
  1556. $grbit |= $fNotes << 5;
  1557. $grbit |= $fNoOrient << 6;
  1558. $grbit |= $fUsePage << 7;
  1559. $numHdr = pack("d", $numHdr);
  1560. $numFtr = pack("d", $numFtr);
  1561. if (self::getByteOrder()) { // if it's Big Endian
  1562. $numHdr = strrev($numHdr);
  1563. $numFtr = strrev($numFtr);
  1564. }
  1565. $header = pack("vv", $record, $length);
  1566. $data1 = pack("vvvvvvvv", $iPaperSize, $iScale, $iPageStart, $iFitWidth, $iFitHeight, $grbit, $iRes, $iVRes);
  1567. $data2 = $numHdr.$numFtr;
  1568. $data3 = pack("v", $iCopies);
  1569. $this->append($header . $data1 . $data2 . $data3);
  1570. }
  1571. /**
  1572. * Store the header caption BIFF record.
  1573. */
  1574. private function writeHeader()
  1575. {
  1576. $record = 0x0014; // Record identifier
  1577. /* removing for now
  1578. // need to fix character count (multibyte!)
  1579. if (strlen($this->phpSheet->getHeaderFooter()->getOddHeader()) <= 255) {
  1580. $str = $this->phpSheet->getHeaderFooter()->getOddHeader(); // header string
  1581. } else {
  1582. $str = '';
  1583. }
  1584. */
  1585. $recordData = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($this->phpSheet->getHeaderFooter()->getOddHeader());
  1586. $length = strlen($recordData);
  1587. $header = pack("vv", $record, $length);
  1588. $this->append($header . $recordData);
  1589. }
  1590. /**
  1591. * Store the footer caption BIFF record.
  1592. */
  1593. private function writeFooter()
  1594. {
  1595. $record = 0x0015; // Record identifier
  1596. /* removing for now
  1597. // need to fix character count (multibyte!)
  1598. if (strlen($this->phpSheet->getHeaderFooter()->getOddFooter()) <= 255) {
  1599. $str = $this->phpSheet->getHeaderFooter()->getOddFooter();
  1600. } else {
  1601. $str = '';
  1602. }
  1603. */
  1604. $recordData = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($this->phpSheet->getHeaderFooter()->getOddFooter());
  1605. $length = strlen($recordData);
  1606. $header = pack("vv", $record, $length);
  1607. $this->append($header . $recordData);
  1608. }
  1609. /**
  1610. * Store the horizontal centering HCENTER BIFF record.
  1611. *
  1612. * @access private
  1613. */
  1614. private function writeHcenter()
  1615. {
  1616. $record = 0x0083; // Record identifier
  1617. $length = 0x0002; // Bytes to follow
  1618. $fHCenter = $this->phpSheet->getPageSetup()->getHorizontalCentered() ? 1 : 0; // Horizontal centering
  1619. $header = pack("vv", $record, $length);
  1620. $data = pack("v", $fHCenter);
  1621. $this->append($header.$data);
  1622. }
  1623. /**
  1624. * Store the vertical centering VCENTER BIFF record.
  1625. */
  1626. private function writeVcenter()
  1627. {
  1628. $record = 0x0084; // Record identifier
  1629. $length = 0x0002; // Bytes to follow
  1630. $fVCenter = $this->phpSheet->getPageSetup()->getVerticalCentered() ? 1 : 0; // Horizontal centering
  1631. $header = pack("vv", $record, $length);
  1632. $data = pack("v", $fVCenter);
  1633. $this->append($header . $data);
  1634. }
  1635. /**
  1636. * Store the LEFTMARGIN BIFF record.
  1637. */
  1638. private function writeMarginLeft()
  1639. {
  1640. $record = 0x0026; // Record identifier
  1641. $length = 0x0008; // Bytes to follow
  1642. $margin = $this->phpSheet->getPageMargins()->getLeft(); // Margin in inches
  1643. $header = pack("vv", $record, $length);
  1644. $data = pack("d", $margin);
  1645. if (self::getByteOrder()) { // if it's Big Endian
  1646. $data = strrev($data);
  1647. }
  1648. $this->append($header . $data);
  1649. }
  1650. /**
  1651. * Store the RIGHTMARGIN BIFF record.
  1652. */
  1653. private function writeMarginRight()
  1654. {
  1655. $record = 0x0027; // Record identifier
  1656. $length = 0x0008; // Bytes to follow
  1657. $margin = $this->phpSheet->getPageMargins()->getRight(); // Margin in inches
  1658. $header = pack("vv", $record, $length);
  1659. $data = pack("d", $margin);
  1660. if (self::getByteOrder()) { // if it's Big Endian
  1661. $data = strrev($data);
  1662. }
  1663. $this->append($header . $data);
  1664. }
  1665. /**
  1666. * Store the TOPMARGIN BIFF record.
  1667. */
  1668. private function writeMarginTop()
  1669. {
  1670. $record = 0x0028; // Record identifier
  1671. $length = 0x0008; // Bytes to follow
  1672. $margin = $this->phpSheet->getPageMargins()->getTop(); // Margin in inches
  1673. $header = pack("vv", $record, $length);
  1674. $data = pack("d", $margin);
  1675. if (self::getByteOrder()) { // if it's Big Endian
  1676. $data = strrev($data);
  1677. }
  1678. $this->append($header . $data);
  1679. }
  1680. /**
  1681. * Store the BOTTOMMARGIN BIFF record.
  1682. */
  1683. private function writeMarginBottom()
  1684. {
  1685. $record = 0x0029; // Record identifier
  1686. $length = 0x0008; // Bytes to follow
  1687. $margin = $this->phpSheet->getPageMargins()->getBottom(); // Margin in inches
  1688. $header = pack("vv", $record, $length);
  1689. $data = pack("d", $margin);
  1690. if (self::getByteOrder()) { // if it's Big Endian
  1691. $data = strrev($data);
  1692. }
  1693. $this->append($header . $data);
  1694. }
  1695. /**
  1696. * Write the PRINTHEADERS BIFF record.
  1697. */
  1698. private function writePrintHeaders()
  1699. {
  1700. $record = 0x002a; // Record identifier
  1701. $length = 0x0002; // Bytes to follow
  1702. $fPrintRwCol = $this->_print_headers; // Boolean flag
  1703. $header = pack("vv", $record, $length);
  1704. $data = pack("v", $fPrintRwCol);
  1705. $this->append($header . $data);
  1706. }
  1707. /**
  1708. * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the
  1709. * GRIDSET record.
  1710. */
  1711. private function writePrintGridlines()
  1712. {
  1713. $record = 0x002b; // Record identifier
  1714. $length = 0x0002; // Bytes to follow
  1715. $fPrintGrid = $this->phpSheet->getPrintGridlines() ? 1 : 0; // Boolean flag
  1716. $header = pack("vv", $record, $length);
  1717. $data = pack("v", $fPrintGrid);
  1718. $this->append($header . $data);
  1719. }
  1720. /**
  1721. * Write the GRIDSET BIFF record. Must be used in conjunction with the
  1722. * PRINTGRIDLINES record.
  1723. */
  1724. private function writeGridset()
  1725. {
  1726. $record = 0x0082; // Record identifier
  1727. $length = 0x0002; // Bytes to follow
  1728. $fGridSet = !$this->phpSheet->getPrintGridlines(); // Boolean flag
  1729. $header = pack("vv", $record, $length);
  1730. $data = pack("v", $fGridSet);
  1731. $this->append($header . $data);
  1732. }
  1733. /**
  1734. * Write the AUTOFILTERINFO BIFF record. This is used to configure the number of autofilter select used in the sheet.
  1735. */
  1736. private function writeAutoFilterInfo()
  1737. {
  1738. $record = 0x009D; // Record identifier
  1739. $length = 0x0002; // Bytes to follow
  1740. $rangeBounds = PHPExcel_Cell::rangeBoundaries($this->phpSheet->getAutoFilter()->getRange());
  1741. $iNumFilters = 1 + $rangeBounds[1][0] - $rangeBounds[0][0];
  1742. $header = pack("vv", $record, $length);
  1743. $data = pack("v", $iNumFilters);
  1744. $this->append($header . $data);
  1745. }
  1746. /**
  1747. * Write the GUTS BIFF record. This is used to configure the gutter margins
  1748. * where Excel outline symbols are displayed. The visibility of the gutters is
  1749. * controlled by a flag in WSBOOL.
  1750. *
  1751. * @see writeWsbool()
  1752. */
  1753. private function writeGuts()
  1754. {
  1755. $record = 0x0080; // Record identifier
  1756. $length = 0x0008; // Bytes to follow
  1757. $dxRwGut = 0x0000; // Size of row gutter
  1758. $dxColGut = 0x0000; // Size of col gutter
  1759. // determine maximum row outline level
  1760. $maxRowOutlineLevel = 0;
  1761. foreach ($this->phpSheet->getRowDimensions() as $rowDimension) {
  1762. $maxRowOutlineLevel = max($maxRowOutlineLevel, $rowDimension->getOutlineLevel());
  1763. }
  1764. $col_level = 0;
  1765. // Calculate the maximum column outline level. The equivalent calculation
  1766. // for the row outline level is carried out in writeRow().
  1767. $colcount = count($this->columnInfo);
  1768. for ($i = 0; $i < $colcount; ++$i) {
  1769. $col_level = max($this->columnInfo[$i][5], $col_level);
  1770. }
  1771. // Set the limits for the outline levels (0 <= x <= 7).
  1772. $col_level = max(0, min($col_level, 7));
  1773. // The displayed level is one greater than the max outline levels
  1774. if ($maxRowOutlineLevel) {
  1775. ++$maxRowOutlineLevel;
  1776. }
  1777. if ($col_level) {
  1778. ++$col_level;
  1779. }
  1780. $header = pack("vv", $record, $length);
  1781. $data = pack("vvvv", $dxRwGut, $dxColGut, $maxRowOutlineLevel, $col_level);
  1782. $this->append($header.$data);
  1783. }
  1784. /**
  1785. * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction
  1786. * with the SETUP record.
  1787. */
  1788. private function writeWsbool()
  1789. {
  1790. $record = 0x0081; // Record identifier
  1791. $length = 0x0002; // Bytes to follow
  1792. $grbit = 0x0000;
  1793. // The only option that is of interest is the flag for fit to page. So we
  1794. // set all the options in one go.
  1795. //
  1796. // Set the option flags
  1797. $grbit |= 0x0001; // Auto page breaks visible
  1798. if ($this->outlineStyle) {
  1799. $grbit |= 0x0020; // Auto outline styles
  1800. }
  1801. if ($this->phpSheet->getShowSummaryBelow()) {
  1802. $grbit |= 0x0040; // Outline summary below
  1803. }
  1804. if ($this->phpSheet->getShowSummaryRight()) {
  1805. $grbit |= 0x0080; // Outline summary right
  1806. }
  1807. if ($this->phpSheet->getPageSetup()->getFitToPage()) {
  1808. $grbit |= 0x0100; // Page setup fit to page
  1809. }
  1810. if ($this->outlineOn) {
  1811. $grbit |= 0x0400; // Outline symbols displayed
  1812. }
  1813. $header = pack("vv", $record, $length);
  1814. $data = pack("v", $grbit);
  1815. $this->append($header . $data);
  1816. }
  1817. /**
  1818. * Write the HORIZONTALPAGEBREAKS and VERTICALPAGEBREAKS BIFF records.
  1819. */
  1820. private function writeBreaks()
  1821. {
  1822. // initialize
  1823. $vbreaks = array();
  1824. $hbreaks = array();
  1825. foreach ($this->phpSheet->getBreaks() as $cell => $breakType) {
  1826. // Fetch coordinates
  1827. $coordinates = PHPExcel_Cell::coordinateFromString($cell);
  1828. // Decide what to do by the type of break
  1829. switch ($breakType) {
  1830. case PHPExcel_Worksheet::BREAK_COLUMN:
  1831. // Add to list of vertical breaks
  1832. $vbreaks[] = PHPExcel_Cell::columnIndexFromString($coordinates[0]) - 1;
  1833. break;
  1834. case PHPExcel_Worksheet::BREAK_ROW:
  1835. // Add to list of horizontal breaks
  1836. $hbreaks[] = $coordinates[1];
  1837. break;
  1838. case PHPExcel_Worksheet::BREAK_NONE:
  1839. default:
  1840. // Nothing to do
  1841. break;
  1842. }
  1843. }
  1844. //horizontal page breaks
  1845. if (!empty($hbreaks)) {
  1846. // Sort and filter array of page breaks
  1847. sort($hbreaks, SORT_NUMERIC);
  1848. if ($hbreaks[0] == 0) { // don't use first break if it's 0
  1849. array_shift($hbreaks);
  1850. }
  1851. $record = 0x001b; // Record identifier
  1852. $cbrk = count($hbreaks); // Number of page breaks
  1853. $length = 2 + 6 * $cbrk; // Bytes to follow
  1854. $header = pack("vv", $record, $length);
  1855. $data = pack("v", $cbrk);
  1856. // Append each page break
  1857. foreach ($hbreaks as $hbreak) {
  1858. $data .= pack("vvv", $hbreak, 0x0000, 0x00ff);
  1859. }
  1860. $this->append($header . $data);
  1861. }
  1862. // vertical page breaks
  1863. if (!empty($vbreaks)) {
  1864. // 1000 vertical pagebreaks appears to be an internal Excel 5 limit.
  1865. // It is slightly higher in Excel 97/200, approx. 1026
  1866. $vbreaks = array_slice($vbreaks, 0, 1000);
  1867. // Sort and filter array of page breaks
  1868. sort($vbreaks, SORT_NUMERIC);
  1869. if ($vbreaks[0] == 0) { // don't use first break if it's 0
  1870. array_shift($vbreaks);
  1871. }
  1872. $record = 0x001a; // Record identifier
  1873. $cbrk = count($vbreaks); // Number of page breaks
  1874. $length = 2 + 6 * $cbrk; // Bytes to follow
  1875. $header = pack("vv", $record, $length);
  1876. $data = pack("v", $cbrk);
  1877. // Append each page break
  1878. foreach ($vbreaks as $vbreak) {
  1879. $data .= pack("vvv", $vbreak, 0x0000, 0xffff);
  1880. }
  1881. $this->append($header . $data);
  1882. }
  1883. }
  1884. /**
  1885. * Set the Biff PROTECT record to indicate that the worksheet is protected.
  1886. */
  1887. private function writeProtect()
  1888. {
  1889. // Exit unless sheet protection has been specified
  1890. if (!$this->phpSheet->getProtection()->getSheet()) {
  1891. return;
  1892. }
  1893. $record = 0x0012; // Record identifier
  1894. $length = 0x0002; // Bytes to follow
  1895. $fLock = 1; // Worksheet is protected
  1896. $header = pack("vv", $record, $length);
  1897. $data = pack("v", $fLock);
  1898. $this->append($header.$data);
  1899. }
  1900. /**
  1901. * Write SCENPROTECT
  1902. */
  1903. private function writeScenProtect()
  1904. {
  1905. // Exit if sheet protection is not active
  1906. if (!$this->phpSheet->getProtection()->getSheet()) {
  1907. return;
  1908. }
  1909. // Exit if scenarios are not protected
  1910. if (!$this->phpSheet->getProtection()->getScenarios()) {
  1911. return;
  1912. }
  1913. $record = 0x00DD; // Record identifier
  1914. $length = 0x0002; // Bytes to follow
  1915. $header = pack('vv', $record, $length);
  1916. $data = pack('v', 1);
  1917. $this->append($header . $data);
  1918. }
  1919. /**
  1920. * Write OBJECTPROTECT
  1921. */
  1922. private function writeObjectProtect()
  1923. {
  1924. // Exit if sheet protection is not active
  1925. if (!$this->phpSheet->getProtection()->getSheet()) {
  1926. return;
  1927. }
  1928. // Exit if objects are not protected
  1929. if (!$this->phpSheet->getProtection()->getObjects()) {
  1930. return;
  1931. }
  1932. $record = 0x0063; // Record identifier
  1933. $length = 0x0002; // Bytes to follow
  1934. $header = pack('vv', $record, $length);
  1935. $data = pack('v', 1);
  1936. $this->append($header . $data);
  1937. }
  1938. /**
  1939. * Write the worksheet PASSWORD record.
  1940. */
  1941. private function writePassword()
  1942. {
  1943. // Exit unless sheet protection and password have been specified
  1944. if (!$this->phpSheet->getProtection()->getSheet() || !$this->phpSheet->getProtection()->getPassword()) {
  1945. return;
  1946. }
  1947. $record = 0x0013; // Record identifier
  1948. $length = 0x0002; // Bytes to follow
  1949. $wPassword = hexdec($this->phpSheet->getProtection()->getPassword()); // Encoded password
  1950. $header = pack("vv", $record, $length);
  1951. $data = pack("v", $wPassword);
  1952. $this->append($header . $data);
  1953. }
  1954. /**
  1955. * Insert a 24bit bitmap image in a worksheet.
  1956. *
  1957. * @access public
  1958. * @param integer $row The row we are going to insert the bitmap into
  1959. * @param integer $col The column we are going to insert the bitmap into
  1960. * @param mixed $bitmap The bitmap filename or GD-image resource
  1961. * @param integer $x The horizontal position (offset) of the image inside the cell.
  1962. * @param integer $y The vertical position (offset) of the image inside the cell.
  1963. * @param float $scale_x The horizontal scale
  1964. * @param float $scale_y The vertical scale
  1965. */
  1966. public function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1)
  1967. {
  1968. $bitmap_array = (is_resource($bitmap) ? $this->processBitmapGd($bitmap) : $this->processBitmap($bitmap));
  1969. list($width, $height, $size, $data) = $bitmap_array; //$this->processBitmap($bitmap);
  1970. // Scale the frame of the image.
  1971. $width *= $scale_x;
  1972. $height *= $scale_y;
  1973. // Calculate the vertices of the image and write the OBJ record
  1974. $this->positionImage($col, $row, $x, $y, $width, $height);
  1975. // Write the IMDATA record to store the bitmap data
  1976. $record = 0x007f;
  1977. $length = 8 + $size;
  1978. $cf = 0x09;
  1979. $env = 0x01;
  1980. $lcb = $size;
  1981. $header = pack("vvvvV", $record, $length, $cf, $env, $lcb);
  1982. $this->append($header.$data);
  1983. }
  1984. /**
  1985. * Calculate the vertices that define the position of the image as required by
  1986. * the OBJ record.
  1987. *
  1988. * +------------+------------+
  1989. * | A | B |
  1990. * +-----+------------+------------+
  1991. * | |(x1,y1) | |
  1992. * | 1 |(A1)._______|______ |
  1993. * | | | | |
  1994. * | | | | |
  1995. * +-----+----| BITMAP |-----+
  1996. * | | | | |
  1997. * | 2 | |______________. |
  1998. * | | | (B2)|
  1999. * | | | (x2,y2)|
  2000. * +---- +------------+------------+
  2001. *
  2002. * Example of a bitmap that covers some of the area from cell A1 to cell B2.
  2003. *
  2004. * Based on the width and height of the bitmap we need to calculate 8 vars:
  2005. * $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
  2006. * The width and height of the cells are also variable and have to be taken into
  2007. * account.
  2008. * The values of $col_start and $row_start are passed in from the calling
  2009. * function. The values of $col_end and $row_end are calculated by subtracting
  2010. * the width and height of the bitmap from the width and height of the
  2011. * underlying cells.
  2012. * The vertices are expressed as a percentage of the underlying cell width as
  2013. * follows (rhs values are in pixels):
  2014. *
  2015. * x1 = X / W *1024
  2016. * y1 = Y / H *256
  2017. * x2 = (X-1) / W *1024
  2018. * y2 = (Y-1) / H *256
  2019. *
  2020. * Where: X is distance from the left side of the underlying cell
  2021. * Y is distance from the top of the underlying cell
  2022. * W is the width of the cell
  2023. * H is the height of the cell
  2024. * The SDK incorrectly states that the height should be expressed as a
  2025. * percentage of 1024.
  2026. *
  2027. * @access private
  2028. * @param integer $col_start Col containing upper left corner of object
  2029. * @param integer $row_start Row containing top left corner of object
  2030. * @param integer $x1 Distance to left side of object
  2031. * @param integer $y1 Distance to top of object
  2032. * @param integer $width Width of image frame
  2033. * @param integer $height Height of image frame
  2034. */
  2035. public function positionImage($col_start, $row_start, $x1, $y1, $width, $height)
  2036. {
  2037. // Initialise end cell to the same as the start cell
  2038. $col_end = $col_start; // Col containing lower right corner of object
  2039. $row_end = $row_start; // Row containing bottom right corner of object
  2040. // Zero the specified offset if greater than the cell dimensions
  2041. if ($x1 >= PHPExcel_Shared_Excel5::sizeCol($this->phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_start))) {
  2042. $x1 = 0;
  2043. }
  2044. if ($y1 >= PHPExcel_Shared_Excel5::sizeRow($this->phpSheet, $row_start + 1)) {
  2045. $y1 = 0;
  2046. }
  2047. $width = $width + $x1 -1;
  2048. $height = $height + $y1 -1;
  2049. // Subtract the underlying cell widths to find the end cell of the image
  2050. while ($width >= PHPExcel_Shared_Excel5::sizeCol($this->phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end))) {
  2051. $width -= PHPExcel_Shared_Excel5::sizeCol($this->phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end));
  2052. ++$col_end;
  2053. }
  2054. // Subtract the underlying cell heights to find the end cell of the image
  2055. while ($height >= PHPExcel_Shared_Excel5::sizeRow($this->phpSheet, $row_end + 1)) {
  2056. $height -= PHPExcel_Shared_Excel5::sizeRow($this->phpSheet, $row_end + 1);
  2057. ++$row_end;
  2058. }
  2059. // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
  2060. // with zero eight or width.
  2061. //
  2062. if (PHPExcel_Shared_Excel5::sizeCol($this->phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_start)) == 0) {
  2063. return;
  2064. }
  2065. if (PHPExcel_Shared_Excel5::sizeCol($this->phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end)) == 0) {
  2066. return;
  2067. }
  2068. if (PHPExcel_Shared_Excel5::sizeRow($this->phpSheet, $row_start + 1) == 0) {
  2069. return;
  2070. }
  2071. if (PHPExcel_Shared_Excel5::sizeRow($this->phpSheet, $row_end + 1) == 0) {
  2072. return;
  2073. }
  2074. // Convert the pixel values to the percentage value expected by Excel
  2075. $x1 = $x1 / PHPExcel_Shared_Excel5::sizeCol($this->phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_start)) * 1024;
  2076. $y1 = $y1 / PHPExcel_Shared_Excel5::sizeRow($this->phpSheet, $row_start + 1) * 256;
  2077. $x2 = $width / PHPExcel_Shared_Excel5::sizeCol($this->phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end)) * 1024; // Distance to right side of object
  2078. $y2 = $height / PHPExcel_Shared_Excel5::sizeRow($this->phpSheet, $row_end + 1) * 256; // Distance to bottom of object
  2079. $this->writeObjPicture($col_start, $x1, $row_start, $y1, $col_end, $x2, $row_end, $y2);
  2080. }
  2081. /**
  2082. * Store the OBJ record that precedes an IMDATA record. This could be generalise
  2083. * to support other Excel objects.
  2084. *
  2085. * @param integer $colL Column containing upper left corner of object
  2086. * @param integer $dxL Distance from left side of cell
  2087. * @param integer $rwT Row containing top left corner of object
  2088. * @param integer $dyT Distance from top of cell
  2089. * @param integer $colR Column containing lower right corner of object
  2090. * @param integer $dxR Distance from right of cell
  2091. * @param integer $rwB Row containing bottom right corner of object
  2092. * @param integer $dyB Distance from bottom of cell
  2093. */
  2094. private function writeObjPicture($colL, $dxL, $rwT, $dyT, $colR, $dxR, $rwB, $dyB)
  2095. {
  2096. $record = 0x005d; // Record identifier
  2097. $length = 0x003c; // Bytes to follow
  2098. $cObj = 0x0001; // Count of objects in file (set to 1)
  2099. $OT = 0x0008; // Object type. 8 = Picture
  2100. $id = 0x0001; // Object ID
  2101. $grbit = 0x0614; // Option flags
  2102. $cbMacro = 0x0000; // Length of FMLA structure
  2103. $Reserved1 = 0x0000; // Reserved
  2104. $Reserved2 = 0x0000; // Reserved
  2105. $icvBack = 0x09; // Background colour
  2106. $icvFore = 0x09; // Foreground colour
  2107. $fls = 0x00; // Fill pattern
  2108. $fAuto = 0x00; // Automatic fill
  2109. $icv = 0x08; // Line colour
  2110. $lns = 0xff; // Line style
  2111. $lnw = 0x01; // Line weight
  2112. $fAutoB = 0x00; // Automatic border
  2113. $frs = 0x0000; // Frame style
  2114. $cf = 0x0009; // Image format, 9 = bitmap
  2115. $Reserved3 = 0x0000; // Reserved
  2116. $cbPictFmla = 0x0000; // Length of FMLA structure
  2117. $Reserved4 = 0x0000; // Reserved
  2118. $grbit2 = 0x0001; // Option flags
  2119. $Reserved5 = 0x0000; // Reserved
  2120. $header = pack("vv", $record, $length);
  2121. $data = pack("V", $cObj);
  2122. $data .= pack("v", $OT);
  2123. $data .= pack("v", $id);
  2124. $data .= pack("v", $grbit);
  2125. $data .= pack("v", $colL);
  2126. $data .= pack("v", $dxL);
  2127. $data .= pack("v", $rwT);
  2128. $data .= pack("v", $dyT);
  2129. $data .= pack("v", $colR);
  2130. $data .= pack("v", $dxR);
  2131. $data .= pack("v", $rwB);
  2132. $data .= pack("v", $dyB);
  2133. $data .= pack("v", $cbMacro);
  2134. $data .= pack("V", $Reserved1);
  2135. $data .= pack("v", $Reserved2);
  2136. $data .= pack("C", $icvBack);
  2137. $data .= pack("C", $icvFore);
  2138. $data .= pack("C", $fls);
  2139. $data .= pack("C", $fAuto);
  2140. $data .= pack("C", $icv);
  2141. $data .= pack("C", $lns);
  2142. $data .= pack("C", $lnw);
  2143. $data .= pack("C", $fAutoB);
  2144. $data .= pack("v", $frs);
  2145. $data .= pack("V", $cf);
  2146. $data .= pack("v", $Reserved3);
  2147. $data .= pack("v", $cbPictFmla);
  2148. $data .= pack("v", $Reserved4);
  2149. $data .= pack("v", $grbit2);
  2150. $data .= pack("V", $Reserved5);
  2151. $this->append($header . $data);
  2152. }
  2153. /**
  2154. * Convert a GD-image into the internal format.
  2155. *
  2156. * @access private
  2157. * @param resource $image The image to process
  2158. * @return array Array with data and properties of the bitmap
  2159. */
  2160. public function processBitmapGd($image)
  2161. {
  2162. $width = imagesx($image);
  2163. $height = imagesy($image);
  2164. $data = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18);
  2165. for ($j=$height; $j--;) {
  2166. for ($i=0; $i < $width; ++$i) {
  2167. $color = imagecolorsforindex($image, imagecolorat($image, $i, $j));
  2168. foreach (array("red", "green", "blue") as $key) {
  2169. $color[$key] = $color[$key] + round((255 - $color[$key]) * $color["alpha"] / 127);
  2170. }
  2171. $data .= chr($color["blue"]) . chr($color["green"]) . chr($color["red"]);
  2172. }
  2173. if (3*$width % 4) {
  2174. $data .= str_repeat("\x00", 4 - 3*$width % 4);
  2175. }
  2176. }
  2177. return array($width, $height, strlen($data), $data);
  2178. }
  2179. /**
  2180. * Convert a 24 bit bitmap into the modified internal format used by Windows.
  2181. * This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the
  2182. * MSDN library.
  2183. *
  2184. * @access private
  2185. * @param string $bitmap The bitmap to process
  2186. * @return array Array with data and properties of the bitmap
  2187. */
  2188. public function processBitmap($bitmap)
  2189. {
  2190. // Open file.
  2191. $bmp_fd = @fopen($bitmap, "rb");
  2192. if (!$bmp_fd) {
  2193. throw new PHPExcel_Writer_Exception("Couldn't import $bitmap");
  2194. }
  2195. // Slurp the file into a string.
  2196. $data = fread($bmp_fd, filesize($bitmap));
  2197. // Check that the file is big enough to be a bitmap.
  2198. if (strlen($data) <= 0x36) {
  2199. throw new PHPExcel_Writer_Exception("$bitmap doesn't contain enough data.\n");
  2200. }
  2201. // The first 2 bytes are used to identify the bitmap.
  2202. $identity = unpack("A2ident", $data);
  2203. if ($identity['ident'] != "BM") {
  2204. throw new PHPExcel_Writer_Exception("$bitmap doesn't appear to be a valid bitmap image.\n");
  2205. }
  2206. // Remove bitmap data: ID.
  2207. $data = substr($data, 2);
  2208. // Read and remove the bitmap size. This is more reliable than reading
  2209. // the data size at offset 0x22.
  2210. //
  2211. $size_array = unpack("Vsa", substr($data, 0, 4));
  2212. $size = $size_array['sa'];
  2213. $data = substr($data, 4);
  2214. $size -= 0x36; // Subtract size of bitmap header.
  2215. $size += 0x0C; // Add size of BIFF header.
  2216. // Remove bitmap data: reserved, offset, header length.
  2217. $data = substr($data, 12);
  2218. // Read and remove the bitmap width and height. Verify the sizes.
  2219. $width_and_height = unpack("V2", substr($data, 0, 8));
  2220. $width = $width_and_height[1];
  2221. $height = $width_and_height[2];
  2222. $data = substr($data, 8);
  2223. if ($width > 0xFFFF) {
  2224. throw new PHPExcel_Writer_Exception("$bitmap: largest image width supported is 65k.\n");
  2225. }
  2226. if ($height > 0xFFFF) {
  2227. throw new PHPExcel_Writer_Exception("$bitmap: largest image height supported is 65k.\n");
  2228. }
  2229. // Read and remove the bitmap planes and bpp data. Verify them.
  2230. $planes_and_bitcount = unpack("v2", substr($data, 0, 4));
  2231. $data = substr($data, 4);
  2232. if ($planes_and_bitcount[2] != 24) { // Bitcount
  2233. throw new PHPExcel_Writer_Exception("$bitmap isn't a 24bit true color bitmap.\n");
  2234. }
  2235. if ($planes_and_bitcount[1] != 1) {
  2236. throw new PHPExcel_Writer_Exception("$bitmap: only 1 plane supported in bitmap image.\n");
  2237. }
  2238. // Read and remove the bitmap compression. Verify compression.
  2239. $compression = unpack("Vcomp", substr($data, 0, 4));
  2240. $data = substr($data, 4);
  2241. //$compression = 0;
  2242. if ($compression['comp'] != 0) {
  2243. throw new PHPExcel_Writer_Exception("$bitmap: compression not supported in bitmap image.\n");
  2244. }
  2245. // Remove bitmap data: data size, hres, vres, colours, imp. colours.
  2246. $data = substr($data, 20);
  2247. // Add the BITMAPCOREHEADER data
  2248. $header = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18);
  2249. $data = $header . $data;
  2250. return (array($width, $height, $size, $data));
  2251. }
  2252. /**
  2253. * Store the window zoom factor. This should be a reduced fraction but for
  2254. * simplicity we will store all fractions with a numerator of 100.
  2255. */
  2256. private function writeZoom()
  2257. {
  2258. // If scale is 100 we don't need to write a record
  2259. if ($this->phpSheet->getSheetView()->getZoomScale() == 100) {
  2260. return;
  2261. }
  2262. $record = 0x00A0; // Record identifier
  2263. $length = 0x0004; // Bytes to follow
  2264. $header = pack("vv", $record, $length);
  2265. $data = pack("vv", $this->phpSheet->getSheetView()->getZoomScale(), 100);
  2266. $this->append($header . $data);
  2267. }
  2268. /**
  2269. * Get Escher object
  2270. *
  2271. * @return PHPExcel_Shared_Escher
  2272. */
  2273. public function getEscher()
  2274. {
  2275. return $this->escher;
  2276. }
  2277. /**
  2278. * Set Escher object
  2279. *
  2280. * @param PHPExcel_Shared_Escher $pValue
  2281. */
  2282. public function setEscher(PHPExcel_Shared_Escher $pValue = null)
  2283. {
  2284. $this->escher = $pValue;
  2285. }
  2286. /**
  2287. * Write MSODRAWING record
  2288. */
  2289. private function writeMsoDrawing()
  2290. {
  2291. // write the Escher stream if necessary
  2292. if (isset($this->escher)) {
  2293. $writer = new PHPExcel_Writer_Excel5_Escher($this->escher);
  2294. $data = $writer->close();
  2295. $spOffsets = $writer->getSpOffsets();
  2296. $spTypes = $writer->getSpTypes();
  2297. // write the neccesary MSODRAWING, OBJ records
  2298. // split the Escher stream
  2299. $spOffsets[0] = 0;
  2300. $nm = count($spOffsets) - 1; // number of shapes excluding first shape
  2301. for ($i = 1; $i <= $nm; ++$i) {
  2302. // MSODRAWING record
  2303. $record = 0x00EC; // Record identifier
  2304. // chunk of Escher stream for one shape
  2305. $dataChunk = substr($data, $spOffsets[$i -1], $spOffsets[$i] - $spOffsets[$i - 1]);
  2306. $length = strlen($dataChunk);
  2307. $header = pack("vv", $record, $length);
  2308. $this->append($header . $dataChunk);
  2309. // OBJ record
  2310. $record = 0x005D; // record identifier
  2311. $objData = '';
  2312. // ftCmo
  2313. if ($spTypes[$i] == 0x00C9) {
  2314. // Add ftCmo (common object data) subobject
  2315. $objData .=
  2316. pack(
  2317. 'vvvvvVVV',
  2318. 0x0015, // 0x0015 = ftCmo
  2319. 0x0012, // length of ftCmo data
  2320. 0x0014, // object type, 0x0014 = filter
  2321. $i, // object id number, Excel seems to use 1-based index, local for the sheet
  2322. 0x2101, // option flags, 0x2001 is what OpenOffice.org uses
  2323. 0, // reserved
  2324. 0, // reserved
  2325. 0 // reserved
  2326. );
  2327. // Add ftSbs Scroll bar subobject
  2328. $objData .= pack('vv', 0x00C, 0x0014);
  2329. $objData .= pack('H*', '0000000000000000640001000A00000010000100');
  2330. // Add ftLbsData (List box data) subobject
  2331. $objData .= pack('vv', 0x0013, 0x1FEE);
  2332. $objData .= pack('H*', '00000000010001030000020008005700');
  2333. } else {
  2334. // Add ftCmo (common object data) subobject
  2335. $objData .=
  2336. pack(
  2337. 'vvvvvVVV',
  2338. 0x0015, // 0x0015 = ftCmo
  2339. 0x0012, // length of ftCmo data
  2340. 0x0008, // object type, 0x0008 = picture
  2341. $i, // object id number, Excel seems to use 1-based index, local for the sheet
  2342. 0x6011, // option flags, 0x6011 is what OpenOffice.org uses
  2343. 0, // reserved
  2344. 0, // reserved
  2345. 0 // reserved
  2346. );
  2347. }
  2348. // ftEnd
  2349. $objData .=
  2350. pack(
  2351. 'vv',
  2352. 0x0000, // 0x0000 = ftEnd
  2353. 0x0000 // length of ftEnd data
  2354. );
  2355. $length = strlen($objData);
  2356. $header = pack('vv', $record, $length);
  2357. $this->append($header . $objData);
  2358. }
  2359. }
  2360. }
  2361. /**
  2362. * Store the DATAVALIDATIONS and DATAVALIDATION records.
  2363. */
  2364. private function writeDataValidity()
  2365. {
  2366. // Datavalidation collection
  2367. $dataValidationCollection = $this->phpSheet->getDataValidationCollection();
  2368. // Write data validations?
  2369. if (!empty($dataValidationCollection)) {
  2370. // DATAVALIDATIONS record
  2371. $record = 0x01B2; // Record identifier
  2372. $length = 0x0012; // Bytes to follow
  2373. $grbit = 0x0000; // Prompt box at cell, no cached validity data at DV records
  2374. $horPos = 0x00000000; // Horizontal position of prompt box, if fixed position
  2375. $verPos = 0x00000000; // Vertical position of prompt box, if fixed position
  2376. $objId = 0xFFFFFFFF; // Object identifier of drop down arrow object, or -1 if not visible
  2377. $header = pack('vv', $record, $length);
  2378. $data = pack('vVVVV', $grbit, $horPos, $verPos, $objId, count($dataValidationCollection));
  2379. $this->append($header.$data);
  2380. // DATAVALIDATION records
  2381. $record = 0x01BE; // Record identifier
  2382. foreach ($dataValidationCollection as $cellCoordinate => $dataValidation) {
  2383. // initialize record data
  2384. $data = '';
  2385. // options
  2386. $options = 0x00000000;
  2387. // data type
  2388. $type = $dataValidation->getType();
  2389. switch ($type) {
  2390. case PHPExcel_Cell_DataValidation::TYPE_NONE:
  2391. $type = 0x00;
  2392. break;
  2393. case PHPExcel_Cell_DataValidation::TYPE_WHOLE:
  2394. $type = 0x01;
  2395. break;
  2396. case PHPExcel_Cell_DataValidation::TYPE_DECIMAL:
  2397. $type = 0x02;
  2398. break;
  2399. case PHPExcel_Cell_DataValidation::TYPE_LIST:
  2400. $type = 0x03;
  2401. break;
  2402. case PHPExcel_Cell_DataValidation::TYPE_DATE:
  2403. $type = 0x04;
  2404. break;
  2405. case PHPExcel_Cell_DataValidation::TYPE_TIME:
  2406. $type = 0x05;
  2407. break;
  2408. case PHPExcel_Cell_DataValidation::TYPE_TEXTLENGTH:
  2409. $type = 0x06;
  2410. break;
  2411. case PHPExcel_Cell_DataValidation::TYPE_CUSTOM:
  2412. $type = 0x07;
  2413. break;
  2414. }
  2415. $options |= $type << 0;
  2416. // error style
  2417. $errorStyle = $dataValidation->getType();
  2418. switch ($errorStyle) {
  2419. case PHPExcel_Cell_DataValidation::STYLE_STOP:
  2420. $errorStyle = 0x00;
  2421. break;
  2422. case PHPExcel_Cell_DataValidation::STYLE_WARNING:
  2423. $errorStyle = 0x01;
  2424. break;
  2425. case PHPExcel_Cell_DataValidation::STYLE_INFORMATION:
  2426. $errorStyle = 0x02;
  2427. break;
  2428. }
  2429. $options |= $errorStyle << 4;
  2430. // explicit formula?
  2431. if ($type == 0x03 && preg_match('/^\".*\"$/', $dataValidation->getFormula1())) {
  2432. $options |= 0x01 << 7;
  2433. }
  2434. // empty cells allowed
  2435. $options |= $dataValidation->getAllowBlank() << 8;
  2436. // show drop down
  2437. $options |= (!$dataValidation->getShowDropDown()) << 9;
  2438. // show input message
  2439. $options |= $dataValidation->getShowInputMessage() << 18;
  2440. // show error message
  2441. $options |= $dataValidation->getShowErrorMessage() << 19;
  2442. // condition operator
  2443. $operator = $dataValidation->getOperator();
  2444. switch ($operator) {
  2445. case PHPExcel_Cell_DataValidation::OPERATOR_BETWEEN:
  2446. $operator = 0x00;
  2447. break;
  2448. case PHPExcel_Cell_DataValidation::OPERATOR_NOTBETWEEN:
  2449. $operator = 0x01;
  2450. break;
  2451. case PHPExcel_Cell_DataValidation::OPERATOR_EQUAL:
  2452. $operator = 0x02;
  2453. break;
  2454. case PHPExcel_Cell_DataValidation::OPERATOR_NOTEQUAL:
  2455. $operator = 0x03;
  2456. break;
  2457. case PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHAN:
  2458. $operator = 0x04;
  2459. break;
  2460. case PHPExcel_Cell_DataValidation::OPERATOR_LESSTHAN:
  2461. $operator = 0x05;
  2462. break;
  2463. case PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHANOREQUAL:
  2464. $operator = 0x06;
  2465. break;
  2466. case PHPExcel_Cell_DataValidation::OPERATOR_LESSTHANOREQUAL:
  2467. $operator = 0x07;
  2468. break;
  2469. }
  2470. $options |= $operator << 20;
  2471. $data = pack('V', $options);
  2472. // prompt title
  2473. $promptTitle = $dataValidation->getPromptTitle() !== '' ?
  2474. $dataValidation->getPromptTitle() : chr(0);
  2475. $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($promptTitle);
  2476. // error title
  2477. $errorTitle = $dataValidation->getErrorTitle() !== '' ?
  2478. $dataValidation->getErrorTitle() : chr(0);
  2479. $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($errorTitle);
  2480. // prompt text
  2481. $prompt = $dataValidation->getPrompt() !== '' ?
  2482. $dataValidation->getPrompt() : chr(0);
  2483. $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($prompt);
  2484. // error text
  2485. $error = $dataValidation->getError() !== '' ?
  2486. $dataValidation->getError() : chr(0);
  2487. $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($error);
  2488. // formula 1
  2489. try {
  2490. $formula1 = $dataValidation->getFormula1();
  2491. if ($type == 0x03) { // list type
  2492. $formula1 = str_replace(',', chr(0), $formula1);
  2493. }
  2494. $this->parser->parse($formula1);
  2495. $formula1 = $this->parser->toReversePolish();
  2496. $sz1 = strlen($formula1);
  2497. } catch (PHPExcel_Exception $e) {
  2498. $sz1 = 0;
  2499. $formula1 = '';
  2500. }
  2501. $data .= pack('vv', $sz1, 0x0000);
  2502. $data .= $formula1;
  2503. // formula 2
  2504. try {
  2505. $formula2 = $dataValidation->getFormula2();
  2506. if ($formula2 === '') {
  2507. throw new PHPExcel_Writer_Exception('No formula2');
  2508. }
  2509. $this->parser->parse($formula2);
  2510. $formula2 = $this->parser->toReversePolish();
  2511. $sz2 = strlen($formula2);
  2512. } catch (PHPExcel_Exception $e) {
  2513. $sz2 = 0;
  2514. $formula2 = '';
  2515. }
  2516. $data .= pack('vv', $sz2, 0x0000);
  2517. $data .= $formula2;
  2518. // cell range address list
  2519. $data .= pack('v', 0x0001);
  2520. $data .= $this->writeBIFF8CellRangeAddressFixed($cellCoordinate);
  2521. $length = strlen($data);
  2522. $header = pack("vv", $record, $length);
  2523. $this->append($header . $data);
  2524. }
  2525. }
  2526. }
  2527. /**
  2528. * Map Error code
  2529. *
  2530. * @param string $errorCode
  2531. * @return int
  2532. */
  2533. private static function mapErrorCode($errorCode)
  2534. {
  2535. switch ($errorCode) {
  2536. case '#NULL!':
  2537. return 0x00;
  2538. case '#DIV/0!':
  2539. return 0x07;
  2540. case '#VALUE!':
  2541. return 0x0F;
  2542. case '#REF!':
  2543. return 0x17;
  2544. case '#NAME?':
  2545. return 0x1D;
  2546. case '#NUM!':
  2547. return 0x24;
  2548. case '#N/A':
  2549. return 0x2A;
  2550. }
  2551. return 0;
  2552. }
  2553. /**
  2554. * Write PLV Record
  2555. */
  2556. private function writePageLayoutView()
  2557. {
  2558. $record = 0x088B; // Record identifier
  2559. $length = 0x0010; // Bytes to follow
  2560. $rt = 0x088B; // 2
  2561. $grbitFrt = 0x0000; // 2
  2562. $reserved = 0x0000000000000000; // 8
  2563. $wScalvePLV = $this->phpSheet->getSheetView()->getZoomScale(); // 2
  2564. // The options flags that comprise $grbit
  2565. if ($this->phpSheet->getSheetView()->getView() == PHPExcel_Worksheet_SheetView::SHEETVIEW_PAGE_LAYOUT) {
  2566. $fPageLayoutView = 1;
  2567. } else {
  2568. $fPageLayoutView = 0;
  2569. }
  2570. $fRulerVisible = 0;
  2571. $fWhitespaceHidden = 0;
  2572. $grbit = $fPageLayoutView; // 2
  2573. $grbit |= $fRulerVisible << 1;
  2574. $grbit |= $fWhitespaceHidden << 3;
  2575. $header = pack("vv", $record, $length);
  2576. $data = pack("vvVVvv", $rt, $grbitFrt, 0x00000000, 0x00000000, $wScalvePLV, $grbit);
  2577. $this->append($header . $data);
  2578. }
  2579. /**
  2580. * Write CFRule Record
  2581. * @param PHPExcel_Style_Conditional $conditional
  2582. */
  2583. private function writeCFRule(PHPExcel_Style_Conditional $conditional)
  2584. {
  2585. $record = 0x01B1; // Record identifier
  2586. // $type : Type of the CF
  2587. // $operatorType : Comparison operator
  2588. if ($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_EXPRESSION) {
  2589. $type = 0x02;
  2590. $operatorType = 0x00;
  2591. } elseif ($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CELLIS) {
  2592. $type = 0x01;
  2593. switch ($conditional->getOperatorType()) {
  2594. case PHPExcel_Style_Conditional::OPERATOR_NONE:
  2595. $operatorType = 0x00;
  2596. break;
  2597. case PHPExcel_Style_Conditional::OPERATOR_EQUAL:
  2598. $operatorType = 0x03;
  2599. break;
  2600. case PHPExcel_Style_Conditional::OPERATOR_GREATERTHAN:
  2601. $operatorType = 0x05;
  2602. break;
  2603. case PHPExcel_Style_Conditional::OPERATOR_GREATERTHANOREQUAL:
  2604. $operatorType = 0x07;
  2605. break;
  2606. case PHPExcel_Style_Conditional::OPERATOR_LESSTHAN:
  2607. $operatorType = 0x06;
  2608. break;
  2609. case PHPExcel_Style_Conditional::OPERATOR_LESSTHANOREQUAL:
  2610. $operatorType = 0x08;
  2611. break;
  2612. case PHPExcel_Style_Conditional::OPERATOR_NOTEQUAL:
  2613. $operatorType = 0x04;
  2614. break;
  2615. case PHPExcel_Style_Conditional::OPERATOR_BETWEEN:
  2616. $operatorType = 0x01;
  2617. break;
  2618. // not OPERATOR_NOTBETWEEN 0x02
  2619. }
  2620. }
  2621. // $szValue1 : size of the formula data for first value or formula
  2622. // $szValue2 : size of the formula data for second value or formula
  2623. $arrConditions = $conditional->getConditions();
  2624. $numConditions = sizeof($arrConditions);
  2625. if ($numConditions == 1) {
  2626. $szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
  2627. $szValue2 = 0x0000;
  2628. $operand1 = pack('Cv', 0x1E, $arrConditions[0]);
  2629. $operand2 = null;
  2630. } elseif ($numConditions == 2 && ($conditional->getOperatorType() == PHPExcel_Style_Conditional::OPERATOR_BETWEEN)) {
  2631. $szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
  2632. $szValue2 = ($arrConditions[1] <= 65535 ? 3 : 0x0000);
  2633. $operand1 = pack('Cv', 0x1E, $arrConditions[0]);
  2634. $operand2 = pack('Cv', 0x1E, $arrConditions[1]);
  2635. } else {
  2636. $szValue1 = 0x0000;
  2637. $szValue2 = 0x0000;
  2638. $operand1 = null;
  2639. $operand2 = null;
  2640. }
  2641. // $flags : Option flags
  2642. // Alignment
  2643. $bAlignHz = ($conditional->getStyle()->getAlignment()->getHorizontal() == null ? 1 : 0);
  2644. $bAlignVt = ($conditional->getStyle()->getAlignment()->getVertical() == null ? 1 : 0);
  2645. $bAlignWrapTx = ($conditional->getStyle()->getAlignment()->getWrapText() == false ? 1 : 0);
  2646. $bTxRotation = ($conditional->getStyle()->getAlignment()->getTextRotation() == null ? 1 : 0);
  2647. $bIndent = ($conditional->getStyle()->getAlignment()->getIndent() == 0 ? 1 : 0);
  2648. $bShrinkToFit = ($conditional->getStyle()->getAlignment()->getShrinkToFit() == false ? 1 : 0);
  2649. if ($bAlignHz == 0 || $bAlignVt == 0 || $bAlignWrapTx == 0 || $bTxRotation == 0 || $bIndent == 0 || $bShrinkToFit == 0) {
  2650. $bFormatAlign = 1;
  2651. } else {
  2652. $bFormatAlign = 0;
  2653. }
  2654. // Protection
  2655. $bProtLocked = ($conditional->getStyle()->getProtection()->getLocked() == null ? 1 : 0);
  2656. $bProtHidden = ($conditional->getStyle()->getProtection()->getHidden() == null ? 1 : 0);
  2657. if ($bProtLocked == 0 || $bProtHidden == 0) {
  2658. $bFormatProt = 1;
  2659. } else {
  2660. $bFormatProt = 0;
  2661. }
  2662. // Border
  2663. $bBorderLeft = ($conditional->getStyle()->getBorders()->getLeft()->getColor()->getARGB() == PHPExcel_Style_Color::COLOR_BLACK
  2664. && $conditional->getStyle()->getBorders()->getLeft()->getBorderStyle() == PHPExcel_Style_Border::BORDER_NONE ? 1 : 0);
  2665. $bBorderRight = ($conditional->getStyle()->getBorders()->getRight()->getColor()->getARGB() == PHPExcel_Style_Color::COLOR_BLACK
  2666. && $conditional->getStyle()->getBorders()->getRight()->getBorderStyle() == PHPExcel_Style_Border::BORDER_NONE ? 1 : 0);
  2667. $bBorderTop = ($conditional->getStyle()->getBorders()->getTop()->getColor()->getARGB() == PHPExcel_Style_Color::COLOR_BLACK
  2668. && $conditional->getStyle()->getBorders()->getTop()->getBorderStyle() == PHPExcel_Style_Border::BORDER_NONE ? 1 : 0);
  2669. $bBorderBottom = ($conditional->getStyle()->getBorders()->getBottom()->getColor()->getARGB() == PHPExcel_Style_Color::COLOR_BLACK
  2670. && $conditional->getStyle()->getBorders()->getBottom()->getBorderStyle() == PHPExcel_Style_Border::BORDER_NONE ? 1 : 0);
  2671. if ($bBorderLeft == 0 || $bBorderRight == 0 || $bBorderTop == 0 || $bBorderBottom == 0) {
  2672. $bFormatBorder = 1;
  2673. } else {
  2674. $bFormatBorder = 0;
  2675. }
  2676. // Pattern
  2677. $bFillStyle = ($conditional->getStyle()->getFill()->getFillType() == null ? 0 : 1);
  2678. $bFillColor = ($conditional->getStyle()->getFill()->getStartColor()->getARGB() == null ? 0 : 1);
  2679. $bFillColorBg = ($conditional->getStyle()->getFill()->getEndColor()->getARGB() == null ? 0 : 1);
  2680. if ($bFillStyle == 0 || $bFillColor == 0 || $bFillColorBg == 0) {
  2681. $bFormatFill = 1;
  2682. } else {
  2683. $bFormatFill = 0;
  2684. }
  2685. // Font
  2686. if ($conditional->getStyle()->getFont()->getName() != null
  2687. || $conditional->getStyle()->getFont()->getSize() != null
  2688. || $conditional->getStyle()->getFont()->getBold() != null
  2689. || $conditional->getStyle()->getFont()->getItalic() != null
  2690. || $conditional->getStyle()->getFont()->getSuperScript() != null
  2691. || $conditional->getStyle()->getFont()->getSubScript() != null
  2692. || $conditional->getStyle()->getFont()->getUnderline() != null
  2693. || $conditional->getStyle()->getFont()->getStrikethrough() != null
  2694. || $conditional->getStyle()->getFont()->getColor()->getARGB() != null) {
  2695. $bFormatFont = 1;
  2696. } else {
  2697. $bFormatFont = 0;
  2698. }
  2699. // Alignment
  2700. $flags = 0;
  2701. $flags |= (1 == $bAlignHz ? 0x00000001 : 0);
  2702. $flags |= (1 == $bAlignVt ? 0x00000002 : 0);
  2703. $flags |= (1 == $bAlignWrapTx ? 0x00000004 : 0);
  2704. $flags |= (1 == $bTxRotation ? 0x00000008 : 0);
  2705. // Justify last line flag
  2706. $flags |= (1 == 1 ? 0x00000010 : 0);
  2707. $flags |= (1 == $bIndent ? 0x00000020 : 0);
  2708. $flags |= (1 == $bShrinkToFit ? 0x00000040 : 0);
  2709. // Default
  2710. $flags |= (1 == 1 ? 0x00000080 : 0);
  2711. // Protection
  2712. $flags |= (1 == $bProtLocked ? 0x00000100 : 0);
  2713. $flags |= (1 == $bProtHidden ? 0x00000200 : 0);
  2714. // Border
  2715. $flags |= (1 == $bBorderLeft ? 0x00000400 : 0);
  2716. $flags |= (1 == $bBorderRight ? 0x00000800 : 0);
  2717. $flags |= (1 == $bBorderTop ? 0x00001000 : 0);
  2718. $flags |= (1 == $bBorderBottom ? 0x00002000 : 0);
  2719. $flags |= (1 == 1 ? 0x00004000 : 0); // Top left to Bottom right border
  2720. $flags |= (1 == 1 ? 0x00008000 : 0); // Bottom left to Top right border
  2721. // Pattern
  2722. $flags |= (1 == $bFillStyle ? 0x00010000 : 0);
  2723. $flags |= (1 == $bFillColor ? 0x00020000 : 0);
  2724. $flags |= (1 == $bFillColorBg ? 0x00040000 : 0);
  2725. $flags |= (1 == 1 ? 0x00380000 : 0);
  2726. // Font
  2727. $flags |= (1 == $bFormatFont ? 0x04000000 : 0);
  2728. // Alignment:
  2729. $flags |= (1 == $bFormatAlign ? 0x08000000 : 0);
  2730. // Border
  2731. $flags |= (1 == $bFormatBorder ? 0x10000000 : 0);
  2732. // Pattern
  2733. $flags |= (1 == $bFormatFill ? 0x20000000 : 0);
  2734. // Protection
  2735. $flags |= (1 == $bFormatProt ? 0x40000000 : 0);
  2736. // Text direction
  2737. $flags |= (1 == 0 ? 0x80000000 : 0);
  2738. // Data Blocks
  2739. if ($bFormatFont == 1) {
  2740. // Font Name
  2741. if ($conditional->getStyle()->getFont()->getName() == null) {
  2742. $dataBlockFont = pack('VVVVVVVV', 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000);
  2743. $dataBlockFont .= pack('VVVVVVVV', 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000);
  2744. } else {
  2745. $dataBlockFont = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($conditional->getStyle()->getFont()->getName());
  2746. }
  2747. // Font Size
  2748. if ($conditional->getStyle()->getFont()->getSize() == null) {
  2749. $dataBlockFont .= pack('V', 20 * 11);
  2750. } else {
  2751. $dataBlockFont .= pack('V', 20 * $conditional->getStyle()->getFont()->getSize());
  2752. }
  2753. // Font Options
  2754. $dataBlockFont .= pack('V', 0);
  2755. // Font weight
  2756. if ($conditional->getStyle()->getFont()->getBold() == true) {
  2757. $dataBlockFont .= pack('v', 0x02BC);
  2758. } else {
  2759. $dataBlockFont .= pack('v', 0x0190);
  2760. }
  2761. // Escapement type
  2762. if ($conditional->getStyle()->getFont()->getSubScript() == true) {
  2763. $dataBlockFont .= pack('v', 0x02);
  2764. $fontEscapement = 0;
  2765. } elseif ($conditional->getStyle()->getFont()->getSuperScript() == true) {
  2766. $dataBlockFont .= pack('v', 0x01);
  2767. $fontEscapement = 0;
  2768. } else {
  2769. $dataBlockFont .= pack('v', 0x00);
  2770. $fontEscapement = 1;
  2771. }
  2772. // Underline type
  2773. switch ($conditional->getStyle()->getFont()->getUnderline()) {
  2774. case PHPExcel_Style_Font::UNDERLINE_NONE:
  2775. $dataBlockFont .= pack('C', 0x00);
  2776. $fontUnderline = 0;
  2777. break;
  2778. case PHPExcel_Style_Font::UNDERLINE_DOUBLE:
  2779. $dataBlockFont .= pack('C', 0x02);
  2780. $fontUnderline = 0;
  2781. break;
  2782. case PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING:
  2783. $dataBlockFont .= pack('C', 0x22);
  2784. $fontUnderline = 0;
  2785. break;
  2786. case PHPExcel_Style_Font::UNDERLINE_SINGLE:
  2787. $dataBlockFont .= pack('C', 0x01);
  2788. $fontUnderline = 0;
  2789. break;
  2790. case PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING:
  2791. $dataBlockFont .= pack('C', 0x21);
  2792. $fontUnderline = 0;
  2793. break;
  2794. default: $dataBlockFont .= pack('C', 0x00);
  2795. $fontUnderline = 1;
  2796. break;
  2797. }
  2798. // Not used (3)
  2799. $dataBlockFont .= pack('vC', 0x0000, 0x00);
  2800. // Font color index
  2801. switch ($conditional->getStyle()->getFont()->getColor()->getRGB()) {
  2802. case '000000':
  2803. $colorIdx = 0x08;
  2804. break;
  2805. case 'FFFFFF':
  2806. $colorIdx = 0x09;
  2807. break;
  2808. case 'FF0000':
  2809. $colorIdx = 0x0A;
  2810. break;
  2811. case '00FF00':
  2812. $colorIdx = 0x0B;
  2813. break;
  2814. case '0000FF':
  2815. $colorIdx = 0x0C;
  2816. break;
  2817. case 'FFFF00':
  2818. $colorIdx = 0x0D;
  2819. break;
  2820. case 'FF00FF':
  2821. $colorIdx = 0x0E;
  2822. break;
  2823. case '00FFFF':
  2824. $colorIdx = 0x0F;
  2825. break;
  2826. case '800000':
  2827. $colorIdx = 0x10;
  2828. break;
  2829. case '008000':
  2830. $colorIdx = 0x11;
  2831. break;
  2832. case '000080':
  2833. $colorIdx = 0x12;
  2834. break;
  2835. case '808000':
  2836. $colorIdx = 0x13;
  2837. break;
  2838. case '800080':
  2839. $colorIdx = 0x14;
  2840. break;
  2841. case '008080':
  2842. $colorIdx = 0x15;
  2843. break;
  2844. case 'C0C0C0':
  2845. $colorIdx = 0x16;
  2846. break;
  2847. case '808080':
  2848. $colorIdx = 0x17;
  2849. break;
  2850. case '9999FF':
  2851. $colorIdx = 0x18;
  2852. break;
  2853. case '993366':
  2854. $colorIdx = 0x19;
  2855. break;
  2856. case 'FFFFCC':
  2857. $colorIdx = 0x1A;
  2858. break;
  2859. case 'CCFFFF':
  2860. $colorIdx = 0x1B;
  2861. break;
  2862. case '660066':
  2863. $colorIdx = 0x1C;
  2864. break;
  2865. case 'FF8080':
  2866. $colorIdx = 0x1D;
  2867. break;
  2868. case '0066CC':
  2869. $colorIdx = 0x1E;
  2870. break;
  2871. case 'CCCCFF':
  2872. $colorIdx = 0x1F;
  2873. break;
  2874. case '000080':
  2875. $colorIdx = 0x20;
  2876. break;
  2877. case 'FF00FF':
  2878. $colorIdx = 0x21;
  2879. break;
  2880. case 'FFFF00':
  2881. $colorIdx = 0x22;
  2882. break;
  2883. case '00FFFF':
  2884. $colorIdx = 0x23;
  2885. break;
  2886. case '800080':
  2887. $colorIdx = 0x24;
  2888. break;
  2889. case '800000':
  2890. $colorIdx = 0x25;
  2891. break;
  2892. case '008080':
  2893. $colorIdx = 0x26;
  2894. break;
  2895. case '0000FF':
  2896. $colorIdx = 0x27;
  2897. break;
  2898. case '00CCFF':
  2899. $colorIdx = 0x28;
  2900. break;
  2901. case 'CCFFFF':
  2902. $colorIdx = 0x29;
  2903. break;
  2904. case 'CCFFCC':
  2905. $colorIdx = 0x2A;
  2906. break;
  2907. case 'FFFF99':
  2908. $colorIdx = 0x2B;
  2909. break;
  2910. case '99CCFF':
  2911. $colorIdx = 0x2C;
  2912. break;
  2913. case 'FF99CC':
  2914. $colorIdx = 0x2D;
  2915. break;
  2916. case 'CC99FF':
  2917. $colorIdx = 0x2E;
  2918. break;
  2919. case 'FFCC99':
  2920. $colorIdx = 0x2F;
  2921. break;
  2922. case '3366FF':
  2923. $colorIdx = 0x30;
  2924. break;
  2925. case '33CCCC':
  2926. $colorIdx = 0x31;
  2927. break;
  2928. case '99CC00':
  2929. $colorIdx = 0x32;
  2930. break;
  2931. case 'FFCC00':
  2932. $colorIdx = 0x33;
  2933. break;
  2934. case 'FF9900':
  2935. $colorIdx = 0x34;
  2936. break;
  2937. case 'FF6600':
  2938. $colorIdx = 0x35;
  2939. break;
  2940. case '666699':
  2941. $colorIdx = 0x36;
  2942. break;
  2943. case '969696':
  2944. $colorIdx = 0x37;
  2945. break;
  2946. case '003366':
  2947. $colorIdx = 0x38;
  2948. break;
  2949. case '339966':
  2950. $colorIdx = 0x39;
  2951. break;
  2952. case '003300':
  2953. $colorIdx = 0x3A;
  2954. break;
  2955. case '333300':
  2956. $colorIdx = 0x3B;
  2957. break;
  2958. case '993300':
  2959. $colorIdx = 0x3C;
  2960. break;
  2961. case '993366':
  2962. $colorIdx = 0x3D;
  2963. break;
  2964. case '333399':
  2965. $colorIdx = 0x3E;
  2966. break;
  2967. case '333333':
  2968. $colorIdx = 0x3F;
  2969. break;
  2970. default:
  2971. $colorIdx = 0x00;
  2972. break;
  2973. }
  2974. $dataBlockFont .= pack('V', $colorIdx);
  2975. // Not used (4)
  2976. $dataBlockFont .= pack('V', 0x00000000);
  2977. // Options flags for modified font attributes
  2978. $optionsFlags = 0;
  2979. $optionsFlagsBold = ($conditional->getStyle()->getFont()->getBold() == null ? 1 : 0);
  2980. $optionsFlags |= (1 == $optionsFlagsBold ? 0x00000002 : 0);
  2981. $optionsFlags |= (1 == 1 ? 0x00000008 : 0);
  2982. $optionsFlags |= (1 == 1 ? 0x00000010 : 0);
  2983. $optionsFlags |= (1 == 0 ? 0x00000020 : 0);
  2984. $optionsFlags |= (1 == 1 ? 0x00000080 : 0);
  2985. $dataBlockFont .= pack('V', $optionsFlags);
  2986. // Escapement type
  2987. $dataBlockFont .= pack('V', $fontEscapement);
  2988. // Underline type
  2989. $dataBlockFont .= pack('V', $fontUnderline);
  2990. // Always
  2991. $dataBlockFont .= pack('V', 0x00000000);
  2992. // Always
  2993. $dataBlockFont .= pack('V', 0x00000000);
  2994. // Not used (8)
  2995. $dataBlockFont .= pack('VV', 0x00000000, 0x00000000);
  2996. // Always
  2997. $dataBlockFont .= pack('v', 0x0001);
  2998. }
  2999. if ($bFormatAlign == 1) {
  3000. $blockAlign = 0;
  3001. // Alignment and text break
  3002. switch ($conditional->getStyle()->getAlignment()->getHorizontal()) {
  3003. case PHPExcel_Style_Alignment::HORIZONTAL_GENERAL:
  3004. $blockAlign = 0;
  3005. break;
  3006. case PHPExcel_Style_Alignment::HORIZONTAL_LEFT:
  3007. $blockAlign = 1;
  3008. break;
  3009. case PHPExcel_Style_Alignment::HORIZONTAL_RIGHT:
  3010. $blockAlign = 3;
  3011. break;
  3012. case PHPExcel_Style_Alignment::HORIZONTAL_CENTER:
  3013. $blockAlign = 2;
  3014. break;
  3015. case PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS:
  3016. $blockAlign = 6;
  3017. break;
  3018. case PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY:
  3019. $blockAlign = 5;
  3020. break;
  3021. }
  3022. if ($conditional->getStyle()->getAlignment()->getWrapText() == true) {
  3023. $blockAlign |= 1 << 3;
  3024. } else {
  3025. $blockAlign |= 0 << 3;
  3026. }
  3027. switch ($conditional->getStyle()->getAlignment()->getVertical()) {
  3028. case PHPExcel_Style_Alignment::VERTICAL_BOTTOM:
  3029. $blockAlign = 2 << 4;
  3030. break;
  3031. case PHPExcel_Style_Alignment::VERTICAL_TOP:
  3032. $blockAlign = 0 << 4;
  3033. break;
  3034. case PHPExcel_Style_Alignment::VERTICAL_CENTER:
  3035. $blockAlign = 1 << 4;
  3036. break;
  3037. case PHPExcel_Style_Alignment::VERTICAL_JUSTIFY:
  3038. $blockAlign = 3 << 4;
  3039. break;
  3040. }
  3041. $blockAlign |= 0 << 7;
  3042. // Text rotation angle
  3043. $blockRotation = $conditional->getStyle()->getAlignment()->getTextRotation();
  3044. // Indentation
  3045. $blockIndent = $conditional->getStyle()->getAlignment()->getIndent();
  3046. if ($conditional->getStyle()->getAlignment()->getShrinkToFit() == true) {
  3047. $blockIndent |= 1 << 4;
  3048. } else {
  3049. $blockIndent |= 0 << 4;
  3050. }
  3051. $blockIndent |= 0 << 6;
  3052. // Relative indentation
  3053. $blockIndentRelative = 255;
  3054. $dataBlockAlign = pack('CCvvv', $blockAlign, $blockRotation, $blockIndent, $blockIndentRelative, 0x0000);
  3055. }
  3056. if ($bFormatBorder == 1) {
  3057. $blockLineStyle = 0;
  3058. switch ($conditional->getStyle()->getBorders()->getLeft()->getBorderStyle()) {
  3059. case PHPExcel_Style_Border::BORDER_NONE:
  3060. $blockLineStyle |= 0x00;
  3061. break;
  3062. case PHPExcel_Style_Border::BORDER_THIN:
  3063. $blockLineStyle |= 0x01;
  3064. break;
  3065. case PHPExcel_Style_Border::BORDER_MEDIUM:
  3066. $blockLineStyle |= 0x02;
  3067. break;
  3068. case PHPExcel_Style_Border::BORDER_DASHED:
  3069. $blockLineStyle |= 0x03;
  3070. break;
  3071. case PHPExcel_Style_Border::BORDER_DOTTED:
  3072. $blockLineStyle |= 0x04;
  3073. break;
  3074. case PHPExcel_Style_Border::BORDER_THICK:
  3075. $blockLineStyle |= 0x05;
  3076. break;
  3077. case PHPExcel_Style_Border::BORDER_DOUBLE:
  3078. $blockLineStyle |= 0x06;
  3079. break;
  3080. case PHPExcel_Style_Border::BORDER_HAIR:
  3081. $blockLineStyle |= 0x07;
  3082. break;
  3083. case PHPExcel_Style_Border::BORDER_MEDIUMDASHED:
  3084. $blockLineStyle |= 0x08;
  3085. break;
  3086. case PHPExcel_Style_Border::BORDER_DASHDOT:
  3087. $blockLineStyle |= 0x09;
  3088. break;
  3089. case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT:
  3090. $blockLineStyle |= 0x0A;
  3091. break;
  3092. case PHPExcel_Style_Border::BORDER_DASHDOTDOT:
  3093. $blockLineStyle |= 0x0B;
  3094. break;
  3095. case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT:
  3096. $blockLineStyle |= 0x0C;
  3097. break;
  3098. case PHPExcel_Style_Border::BORDER_SLANTDASHDOT:
  3099. $blockLineStyle |= 0x0D;
  3100. break;
  3101. }
  3102. switch ($conditional->getStyle()->getBorders()->getRight()->getBorderStyle()) {
  3103. case PHPExcel_Style_Border::BORDER_NONE:
  3104. $blockLineStyle |= 0x00 << 4;
  3105. break;
  3106. case PHPExcel_Style_Border::BORDER_THIN:
  3107. $blockLineStyle |= 0x01 << 4;
  3108. break;
  3109. case PHPExcel_Style_Border::BORDER_MEDIUM:
  3110. $blockLineStyle |= 0x02 << 4;
  3111. break;
  3112. case PHPExcel_Style_Border::BORDER_DASHED:
  3113. $blockLineStyle |= 0x03 << 4;
  3114. break;
  3115. case PHPExcel_Style_Border::BORDER_DOTTED:
  3116. $blockLineStyle |= 0x04 << 4;
  3117. break;
  3118. case PHPExcel_Style_Border::BORDER_THICK:
  3119. $blockLineStyle |= 0x05 << 4;
  3120. break;
  3121. case PHPExcel_Style_Border::BORDER_DOUBLE:
  3122. $blockLineStyle |= 0x06 << 4;
  3123. break;
  3124. case PHPExcel_Style_Border::BORDER_HAIR:
  3125. $blockLineStyle |= 0x07 << 4;
  3126. break;
  3127. case PHPExcel_Style_Border::BORDER_MEDIUMDASHED:
  3128. $blockLineStyle |= 0x08 << 4;
  3129. break;
  3130. case PHPExcel_Style_Border::BORDER_DASHDOT:
  3131. $blockLineStyle |= 0x09 << 4;
  3132. break;
  3133. case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT:
  3134. $blockLineStyle |= 0x0A << 4;
  3135. break;
  3136. case PHPExcel_Style_Border::BORDER_DASHDOTDOT:
  3137. $blockLineStyle |= 0x0B << 4;
  3138. break;
  3139. case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT:
  3140. $blockLineStyle |= 0x0C << 4;
  3141. break;
  3142. case PHPExcel_Style_Border::BORDER_SLANTDASHDOT:
  3143. $blockLineStyle |= 0x0D << 4;
  3144. break;
  3145. }
  3146. switch ($conditional->getStyle()->getBorders()->getTop()->getBorderStyle()) {
  3147. case PHPExcel_Style_Border::BORDER_NONE:
  3148. $blockLineStyle |= 0x00 << 8;
  3149. break;
  3150. case PHPExcel_Style_Border::BORDER_THIN:
  3151. $blockLineStyle |= 0x01 << 8;
  3152. break;
  3153. case PHPExcel_Style_Border::BORDER_MEDIUM:
  3154. $blockLineStyle |= 0x02 << 8;
  3155. break;
  3156. case PHPExcel_Style_Border::BORDER_DASHED:
  3157. $blockLineStyle |= 0x03 << 8;
  3158. break;
  3159. case PHPExcel_Style_Border::BORDER_DOTTED:
  3160. $blockLineStyle |= 0x04 << 8;
  3161. break;
  3162. case PHPExcel_Style_Border::BORDER_THICK:
  3163. $blockLineStyle |= 0x05 << 8;
  3164. break;
  3165. case PHPExcel_Style_Border::BORDER_DOUBLE:
  3166. $blockLineStyle |= 0x06 << 8;
  3167. break;
  3168. case PHPExcel_Style_Border::BORDER_HAIR:
  3169. $blockLineStyle |= 0x07 << 8;
  3170. break;
  3171. case PHPExcel_Style_Border::BORDER_MEDIUMDASHED:
  3172. $blockLineStyle |= 0x08 << 8;
  3173. break;
  3174. case PHPExcel_Style_Border::BORDER_DASHDOT:
  3175. $blockLineStyle |= 0x09 << 8;
  3176. break;
  3177. case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT:
  3178. $blockLineStyle |= 0x0A << 8;
  3179. break;
  3180. case PHPExcel_Style_Border::BORDER_DASHDOTDOT:
  3181. $blockLineStyle |= 0x0B << 8;
  3182. break;
  3183. case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT:
  3184. $blockLineStyle |= 0x0C << 8;
  3185. break;
  3186. case PHPExcel_Style_Border::BORDER_SLANTDASHDOT:
  3187. $blockLineStyle |= 0x0D << 8;
  3188. break;
  3189. }
  3190. switch ($conditional->getStyle()->getBorders()->getBottom()->getBorderStyle()) {
  3191. case PHPExcel_Style_Border::BORDER_NONE:
  3192. $blockLineStyle |= 0x00 << 12;
  3193. break;
  3194. case PHPExcel_Style_Border::BORDER_THIN:
  3195. $blockLineStyle |= 0x01 << 12;
  3196. break;
  3197. case PHPExcel_Style_Border::BORDER_MEDIUM:
  3198. $blockLineStyle |= 0x02 << 12;
  3199. break;
  3200. case PHPExcel_Style_Border::BORDER_DASHED:
  3201. $blockLineStyle |= 0x03 << 12;
  3202. break;
  3203. case PHPExcel_Style_Border::BORDER_DOTTED:
  3204. $blockLineStyle |= 0x04 << 12;
  3205. break;
  3206. case PHPExcel_Style_Border::BORDER_THICK:
  3207. $blockLineStyle |= 0x05 << 12;
  3208. break;
  3209. case PHPExcel_Style_Border::BORDER_DOUBLE:
  3210. $blockLineStyle |= 0x06 << 12;
  3211. break;
  3212. case PHPExcel_Style_Border::BORDER_HAIR:
  3213. $blockLineStyle |= 0x07 << 12;
  3214. break;
  3215. case PHPExcel_Style_Border::BORDER_MEDIUMDASHED:
  3216. $blockLineStyle |= 0x08 << 12;
  3217. break;
  3218. case PHPExcel_Style_Border::BORDER_DASHDOT:
  3219. $blockLineStyle |= 0x09 << 12;
  3220. break;
  3221. case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT:
  3222. $blockLineStyle |= 0x0A << 12;
  3223. break;
  3224. case PHPExcel_Style_Border::BORDER_DASHDOTDOT:
  3225. $blockLineStyle |= 0x0B << 12;
  3226. break;
  3227. case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT:
  3228. $blockLineStyle |= 0x0C << 12;
  3229. break;
  3230. case PHPExcel_Style_Border::BORDER_SLANTDASHDOT:
  3231. $blockLineStyle |= 0x0D << 12;
  3232. break;
  3233. }
  3234. //@todo writeCFRule() => $blockLineStyle => Index Color for left line
  3235. //@todo writeCFRule() => $blockLineStyle => Index Color for right line
  3236. //@todo writeCFRule() => $blockLineStyle => Top-left to bottom-right on/off
  3237. //@todo writeCFRule() => $blockLineStyle => Bottom-left to top-right on/off
  3238. $blockColor = 0;
  3239. //@todo writeCFRule() => $blockColor => Index Color for top line
  3240. //@todo writeCFRule() => $blockColor => Index Color for bottom line
  3241. //@todo writeCFRule() => $blockColor => Index Color for diagonal line
  3242. switch ($conditional->getStyle()->getBorders()->getDiagonal()->getBorderStyle()) {
  3243. case PHPExcel_Style_Border::BORDER_NONE:
  3244. $blockColor |= 0x00 << 21;
  3245. break;
  3246. case PHPExcel_Style_Border::BORDER_THIN:
  3247. $blockColor |= 0x01 << 21;
  3248. break;
  3249. case PHPExcel_Style_Border::BORDER_MEDIUM:
  3250. $blockColor |= 0x02 << 21;
  3251. break;
  3252. case PHPExcel_Style_Border::BORDER_DASHED:
  3253. $blockColor |= 0x03 << 21;
  3254. break;
  3255. case PHPExcel_Style_Border::BORDER_DOTTED:
  3256. $blockColor |= 0x04 << 21;
  3257. break;
  3258. case PHPExcel_Style_Border::BORDER_THICK:
  3259. $blockColor |= 0x05 << 21;
  3260. break;
  3261. case PHPExcel_Style_Border::BORDER_DOUBLE:
  3262. $blockColor |= 0x06 << 21;
  3263. break;
  3264. case PHPExcel_Style_Border::BORDER_HAIR:
  3265. $blockColor |= 0x07 << 21;
  3266. break;
  3267. case PHPExcel_Style_Border::BORDER_MEDIUMDASHED:
  3268. $blockColor |= 0x08 << 21;
  3269. break;
  3270. case PHPExcel_Style_Border::BORDER_DASHDOT:
  3271. $blockColor |= 0x09 << 21;
  3272. break;
  3273. case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT:
  3274. $blockColor |= 0x0A << 21;
  3275. break;
  3276. case PHPExcel_Style_Border::BORDER_DASHDOTDOT:
  3277. $blockColor |= 0x0B << 21;
  3278. break;
  3279. case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT:
  3280. $blockColor |= 0x0C << 21;
  3281. break;
  3282. case PHPExcel_Style_Border::BORDER_SLANTDASHDOT:
  3283. $blockColor |= 0x0D << 21;
  3284. break;
  3285. }
  3286. $dataBlockBorder = pack('vv', $blockLineStyle, $blockColor);
  3287. }
  3288. if ($bFormatFill == 1) {
  3289. // Fill Patern Style
  3290. $blockFillPatternStyle = 0;
  3291. switch ($conditional->getStyle()->getFill()->getFillType()) {
  3292. case PHPExcel_Style_Fill::FILL_NONE:
  3293. $blockFillPatternStyle = 0x00;
  3294. break;
  3295. case PHPExcel_Style_Fill::FILL_SOLID:
  3296. $blockFillPatternStyle = 0x01;
  3297. break;
  3298. case PHPExcel_Style_Fill::FILL_PATTERN_MEDIUMGRAY:
  3299. $blockFillPatternStyle = 0x02;
  3300. break;
  3301. case PHPExcel_Style_Fill::FILL_PATTERN_DARKGRAY:
  3302. $blockFillPatternStyle = 0x03;
  3303. break;
  3304. case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRAY:
  3305. $blockFillPatternStyle = 0x04;
  3306. break;
  3307. case PHPExcel_Style_Fill::FILL_PATTERN_DARKHORIZONTAL:
  3308. $blockFillPatternStyle = 0x05;
  3309. break;
  3310. case PHPExcel_Style_Fill::FILL_PATTERN_DARKVERTICAL:
  3311. $blockFillPatternStyle = 0x06;
  3312. break;
  3313. case PHPExcel_Style_Fill::FILL_PATTERN_DARKDOWN:
  3314. $blockFillPatternStyle = 0x07;
  3315. break;
  3316. case PHPExcel_Style_Fill::FILL_PATTERN_DARKUP:
  3317. $blockFillPatternStyle = 0x08;
  3318. break;
  3319. case PHPExcel_Style_Fill::FILL_PATTERN_DARKGRID:
  3320. $blockFillPatternStyle = 0x09;
  3321. break;
  3322. case PHPExcel_Style_Fill::FILL_PATTERN_DARKTRELLIS:
  3323. $blockFillPatternStyle = 0x0A;
  3324. break;
  3325. case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTHORIZONTAL:
  3326. $blockFillPatternStyle = 0x0B;
  3327. break;
  3328. case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTVERTICAL:
  3329. $blockFillPatternStyle = 0x0C;
  3330. break;
  3331. case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTDOWN:
  3332. $blockFillPatternStyle = 0x0D;
  3333. break;
  3334. case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTUP:
  3335. $blockFillPatternStyle = 0x0E;
  3336. break;
  3337. case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRID:
  3338. $blockFillPatternStyle = 0x0F;
  3339. break;
  3340. case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTTRELLIS:
  3341. $blockFillPatternStyle = 0x10;
  3342. break;
  3343. case PHPExcel_Style_Fill::FILL_PATTERN_GRAY125:
  3344. $blockFillPatternStyle = 0x11;
  3345. break;
  3346. case PHPExcel_Style_Fill::FILL_PATTERN_GRAY0625:
  3347. $blockFillPatternStyle = 0x12;
  3348. break;
  3349. case PHPExcel_Style_Fill::FILL_GRADIENT_LINEAR:
  3350. $blockFillPatternStyle = 0x00;
  3351. break; // does not exist in BIFF8
  3352. case PHPExcel_Style_Fill::FILL_GRADIENT_PATH:
  3353. $blockFillPatternStyle = 0x00;
  3354. break; // does not exist in BIFF8
  3355. default:
  3356. $blockFillPatternStyle = 0x00;
  3357. break;
  3358. }
  3359. // Color
  3360. switch ($conditional->getStyle()->getFill()->getStartColor()->getRGB()) {
  3361. case '000000':
  3362. $colorIdxBg = 0x08;
  3363. break;
  3364. case 'FFFFFF':
  3365. $colorIdxBg = 0x09;
  3366. break;
  3367. case 'FF0000':
  3368. $colorIdxBg = 0x0A;
  3369. break;
  3370. case '00FF00':
  3371. $colorIdxBg = 0x0B;
  3372. break;
  3373. case '0000FF':
  3374. $colorIdxBg = 0x0C;
  3375. break;
  3376. case 'FFFF00':
  3377. $colorIdxBg = 0x0D;
  3378. break;
  3379. case 'FF00FF':
  3380. $colorIdxBg = 0x0E;
  3381. break;
  3382. case '00FFFF':
  3383. $colorIdxBg = 0x0F;
  3384. break;
  3385. case '800000':
  3386. $colorIdxBg = 0x10;
  3387. break;
  3388. case '008000':
  3389. $colorIdxBg = 0x11;
  3390. break;
  3391. case '000080':
  3392. $colorIdxBg = 0x12;
  3393. break;
  3394. case '808000':
  3395. $colorIdxBg = 0x13;
  3396. break;
  3397. case '800080':
  3398. $colorIdxBg = 0x14;
  3399. break;
  3400. case '008080':
  3401. $colorIdxBg = 0x15;
  3402. break;
  3403. case 'C0C0C0':
  3404. $colorIdxBg = 0x16;
  3405. break;
  3406. case '808080':
  3407. $colorIdxBg = 0x17;
  3408. break;
  3409. case '9999FF':
  3410. $colorIdxBg = 0x18;
  3411. break;
  3412. case '993366':
  3413. $colorIdxBg = 0x19;
  3414. break;
  3415. case 'FFFFCC':
  3416. $colorIdxBg = 0x1A;
  3417. break;
  3418. case 'CCFFFF':
  3419. $colorIdxBg = 0x1B;
  3420. break;
  3421. case '660066':
  3422. $colorIdxBg = 0x1C;
  3423. break;
  3424. case 'FF8080':
  3425. $colorIdxBg = 0x1D;
  3426. break;
  3427. case '0066CC':
  3428. $colorIdxBg = 0x1E;
  3429. break;
  3430. case 'CCCCFF':
  3431. $colorIdxBg = 0x1F;
  3432. break;
  3433. case '000080':
  3434. $colorIdxBg = 0x20;
  3435. break;
  3436. case 'FF00FF':
  3437. $colorIdxBg = 0x21;
  3438. break;
  3439. case 'FFFF00':
  3440. $colorIdxBg = 0x22;
  3441. break;
  3442. case '00FFFF':
  3443. $colorIdxBg = 0x23;
  3444. break;
  3445. case '800080':
  3446. $colorIdxBg = 0x24;
  3447. break;
  3448. case '800000':
  3449. $colorIdxBg = 0x25;
  3450. break;
  3451. case '008080':
  3452. $colorIdxBg = 0x26;
  3453. break;
  3454. case '0000FF':
  3455. $colorIdxBg = 0x27;
  3456. break;
  3457. case '00CCFF':
  3458. $colorIdxBg = 0x28;
  3459. break;
  3460. case 'CCFFFF':
  3461. $colorIdxBg = 0x29;
  3462. break;
  3463. case 'CCFFCC':
  3464. $colorIdxBg = 0x2A;
  3465. break;
  3466. case 'FFFF99':
  3467. $colorIdxBg = 0x2B;
  3468. break;
  3469. case '99CCFF':
  3470. $colorIdxBg = 0x2C;
  3471. break;
  3472. case 'FF99CC':
  3473. $colorIdxBg = 0x2D;
  3474. break;
  3475. case 'CC99FF':
  3476. $colorIdxBg = 0x2E;
  3477. break;
  3478. case 'FFCC99':
  3479. $colorIdxBg = 0x2F;
  3480. break;
  3481. case '3366FF':
  3482. $colorIdxBg = 0x30;
  3483. break;
  3484. case '33CCCC':
  3485. $colorIdxBg = 0x31;
  3486. break;
  3487. case '99CC00':
  3488. $colorIdxBg = 0x32;
  3489. break;
  3490. case 'FFCC00':
  3491. $colorIdxBg = 0x33;
  3492. break;
  3493. case 'FF9900':
  3494. $colorIdxBg = 0x34;
  3495. break;
  3496. case 'FF6600':
  3497. $colorIdxBg = 0x35;
  3498. break;
  3499. case '666699':
  3500. $colorIdxBg = 0x36;
  3501. break;
  3502. case '969696':
  3503. $colorIdxBg = 0x37;
  3504. break;
  3505. case '003366':
  3506. $colorIdxBg = 0x38;
  3507. break;
  3508. case '339966':
  3509. $colorIdxBg = 0x39;
  3510. break;
  3511. case '003300':
  3512. $colorIdxBg = 0x3A;
  3513. break;
  3514. case '333300':
  3515. $colorIdxBg = 0x3B;
  3516. break;
  3517. case '993300':
  3518. $colorIdxBg = 0x3C;
  3519. break;
  3520. case '993366':
  3521. $colorIdxBg = 0x3D;
  3522. break;
  3523. case '333399':
  3524. $colorIdxBg = 0x3E;
  3525. break;
  3526. case '333333':
  3527. $colorIdxBg = 0x3F;
  3528. break;
  3529. default:
  3530. $colorIdxBg = 0x41;
  3531. break;
  3532. }
  3533. // Fg Color
  3534. switch ($conditional->getStyle()->getFill()->getEndColor()->getRGB()) {
  3535. case '000000':
  3536. $colorIdxFg = 0x08;
  3537. break;
  3538. case 'FFFFFF':
  3539. $colorIdxFg = 0x09;
  3540. break;
  3541. case 'FF0000':
  3542. $colorIdxFg = 0x0A;
  3543. break;
  3544. case '00FF00':
  3545. $colorIdxFg = 0x0B;
  3546. break;
  3547. case '0000FF':
  3548. $colorIdxFg = 0x0C;
  3549. break;
  3550. case 'FFFF00':
  3551. $colorIdxFg = 0x0D;
  3552. break;
  3553. case 'FF00FF':
  3554. $colorIdxFg = 0x0E;
  3555. break;
  3556. case '00FFFF':
  3557. $colorIdxFg = 0x0F;
  3558. break;
  3559. case '800000':
  3560. $colorIdxFg = 0x10;
  3561. break;
  3562. case '008000':
  3563. $colorIdxFg = 0x11;
  3564. break;
  3565. case '000080':
  3566. $colorIdxFg = 0x12;
  3567. break;
  3568. case '808000':
  3569. $colorIdxFg = 0x13;
  3570. break;
  3571. case '800080':
  3572. $colorIdxFg = 0x14;
  3573. break;
  3574. case '008080':
  3575. $colorIdxFg = 0x15;
  3576. break;
  3577. case 'C0C0C0':
  3578. $colorIdxFg = 0x16;
  3579. break;
  3580. case '808080':
  3581. $colorIdxFg = 0x17;
  3582. break;
  3583. case '9999FF':
  3584. $colorIdxFg = 0x18;
  3585. break;
  3586. case '993366':
  3587. $colorIdxFg = 0x19;
  3588. break;
  3589. case 'FFFFCC':
  3590. $colorIdxFg = 0x1A;
  3591. break;
  3592. case 'CCFFFF':
  3593. $colorIdxFg = 0x1B;
  3594. break;
  3595. case '660066':
  3596. $colorIdxFg = 0x1C;
  3597. break;
  3598. case 'FF8080':
  3599. $colorIdxFg = 0x1D;
  3600. break;
  3601. case '0066CC':
  3602. $colorIdxFg = 0x1E;
  3603. break;
  3604. case 'CCCCFF':
  3605. $colorIdxFg = 0x1F;
  3606. break;
  3607. case '000080':
  3608. $colorIdxFg = 0x20;
  3609. break;
  3610. case 'FF00FF':
  3611. $colorIdxFg = 0x21;
  3612. break;
  3613. case 'FFFF00':
  3614. $colorIdxFg = 0x22;
  3615. break;
  3616. case '00FFFF':
  3617. $colorIdxFg = 0x23;
  3618. break;
  3619. case '800080':
  3620. $colorIdxFg = 0x24;
  3621. break;
  3622. case '800000':
  3623. $colorIdxFg = 0x25;
  3624. break;
  3625. case '008080':
  3626. $colorIdxFg = 0x26;
  3627. break;
  3628. case '0000FF':
  3629. $colorIdxFg = 0x27;
  3630. break;
  3631. case '00CCFF':
  3632. $colorIdxFg = 0x28;
  3633. break;
  3634. case 'CCFFFF':
  3635. $colorIdxFg = 0x29;
  3636. break;
  3637. case 'CCFFCC':
  3638. $colorIdxFg = 0x2A;
  3639. break;
  3640. case 'FFFF99':
  3641. $colorIdxFg = 0x2B;
  3642. break;
  3643. case '99CCFF':
  3644. $colorIdxFg = 0x2C;
  3645. break;
  3646. case 'FF99CC':
  3647. $colorIdxFg = 0x2D;
  3648. break;
  3649. case 'CC99FF':
  3650. $colorIdxFg = 0x2E;
  3651. break;
  3652. case 'FFCC99':
  3653. $colorIdxFg = 0x2F;
  3654. break;
  3655. case '3366FF':
  3656. $colorIdxFg = 0x30;
  3657. break;
  3658. case '33CCCC':
  3659. $colorIdxFg = 0x31;
  3660. break;
  3661. case '99CC00':
  3662. $colorIdxFg = 0x32;
  3663. break;
  3664. case 'FFCC00':
  3665. $colorIdxFg = 0x33;
  3666. break;
  3667. case 'FF9900':
  3668. $colorIdxFg = 0x34;
  3669. break;
  3670. case 'FF6600':
  3671. $colorIdxFg = 0x35;
  3672. break;
  3673. case '666699':
  3674. $colorIdxFg = 0x36;
  3675. break;
  3676. case '969696':
  3677. $colorIdxFg = 0x37;
  3678. break;
  3679. case '003366':
  3680. $colorIdxFg = 0x38;
  3681. break;
  3682. case '339966':
  3683. $colorIdxFg = 0x39;
  3684. break;
  3685. case '003300':
  3686. $colorIdxFg = 0x3A;
  3687. break;
  3688. case '333300':
  3689. $colorIdxFg = 0x3B;
  3690. break;
  3691. case '993300':
  3692. $colorIdxFg = 0x3C;
  3693. break;
  3694. case '993366':
  3695. $colorIdxFg = 0x3D;
  3696. break;
  3697. case '333399':
  3698. $colorIdxFg = 0x3E;
  3699. break;
  3700. case '333333':
  3701. $colorIdxFg = 0x3F;
  3702. break;
  3703. default:
  3704. $colorIdxFg = 0x40;
  3705. break;
  3706. }
  3707. $dataBlockFill = pack('v', $blockFillPatternStyle);
  3708. $dataBlockFill .= pack('v', $colorIdxFg | ($colorIdxBg << 7));
  3709. }
  3710. if ($bFormatProt == 1) {
  3711. $dataBlockProtection = 0;
  3712. if ($conditional->getStyle()->getProtection()->getLocked() == PHPExcel_Style_Protection::PROTECTION_PROTECTED) {
  3713. $dataBlockProtection = 1;
  3714. }
  3715. if ($conditional->getStyle()->getProtection()->getHidden() == PHPExcel_Style_Protection::PROTECTION_PROTECTED) {
  3716. $dataBlockProtection = 1 << 1;
  3717. }
  3718. }
  3719. $data = pack('CCvvVv', $type, $operatorType, $szValue1, $szValue2, $flags, 0x0000);
  3720. if ($bFormatFont == 1) { // Block Formatting : OK
  3721. $data .= $dataBlockFont;
  3722. }
  3723. if ($bFormatAlign == 1) {
  3724. $data .= $dataBlockAlign;
  3725. }
  3726. if ($bFormatBorder == 1) {
  3727. $data .= $dataBlockBorder;
  3728. }
  3729. if ($bFormatFill == 1) { // Block Formatting : OK
  3730. $data .= $dataBlockFill;
  3731. }
  3732. if ($bFormatProt == 1) {
  3733. $data .= $dataBlockProtection;
  3734. }
  3735. if (!is_null($operand1)) {
  3736. $data .= $operand1;
  3737. }
  3738. if (!is_null($operand2)) {
  3739. $data .= $operand2;
  3740. }
  3741. $header = pack('vv', $record, strlen($data));
  3742. $this->append($header . $data);
  3743. }
  3744. /**
  3745. * Write CFHeader record
  3746. */
  3747. private function writeCFHeader()
  3748. {
  3749. $record = 0x01B0; // Record identifier
  3750. $length = 0x0016; // Bytes to follow
  3751. $numColumnMin = null;
  3752. $numColumnMax = null;
  3753. $numRowMin = null;
  3754. $numRowMax = null;
  3755. $arrConditional = array();
  3756. foreach ($this->phpSheet->getConditionalStylesCollection() as $cellCoordinate => $conditionalStyles) {
  3757. foreach ($conditionalStyles as $conditional) {
  3758. if ($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_EXPRESSION
  3759. || $conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CELLIS) {
  3760. if (!in_array($conditional->getHashCode(), $arrConditional)) {
  3761. $arrConditional[] = $conditional->getHashCode();
  3762. }
  3763. // Cells
  3764. $arrCoord = PHPExcel_Cell::coordinateFromString($cellCoordinate);
  3765. if (!is_numeric($arrCoord[0])) {
  3766. $arrCoord[0] = PHPExcel_Cell::columnIndexFromString($arrCoord[0]);
  3767. }
  3768. if (is_null($numColumnMin) || ($numColumnMin > $arrCoord[0])) {
  3769. $numColumnMin = $arrCoord[0];
  3770. }
  3771. if (is_null($numColumnMax) || ($numColumnMax < $arrCoord[0])) {
  3772. $numColumnMax = $arrCoord[0];
  3773. }
  3774. if (is_null($numRowMin) || ($numRowMin > $arrCoord[1])) {
  3775. $numRowMin = $arrCoord[1];
  3776. }
  3777. if (is_null($numRowMax) || ($numRowMax < $arrCoord[1])) {
  3778. $numRowMax = $arrCoord[1];
  3779. }
  3780. }
  3781. }
  3782. }
  3783. $needRedraw = 1;
  3784. $cellRange = pack('vvvv', $numRowMin-1, $numRowMax-1, $numColumnMin-1, $numColumnMax-1);
  3785. $header = pack('vv', $record, $length);
  3786. $data = pack('vv', count($arrConditional), $needRedraw);
  3787. $data .= $cellRange;
  3788. $data .= pack('v', 0x0001);
  3789. $data .= $cellRange;
  3790. $this->append($header . $data);
  3791. }
  3792. }