Updated gui for user facility settings (#1327)
[openemr.git] / vendor / phpoffice / phpexcel / Classes / PHPExcel / Writer / Excel5 / Worksheet.php
blobfb75499b1185ddbf8b0e8e7cdd831b03ce2b1e99
1 <?php
2 /**
3 * PHPExcel
5 * Copyright (c) 2006 - 2014 PHPExcel
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.
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.
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
21 * @category PHPExcel
22 * @package PHPExcel_Writer_Excel5
23 * @copyright Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
24 * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
25 * @version ##VERSION##, ##DATE##
28 // Original file header of PEAR::Spreadsheet_Excel_Writer_Worksheet (used as the base for this class):
29 // -----------------------------------------------------------------------------------------
30 // /*
31 // * Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
32 // *
33 // * The majority of this is _NOT_ my code. I simply ported it from the
34 // * PERL Spreadsheet::WriteExcel module.
35 // *
36 // * The author of the Spreadsheet::WriteExcel module is John McNamara
37 // * <jmcnamara@cpan.org>
38 // *
39 // * I _DO_ maintain this code, and John McNamara has nothing to do with the
40 // * porting of this code to PHP. Any questions directly related to this
41 // * class library should be directed to me.
42 // *
43 // * License Information:
44 // *
45 // * Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
46 // * Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
47 // *
48 // * This library is free software; you can redistribute it and/or
49 // * modify it under the terms of the GNU Lesser General Public
50 // * License as published by the Free Software Foundation; either
51 // * version 2.1 of the License, or (at your option) any later version.
52 // *
53 // * This library is distributed in the hope that it will be useful,
54 // * but WITHOUT ANY WARRANTY; without even the implied warranty of
55 // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
56 // * Lesser General Public License for more details.
57 // *
58 // * You should have received a copy of the GNU Lesser General Public
59 // * License along with this library; if not, write to the Free Software
60 // * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
61 // */
64 /**
65 * PHPExcel_Writer_Excel5_Worksheet
67 * @category PHPExcel
68 * @package PHPExcel_Writer_Excel5
69 * @copyright Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
71 class PHPExcel_Writer_Excel5_Worksheet extends PHPExcel_Writer_Excel5_BIFFwriter
73 /**
74 * Formula parser
76 * @var PHPExcel_Writer_Excel5_Parser
78 private $_parser;
80 /**
81 * Maximum number of characters for a string (LABEL record in BIFF5)
82 * @var integer
84 public $_xls_strmax;
86 /**
87 * Array containing format information for columns
88 * @var array
90 public $_colinfo;
92 /**
93 * Array containing the selected area for the worksheet
94 * @var array
96 public $_selection;
98 /**
99 * The active pane for the worksheet
100 * @var integer
102 public $_active_pane;
105 * Whether to use outline.
106 * @var integer
108 public $_outline_on;
111 * Auto outline styles.
112 * @var bool
114 public $_outline_style;
117 * Whether to have outline summary below.
118 * @var bool
120 public $_outline_below;
123 * Whether to have outline summary at the right.
124 * @var bool
126 public $_outline_right;
129 * Reference to the total number of strings in the workbook
130 * @var integer
132 public $_str_total;
135 * Reference to the number of unique strings in the workbook
136 * @var integer
138 public $_str_unique;
141 * Reference to the array containing all the unique strings in the workbook
142 * @var array
144 public $_str_table;
147 * Color cache
149 private $_colors;
152 * Index of first used row (at least 0)
153 * @var int
155 private $_firstRowIndex;
158 * Index of last used row. (no used rows means -1)
159 * @var int
161 private $_lastRowIndex;
164 * Index of first used column (at least 0)
165 * @var int
167 private $_firstColumnIndex;
170 * Index of last used column (no used columns means -1)
171 * @var int
173 private $_lastColumnIndex;
176 * Sheet object
177 * @var PHPExcel_Worksheet
179 public $_phpSheet;
182 * Count cell style Xfs
184 * @var int
186 private $_countCellStyleXfs;
189 * Escher object corresponding to MSODRAWING
191 * @var PHPExcel_Shared_Escher
193 private $_escher;
196 * Array of font hashes associated to FONT records index
198 * @var array
200 public $_fntHashIndex;
203 * Constructor
205 * @param int &$str_total Total number of strings
206 * @param int &$str_unique Total number of unique strings
207 * @param array &$str_table String Table
208 * @param array &$colors Colour Table
209 * @param mixed $parser The formula parser created for the Workbook
210 * @param boolean $preCalculateFormulas Flag indicating whether formulas should be calculated or just written
211 * @param string $phpSheet The worksheet to write
212 * @param PHPExcel_Worksheet $phpSheet
214 public function __construct(&$str_total, &$str_unique, &$str_table, &$colors,
215 $parser, $preCalculateFormulas, $phpSheet)
217 // It needs to call its parent's constructor explicitly
218 parent::__construct();
220 // change BIFFwriter limit for CONTINUE records
221 // $this->_limit = 8224;
224 $this->_preCalculateFormulas = $preCalculateFormulas;
225 $this->_str_total = &$str_total;
226 $this->_str_unique = &$str_unique;
227 $this->_str_table = &$str_table;
228 $this->_colors = &$colors;
229 $this->_parser = $parser;
231 $this->_phpSheet = $phpSheet;
233 //$this->ext_sheets = array();
234 //$this->offset = 0;
235 $this->_xls_strmax = 255;
236 $this->_colinfo = array();
237 $this->_selection = array(0,0,0,0);
238 $this->_active_pane = 3;
240 $this->_print_headers = 0;
242 $this->_outline_style = 0;
243 $this->_outline_below = 1;
244 $this->_outline_right = 1;
245 $this->_outline_on = 1;
247 $this->_fntHashIndex = array();
249 // calculate values for DIMENSIONS record
250 $minR = 1;
251 $minC = 'A';
253 $maxR = $this->_phpSheet->getHighestRow();
254 $maxC = $this->_phpSheet->getHighestColumn();
256 // Determine lowest and highest column and row
257 // $this->_firstRowIndex = ($minR > 65535) ? 65535 : $minR;
258 $this->_lastRowIndex = ($maxR > 65535) ? 65535 : $maxR ;
260 $this->_firstColumnIndex = PHPExcel_Cell::columnIndexFromString($minC);
261 $this->_lastColumnIndex = PHPExcel_Cell::columnIndexFromString($maxC);
263 // if ($this->_firstColumnIndex > 255) $this->_firstColumnIndex = 255;
264 if ($this->_lastColumnIndex > 255) $this->_lastColumnIndex = 255;
266 $this->_countCellStyleXfs = count($phpSheet->getParent()->getCellStyleXfCollection());
270 * Add data to the beginning of the workbook (note the reverse order)
271 * and to the end of the workbook.
273 * @access public
274 * @see PHPExcel_Writer_Excel5_Workbook::storeWorkbook()
276 function close()
278 $_phpSheet = $this->_phpSheet;
280 $num_sheets = $_phpSheet->getParent()->getSheetCount();
282 // Write BOF record
283 $this->_storeBof(0x0010);
285 // Write PRINTHEADERS
286 $this->_writePrintHeaders();
288 // Write PRINTGRIDLINES
289 $this->_writePrintGridlines();
291 // Write GRIDSET
292 $this->_writeGridset();
294 // Calculate column widths
295 $_phpSheet->calculateColumnWidths();
297 // Column dimensions
298 if (($defaultWidth = $_phpSheet->getDefaultColumnDimension()->getWidth()) < 0) {
299 $defaultWidth = PHPExcel_Shared_Font::getDefaultColumnWidthByFont($_phpSheet->getParent()->getDefaultStyle()->getFont());
302 $columnDimensions = $_phpSheet->getColumnDimensions();
303 $maxCol = $this->_lastColumnIndex -1;
304 for ($i = 0; $i <= $maxCol; ++$i) {
305 $hidden = 0;
306 $level = 0;
307 $xfIndex = 15; // there are 15 cell style Xfs
309 $width = $defaultWidth;
311 $columnLetter = PHPExcel_Cell::stringFromColumnIndex($i);
312 if (isset($columnDimensions[$columnLetter])) {
313 $columnDimension = $columnDimensions[$columnLetter];
314 if ($columnDimension->getWidth() >= 0) {
315 $width = $columnDimension->getWidth();
317 $hidden = $columnDimension->getVisible() ? 0 : 1;
318 $level = $columnDimension->getOutlineLevel();
319 $xfIndex = $columnDimension->getXfIndex() + 15; // there are 15 cell style Xfs
322 // Components of _colinfo:
323 // $firstcol first column on the range
324 // $lastcol last column on the range
325 // $width width to set
326 // $xfIndex The optional cell style Xf index to apply to the columns
327 // $hidden The optional hidden atribute
328 // $level The optional outline level
329 $this->_colinfo[] = array($i, $i, $width, $xfIndex, $hidden, $level);
332 // Write GUTS
333 $this->_writeGuts();
335 // Write DEFAULTROWHEIGHT
336 $this->_writeDefaultRowHeight();
338 // Write WSBOOL
339 $this->_writeWsbool();
341 // Write horizontal and vertical page breaks
342 $this->_writeBreaks();
344 // Write page header
345 $this->_writeHeader();
347 // Write page footer
348 $this->_writeFooter();
350 // Write page horizontal centering
351 $this->_writeHcenter();
353 // Write page vertical centering
354 $this->_writeVcenter();
356 // Write left margin
357 $this->_writeMarginLeft();
359 // Write right margin
360 $this->_writeMarginRight();
362 // Write top margin
363 $this->_writeMarginTop();
365 // Write bottom margin
366 $this->_writeMarginBottom();
368 // Write page setup
369 $this->_writeSetup();
371 // Write sheet protection
372 $this->_writeProtect();
374 // Write SCENPROTECT
375 $this->_writeScenProtect();
377 // Write OBJECTPROTECT
378 $this->_writeObjectProtect();
380 // Write sheet password
381 $this->_writePassword();
383 // Write DEFCOLWIDTH record
384 $this->_writeDefcol();
386 // Write the COLINFO records if they exist
387 if (!empty($this->_colinfo)) {
388 $colcount = count($this->_colinfo);
389 for ($i = 0; $i < $colcount; ++$i) {
390 $this->_writeColinfo($this->_colinfo[$i]);
393 $autoFilterRange = $_phpSheet->getAutoFilter()->getRange();
394 if (!empty($autoFilterRange)) {
395 // Write AUTOFILTERINFO
396 $this->_writeAutoFilterInfo();
399 // Write sheet dimensions
400 $this->_writeDimensions();
402 // Row dimensions
403 foreach ($_phpSheet->getRowDimensions() as $rowDimension) {
404 $xfIndex = $rowDimension->getXfIndex() + 15; // there are 15 cellXfs
405 $this->_writeRow( $rowDimension->getRowIndex() - 1, $rowDimension->getRowHeight(), $xfIndex, ($rowDimension->getVisible() ? '0' : '1'), $rowDimension->getOutlineLevel() );
408 // Write Cells
409 foreach ($_phpSheet->getCellCollection() as $cellID) {
410 $cell = $_phpSheet->getCell($cellID);
411 $row = $cell->getRow() - 1;
412 $column = PHPExcel_Cell::columnIndexFromString($cell->getColumn()) - 1;
414 // Don't break Excel!
415 // if ($row + 1 > 65536 or $column + 1 > 256) {
416 if ($row > 65535 || $column > 255) {
417 break;
420 // Write cell value
421 $xfIndex = $cell->getXfIndex() + 15; // there are 15 cell style Xfs
423 $cVal = $cell->getValue();
424 if ($cVal instanceof PHPExcel_RichText) {
425 // $this->_writeString($row, $column, $cVal->getPlainText(), $xfIndex);
426 $arrcRun = array();
427 $str_len = PHPExcel_Shared_String::CountCharacters($cVal->getPlainText(), 'UTF-8');
428 $str_pos = 0;
429 $elements = $cVal->getRichTextElements();
430 foreach ($elements as $element) {
431 // FONT Index
432 if ($element instanceof PHPExcel_RichText_Run) {
433 $str_fontidx = $this->_fntHashIndex[$element->getFont()->getHashCode()];
435 else {
436 $str_fontidx = 0;
438 $arrcRun[] = array('strlen' => $str_pos, 'fontidx' => $str_fontidx);
439 // Position FROM
440 $str_pos += PHPExcel_Shared_String::CountCharacters($element->getText(), 'UTF-8');
442 $this->_writeRichTextString($row, $column, $cVal->getPlainText(), $xfIndex, $arrcRun);
443 } else {
444 switch ($cell->getDatatype()) {
445 case PHPExcel_Cell_DataType::TYPE_STRING:
446 case PHPExcel_Cell_DataType::TYPE_NULL:
447 if ($cVal === '' || $cVal === null) {
448 $this->_writeBlank($row, $column, $xfIndex);
449 } else {
450 $this->_writeString($row, $column, $cVal, $xfIndex);
452 break;
454 case PHPExcel_Cell_DataType::TYPE_NUMERIC:
455 $this->_writeNumber($row, $column, $cVal, $xfIndex);
456 break;
458 case PHPExcel_Cell_DataType::TYPE_FORMULA:
459 $calculatedValue = $this->_preCalculateFormulas ?
460 $cell->getCalculatedValue() : null;
461 $this->_writeFormula($row, $column, $cVal, $xfIndex, $calculatedValue);
462 break;
464 case PHPExcel_Cell_DataType::TYPE_BOOL:
465 $this->_writeBoolErr($row, $column, $cVal, 0, $xfIndex);
466 break;
468 case PHPExcel_Cell_DataType::TYPE_ERROR:
469 $this->_writeBoolErr($row, $column, self::_mapErrorCode($cVal), 1, $xfIndex);
470 break;
476 // Append
477 $this->_writeMsoDrawing();
479 // Write WINDOW2 record
480 $this->_writeWindow2();
482 // Write PLV record
483 $this->_writePageLayoutView();
485 // Write ZOOM record
486 $this->_writeZoom();
487 if ($_phpSheet->getFreezePane()) {
488 $this->_writePanes();
491 // Write SELECTION record
492 $this->_writeSelection();
494 // Write MergedCellsTable Record
495 $this->_writeMergedCells();
497 // Hyperlinks
498 foreach ($_phpSheet->getHyperLinkCollection() as $coordinate => $hyperlink) {
499 list($column, $row) = PHPExcel_Cell::coordinateFromString($coordinate);
501 $url = $hyperlink->getUrl();
503 if ( strpos($url, 'sheet://') !== false ) {
504 // internal to current workbook
505 $url = str_replace('sheet://', 'internal:', $url);
507 } else if ( preg_match('/^(http:|https:|ftp:|mailto:)/', $url) ) {
508 // URL
509 // $url = $url;
511 } else {
512 // external (local file)
513 $url = 'external:' . $url;
516 $this->_writeUrl($row - 1, PHPExcel_Cell::columnIndexFromString($column) - 1, $url);
519 $this->_writeDataValidity();
520 $this->_writeSheetLayout();
522 // Write SHEETPROTECTION record
523 $this->_writeSheetProtection();
524 $this->_writeRangeProtection();
526 $arrConditionalStyles = $_phpSheet->getConditionalStylesCollection();
527 if(!empty($arrConditionalStyles)){
528 $arrConditional = array();
529 // @todo CFRule & CFHeader
530 // Write CFHEADER record
531 $this->_writeCFHeader();
532 // Write ConditionalFormattingTable records
533 foreach ($arrConditionalStyles as $cellCoordinate => $conditionalStyles) {
534 foreach ($conditionalStyles as $conditional) {
535 if($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_EXPRESSION
536 || $conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CELLIS){
537 if(!in_array($conditional->getHashCode(), $arrConditional)){
538 $arrConditional[] = $conditional->getHashCode();
539 // Write CFRULE record
540 $this->_writeCFRule($conditional);
547 $this->_storeEof();
551 * Write a cell range address in BIFF8
552 * always fixed range
553 * See section 2.5.14 in OpenOffice.org's Documentation of the Microsoft Excel File Format
555 * @param string $range E.g. 'A1' or 'A1:B6'
556 * @return string Binary data
558 private function _writeBIFF8CellRangeAddressFixed($range = 'A1')
560 $explodes = explode(':', $range);
562 // extract first cell, e.g. 'A1'
563 $firstCell = $explodes[0];
565 // extract last cell, e.g. 'B6'
566 if (count($explodes) == 1) {
567 $lastCell = $firstCell;
568 } else {
569 $lastCell = $explodes[1];
572 $firstCellCoordinates = PHPExcel_Cell::coordinateFromString($firstCell); // e.g. array(0, 1)
573 $lastCellCoordinates = PHPExcel_Cell::coordinateFromString($lastCell); // e.g. array(1, 6)
575 return(pack('vvvv',
576 $firstCellCoordinates[1] - 1,
577 $lastCellCoordinates[1] - 1,
578 PHPExcel_Cell::columnIndexFromString($firstCellCoordinates[0]) - 1,
579 PHPExcel_Cell::columnIndexFromString($lastCellCoordinates[0]) - 1
584 * Retrieves data from memory in one chunk, or from disk in $buffer
585 * sized chunks.
587 * @return string The data
589 function getData()
591 $buffer = 4096;
593 // Return data stored in memory
594 if (isset($this->_data)) {
595 $tmp = $this->_data;
596 unset($this->_data);
597 return $tmp;
599 // No data to return
600 return false;
604 * Set the option to print the row and column headers on the printed page.
606 * @access public
607 * @param integer $print Whether to print the headers or not. Defaults to 1 (print).
609 function printRowColHeaders($print = 1)
611 $this->_print_headers = $print;
615 * This method sets the properties for outlining and grouping. The defaults
616 * correspond to Excel's defaults.
618 * @param bool $visible
619 * @param bool $symbols_below
620 * @param bool $symbols_right
621 * @param bool $auto_style
623 function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false)
625 $this->_outline_on = $visible;
626 $this->_outline_below = $symbols_below;
627 $this->_outline_right = $symbols_right;
628 $this->_outline_style = $auto_style;
630 // Ensure this is a boolean vale for Window2
631 if ($this->_outline_on) {
632 $this->_outline_on = 1;
637 * Write a double to the specified row and column (zero indexed).
638 * An integer can be written as a double. Excel will display an
639 * integer. $format is optional.
641 * Returns 0 : normal termination
642 * -2 : row or column out of range
644 * @param integer $row Zero indexed row
645 * @param integer $col Zero indexed column
646 * @param float $num The number to write
647 * @param mixed $xfIndex The optional XF format
648 * @return integer
650 private function _writeNumber($row, $col, $num, $xfIndex)
652 $record = 0x0203; // Record identifier
653 $length = 0x000E; // Number of bytes to follow
655 $header = pack("vv", $record, $length);
656 $data = pack("vvv", $row, $col, $xfIndex);
657 $xl_double = pack("d", $num);
658 if (self::getByteOrder()) { // if it's Big Endian
659 $xl_double = strrev($xl_double);
662 $this->_append($header.$data.$xl_double);
663 return(0);
667 * Write a LABELSST record or a LABEL record. Which one depends on BIFF version
669 * @param int $row Row index (0-based)
670 * @param int $col Column index (0-based)
671 * @param string $str The string
672 * @param int $xfIndex Index to XF record
674 private function _writeString($row, $col, $str, $xfIndex)
676 $this->_writeLabelSst($row, $col, $str, $xfIndex);
680 * Write a LABELSST record or a LABEL record. Which one depends on BIFF version
681 * It differs from _writeString by the writing of rich text strings.
682 * @param int $row Row index (0-based)
683 * @param int $col Column index (0-based)
684 * @param string $str The string
685 * @param mixed $xfIndex The XF format index for the cell
686 * @param array $arrcRun Index to Font record and characters beginning
688 private function _writeRichTextString($row, $col, $str, $xfIndex, $arrcRun){
689 $record = 0x00FD; // Record identifier
690 $length = 0x000A; // Bytes to follow
691 $str = PHPExcel_Shared_String::UTF8toBIFF8UnicodeShort($str, $arrcRun);
693 /* check if string is already present */
694 if (!isset($this->_str_table[$str])) {
695 $this->_str_table[$str] = $this->_str_unique++;
697 $this->_str_total++;
699 $header = pack('vv', $record, $length);
700 $data = pack('vvvV', $row, $col, $xfIndex, $this->_str_table[$str]);
701 $this->_append($header.$data);
705 * Write a string to the specified row and column (zero indexed).
706 * NOTE: there is an Excel 5 defined limit of 255 characters.
707 * $format is optional.
708 * Returns 0 : normal termination
709 * -2 : row or column out of range
710 * -3 : long string truncated to 255 chars
712 * @access public
713 * @param integer $row Zero indexed row
714 * @param integer $col Zero indexed column
715 * @param string $str The string to write
716 * @param mixed $xfIndex The XF format index for the cell
717 * @return integer
719 private function _writeLabel($row, $col, $str, $xfIndex)
721 $strlen = strlen($str);
722 $record = 0x0204; // Record identifier
723 $length = 0x0008 + $strlen; // Bytes to follow
725 $str_error = 0;
727 if ($strlen > $this->_xls_strmax) { // LABEL must be < 255 chars
728 $str = substr($str, 0, $this->_xls_strmax);
729 $length = 0x0008 + $this->_xls_strmax;
730 $strlen = $this->_xls_strmax;
731 $str_error = -3;
734 $header = pack("vv", $record, $length);
735 $data = pack("vvvv", $row, $col, $xfIndex, $strlen);
736 $this->_append($header . $data . $str);
737 return($str_error);
741 * Write a string to the specified row and column (zero indexed).
742 * This is the BIFF8 version (no 255 chars limit).
743 * $format is optional.
744 * Returns 0 : normal termination
745 * -2 : row or column out of range
746 * -3 : long string truncated to 255 chars
748 * @access public
749 * @param integer $row Zero indexed row
750 * @param integer $col Zero indexed column
751 * @param string $str The string to write
752 * @param mixed $xfIndex The XF format index for the cell
753 * @return integer
755 private function _writeLabelSst($row, $col, $str, $xfIndex)
757 $record = 0x00FD; // Record identifier
758 $length = 0x000A; // Bytes to follow
760 $str = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($str);
762 /* check if string is already present */
763 if (!isset($this->_str_table[$str])) {
764 $this->_str_table[$str] = $this->_str_unique++;
766 $this->_str_total++;
768 $header = pack('vv', $record, $length);
769 $data = pack('vvvV', $row, $col, $xfIndex, $this->_str_table[$str]);
770 $this->_append($header.$data);
774 * Writes a note associated with the cell given by the row and column.
775 * NOTE records don't have a length limit.
777 * @param integer $row Zero indexed row
778 * @param integer $col Zero indexed column
779 * @param string $note The note to write
781 private function _writeNote($row, $col, $note)
783 $note_length = strlen($note);
784 $record = 0x001C; // Record identifier
785 $max_length = 2048; // Maximun length for a NOTE record
787 // Length for this record is no more than 2048 + 6
788 $length = 0x0006 + min($note_length, 2048);
789 $header = pack("vv", $record, $length);
790 $data = pack("vvv", $row, $col, $note_length);
791 $this->_append($header . $data . substr($note, 0, 2048));
793 for ($i = $max_length; $i < $note_length; $i += $max_length) {
794 $chunk = substr($note, $i, $max_length);
795 $length = 0x0006 + strlen($chunk);
796 $header = pack("vv", $record, $length);
797 $data = pack("vvv", -1, 0, strlen($chunk));
798 $this->_append($header.$data.$chunk);
800 return(0);
804 * Write a blank cell to the specified row and column (zero indexed).
805 * A blank cell is used to specify formatting without adding a string
806 * or a number.
808 * A blank cell without a format serves no purpose. Therefore, we don't write
809 * a BLANK record unless a format is specified.
811 * Returns 0 : normal termination (including no format)
812 * -1 : insufficient number of arguments
813 * -2 : row or column out of range
815 * @param integer $row Zero indexed row
816 * @param integer $col Zero indexed column
817 * @param mixed $xfIndex The XF format index
819 function _writeBlank($row, $col, $xfIndex)
821 $record = 0x0201; // Record identifier
822 $length = 0x0006; // Number of bytes to follow
824 $header = pack("vv", $record, $length);
825 $data = pack("vvv", $row, $col, $xfIndex);
826 $this->_append($header . $data);
827 return 0;
831 * Write a boolean or an error type to the specified row and column (zero indexed)
833 * @param int $row Row index (0-based)
834 * @param int $col Column index (0-based)
835 * @param int $value
836 * @param boolean $isError Error or Boolean?
837 * @param int $xfIndex
839 private function _writeBoolErr($row, $col, $value, $isError, $xfIndex)
841 $record = 0x0205;
842 $length = 8;
844 $header = pack("vv", $record, $length);
845 $data = pack("vvvCC", $row, $col, $xfIndex, $value, $isError);
846 $this->_append($header . $data);
847 return 0;
851 * Write a formula to the specified row and column (zero indexed).
852 * The textual representation of the formula is passed to the parser in
853 * Parser.php which returns a packed binary string.
855 * Returns 0 : normal termination
856 * -1 : formula errors (bad formula)
857 * -2 : row or column out of range
859 * @param integer $row Zero indexed row
860 * @param integer $col Zero indexed column
861 * @param string $formula The formula text string
862 * @param mixed $xfIndex The XF format index
863 * @param mixed $calculatedValue Calculated value
864 * @return integer
866 private function _writeFormula($row, $col, $formula, $xfIndex, $calculatedValue)
868 $record = 0x0006; // Record identifier
870 // Initialize possible additional value for STRING record that should be written after the FORMULA record?
871 $stringValue = null;
873 // calculated value
874 if (isset($calculatedValue)) {
875 // Since we can't yet get the data type of the calculated value,
876 // we use best effort to determine data type
877 if (is_bool($calculatedValue)) {
878 // Boolean value
879 $num = pack('CCCvCv', 0x01, 0x00, (int)$calculatedValue, 0x00, 0x00, 0xFFFF);
880 } elseif (is_int($calculatedValue) || is_float($calculatedValue)) {
881 // Numeric value
882 $num = pack('d', $calculatedValue);
883 } elseif (is_string($calculatedValue)) {
884 if (array_key_exists($calculatedValue, PHPExcel_Cell_DataType::getErrorCodes())) {
885 // Error value
886 $num = pack('CCCvCv', 0x02, 0x00, self::_mapErrorCode($calculatedValue), 0x00, 0x00, 0xFFFF);
887 } elseif ($calculatedValue === '') {
888 // Empty string (and BIFF8)
889 $num = pack('CCCvCv', 0x03, 0x00, 0x00, 0x00, 0x00, 0xFFFF);
890 } else {
891 // Non-empty string value (or empty string BIFF5)
892 $stringValue = $calculatedValue;
893 $num = pack('CCCvCv', 0x00, 0x00, 0x00, 0x00, 0x00, 0xFFFF);
895 } else {
896 // We are really not supposed to reach here
897 $num = pack('d', 0x00);
899 } else {
900 $num = pack('d', 0x00);
903 $grbit = 0x03; // Option flags
904 $unknown = 0x0000; // Must be zero
906 // Strip the '=' or '@' sign at the beginning of the formula string
907 if ($formula{0} == '=') {
908 $formula = substr($formula,1);
909 } else {
910 // Error handling
911 $this->_writeString($row, $col, 'Unrecognised character for formula');
912 return -1;
915 // Parse the formula using the parser in Parser.php
916 try {
917 $error = $this->_parser->parse($formula);
918 $formula = $this->_parser->toReversePolish();
920 $formlen = strlen($formula); // Length of the binary string
921 $length = 0x16 + $formlen; // Length of the record data
923 $header = pack("vv", $record, $length);
925 $data = pack("vvv", $row, $col, $xfIndex)
926 . $num
927 . pack("vVv", $grbit, $unknown, $formlen);
928 $this->_append($header . $data . $formula);
930 // Append also a STRING record if necessary
931 if ($stringValue !== null) {
932 $this->_writeStringRecord($stringValue);
935 return 0;
937 } catch (PHPExcel_Exception $e) {
938 // do nothing
944 * Write a STRING record. This
946 * @param string $stringValue
948 private function _writeStringRecord($stringValue)
950 $record = 0x0207; // Record identifier
951 $data = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($stringValue);
953 $length = strlen($data);
954 $header = pack('vv', $record, $length);
956 $this->_append($header . $data);
960 * Write a hyperlink.
961 * This is comprised of two elements: the visible label and
962 * the invisible link. The visible label is the same as the link unless an
963 * alternative string is specified. The label is written using the
964 * _writeString() method. Therefore the 255 characters string limit applies.
965 * $string and $format are optional.
967 * The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external
968 * directory url.
970 * Returns 0 : normal termination
971 * -2 : row or column out of range
972 * -3 : long string truncated to 255 chars
974 * @param integer $row Row
975 * @param integer $col Column
976 * @param string $url URL string
977 * @return integer
979 private function _writeUrl($row, $col, $url)
981 // Add start row and col to arg list
982 return($this->_writeUrlRange($row, $col, $row, $col, $url));
986 * This is the more general form of _writeUrl(). It allows a hyperlink to be
987 * written to a range of cells. This function also decides the type of hyperlink
988 * to be written. These are either, Web (http, ftp, mailto), Internal
989 * (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1').
991 * @access private
992 * @see _writeUrl()
993 * @param integer $row1 Start row
994 * @param integer $col1 Start column
995 * @param integer $row2 End row
996 * @param integer $col2 End column
997 * @param string $url URL string
998 * @return integer
1000 function _writeUrlRange($row1, $col1, $row2, $col2, $url)
1002 // Check for internal/external sheet links or default to web link
1003 if (preg_match('[^internal:]', $url)) {
1004 return($this->_writeUrlInternal($row1, $col1, $row2, $col2, $url));
1006 if (preg_match('[^external:]', $url)) {
1007 return($this->_writeUrlExternal($row1, $col1, $row2, $col2, $url));
1009 return($this->_writeUrlWeb($row1, $col1, $row2, $col2, $url));
1013 * Used to write http, ftp and mailto hyperlinks.
1014 * The link type ($options) is 0x03 is the same as absolute dir ref without
1015 * sheet. However it is differentiated by the $unknown2 data stream.
1017 * @access private
1018 * @see _writeUrl()
1019 * @param integer $row1 Start row
1020 * @param integer $col1 Start column
1021 * @param integer $row2 End row
1022 * @param integer $col2 End column
1023 * @param string $url URL string
1024 * @return integer
1026 function _writeUrlWeb($row1, $col1, $row2, $col2, $url)
1028 $record = 0x01B8; // Record identifier
1029 $length = 0x00000; // Bytes to follow
1031 // Pack the undocumented parts of the hyperlink stream
1032 $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
1033 $unknown2 = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B");
1035 // Pack the option flags
1036 $options = pack("V", 0x03);
1038 // Convert URL to a null terminated wchar string
1039 $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
1040 $url = $url . "\0\0\0";
1042 // Pack the length of the URL
1043 $url_len = pack("V", strlen($url));
1045 // Calculate the data length
1046 $length = 0x34 + strlen($url);
1048 // Pack the header data
1049 $header = pack("vv", $record, $length);
1050 $data = pack("vvvv", $row1, $row2, $col1, $col2);
1052 // Write the packed data
1053 $this->_append($header . $data .
1054 $unknown1 . $options .
1055 $unknown2 . $url_len . $url);
1056 return 0;
1060 * Used to write internal reference hyperlinks such as "Sheet1!A1".
1062 * @access private
1063 * @see _writeUrl()
1064 * @param integer $row1 Start row
1065 * @param integer $col1 Start column
1066 * @param integer $row2 End row
1067 * @param integer $col2 End column
1068 * @param string $url URL string
1069 * @return integer
1071 function _writeUrlInternal($row1, $col1, $row2, $col2, $url)
1073 $record = 0x01B8; // Record identifier
1074 $length = 0x00000; // Bytes to follow
1076 // Strip URL type
1077 $url = preg_replace('/^internal:/', '', $url);
1079 // Pack the undocumented parts of the hyperlink stream
1080 $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
1082 // Pack the option flags
1083 $options = pack("V", 0x08);
1085 // Convert the URL type and to a null terminated wchar string
1086 $url .= "\0";
1088 // character count
1089 $url_len = PHPExcel_Shared_String::CountCharacters($url);
1090 $url_len = pack('V', $url_len);
1092 $url = PHPExcel_Shared_String::ConvertEncoding($url, 'UTF-16LE', 'UTF-8');
1094 // Calculate the data length
1095 $length = 0x24 + strlen($url);
1097 // Pack the header data
1098 $header = pack("vv", $record, $length);
1099 $data = pack("vvvv", $row1, $row2, $col1, $col2);
1101 // Write the packed data
1102 $this->_append($header . $data .
1103 $unknown1 . $options .
1104 $url_len . $url);
1105 return 0;
1109 * Write links to external directory names such as 'c:\foo.xls',
1110 * c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'.
1112 * Note: Excel writes some relative links with the $dir_long string. We ignore
1113 * these cases for the sake of simpler code.
1115 * @access private
1116 * @see _writeUrl()
1117 * @param integer $row1 Start row
1118 * @param integer $col1 Start column
1119 * @param integer $row2 End row
1120 * @param integer $col2 End column
1121 * @param string $url URL string
1122 * @return integer
1124 function _writeUrlExternal($row1, $col1, $row2, $col2, $url)
1126 // Network drives are different. We will handle them separately
1127 // MS/Novell network drives and shares start with \\
1128 if (preg_match('[^external:\\\\]', $url)) {
1129 return; //($this->_writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format));
1132 $record = 0x01B8; // Record identifier
1133 $length = 0x00000; // Bytes to follow
1135 // Strip URL type and change Unix dir separator to Dos style (if needed)
1137 $url = preg_replace('/^external:/', '', $url);
1138 $url = preg_replace('/\//', "\\", $url);
1140 // Determine if the link is relative or absolute:
1141 // relative if link contains no dir separator, "somefile.xls"
1142 // relative if link starts with up-dir, "..\..\somefile.xls"
1143 // otherwise, absolute
1145 $absolute = 0x00; // relative path
1146 if ( preg_match('/^[A-Z]:/', $url) ) {
1147 $absolute = 0x02; // absolute path on Windows, e.g. C:\...
1149 $link_type = 0x01 | $absolute;
1151 // Determine if the link contains a sheet reference and change some of the
1152 // parameters accordingly.
1153 // Split the dir name and sheet name (if it exists)
1154 $dir_long = $url;
1155 if (preg_match("/\#/", $url)) {
1156 $link_type |= 0x08;
1160 // Pack the link type
1161 $link_type = pack("V", $link_type);
1163 // Calculate the up-level dir count e.g.. (..\..\..\ == 3)
1164 $up_count = preg_match_all("/\.\.\\\/", $dir_long, $useless);
1165 $up_count = pack("v", $up_count);
1167 // Store the short dos dir name (null terminated)
1168 $dir_short = preg_replace("/\.\.\\\/", '', $dir_long) . "\0";
1170 // Store the long dir name as a wchar string (non-null terminated)
1171 $dir_long = $dir_long . "\0";
1173 // Pack the lengths of the dir strings
1174 $dir_short_len = pack("V", strlen($dir_short) );
1175 $dir_long_len = pack("V", strlen($dir_long) );
1176 $stream_len = pack("V", 0);//strlen($dir_long) + 0x06);
1178 // Pack the undocumented parts of the hyperlink stream
1179 $unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000' );
1180 $unknown2 = pack("H*",'0303000000000000C000000000000046' );
1181 $unknown3 = pack("H*",'FFFFADDE000000000000000000000000000000000000000');
1182 $unknown4 = pack("v", 0x03 );
1184 // Pack the main data stream
1185 $data = pack("vvvv", $row1, $row2, $col1, $col2) .
1186 $unknown1 .
1187 $link_type .
1188 $unknown2 .
1189 $up_count .
1190 $dir_short_len.
1191 $dir_short .
1192 $unknown3 .
1193 $stream_len ;/*.
1194 $dir_long_len .
1195 $unknown4 .
1196 $dir_long .
1197 $sheet_len .
1198 $sheet ;*/
1200 // Pack the header data
1201 $length = strlen($data);
1202 $header = pack("vv", $record, $length);
1204 // Write the packed data
1205 $this->_append($header. $data);
1206 return 0;
1210 * This method is used to set the height and format for a row.
1212 * @param integer $row The row to set
1213 * @param integer $height Height we are giving to the row.
1214 * Use null to set XF without setting height
1215 * @param integer $xfIndex The optional cell style Xf index to apply to the columns
1216 * @param bool $hidden The optional hidden attribute
1217 * @param integer $level The optional outline level for row, in range [0,7]
1219 private function _writeRow($row, $height, $xfIndex, $hidden = false, $level = 0)
1221 $record = 0x0208; // Record identifier
1222 $length = 0x0010; // Number of bytes to follow
1224 $colMic = 0x0000; // First defined column
1225 $colMac = 0x0000; // Last defined column
1226 $irwMac = 0x0000; // Used by Excel to optimise loading
1227 $reserved = 0x0000; // Reserved
1228 $grbit = 0x0000; // Option flags
1229 $ixfe = $xfIndex;
1231 if ( $height < 0 ){
1232 $height = null;
1235 // Use _writeRow($row, null, $XF) to set XF format without setting height
1236 if ($height != null) {
1237 $miyRw = $height * 20; // row height
1238 } else {
1239 $miyRw = 0xff; // default row height is 256
1242 // Set the options flags. fUnsynced is used to show that the font and row
1243 // heights are not compatible. This is usually the case for WriteExcel.
1244 // The collapsed flag 0x10 doesn't seem to be used to indicate that a row
1245 // is collapsed. Instead it is used to indicate that the previous row is
1246 // collapsed. The zero height flag, 0x20, is used to collapse a row.
1248 $grbit |= $level;
1249 if ($hidden) {
1250 $grbit |= 0x0030;
1252 if ($height !== null) {
1253 $grbit |= 0x0040; // fUnsynced
1255 if ($xfIndex !== 0xF) {
1256 $grbit |= 0x0080;
1258 $grbit |= 0x0100;
1260 $header = pack("vv", $record, $length);
1261 $data = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw,
1262 $irwMac,$reserved, $grbit, $ixfe);
1263 $this->_append($header.$data);
1267 * Writes Excel DIMENSIONS to define the area in which there is data.
1269 private function _writeDimensions()
1271 $record = 0x0200; // Record identifier
1273 $length = 0x000E;
1274 $data = pack('VVvvv'
1275 , $this->_firstRowIndex
1276 , $this->_lastRowIndex + 1
1277 , $this->_firstColumnIndex
1278 , $this->_lastColumnIndex + 1
1279 , 0x0000 // reserved
1282 $header = pack("vv", $record, $length);
1283 $this->_append($header.$data);
1287 * Write BIFF record Window2.
1289 private function _writeWindow2()
1291 $record = 0x023E; // Record identifier
1292 $length = 0x0012;
1294 $grbit = 0x00B6; // Option flags
1295 $rwTop = 0x0000; // Top row visible in window
1296 $colLeft = 0x0000; // Leftmost column visible in window
1299 // The options flags that comprise $grbit
1300 $fDspFmla = 0; // 0 - bit
1301 $fDspGrid = $this->_phpSheet->getShowGridlines() ? 1 : 0; // 1
1302 $fDspRwCol = $this->_phpSheet->getShowRowColHeaders() ? 1 : 0; // 2
1303 $fFrozen = $this->_phpSheet->getFreezePane() ? 1 : 0; // 3
1304 $fDspZeros = 1; // 4
1305 $fDefaultHdr = 1; // 5
1306 $fArabic = $this->_phpSheet->getRightToLeft() ? 1 : 0; // 6
1307 $fDspGuts = $this->_outline_on; // 7
1308 $fFrozenNoSplit = 0; // 0 - bit
1309 // no support in PHPExcel for selected sheet, therefore sheet is only selected if it is the active sheet
1310 $fSelected = ($this->_phpSheet === $this->_phpSheet->getParent()->getActiveSheet()) ? 1 : 0;
1311 $fPaged = 1; // 2
1312 $fPageBreakPreview = $this->_phpSheet->getSheetView()->getView() === PHPExcel_Worksheet_SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW;
1314 $grbit = $fDspFmla;
1315 $grbit |= $fDspGrid << 1;
1316 $grbit |= $fDspRwCol << 2;
1317 $grbit |= $fFrozen << 3;
1318 $grbit |= $fDspZeros << 4;
1319 $grbit |= $fDefaultHdr << 5;
1320 $grbit |= $fArabic << 6;
1321 $grbit |= $fDspGuts << 7;
1322 $grbit |= $fFrozenNoSplit << 8;
1323 $grbit |= $fSelected << 9;
1324 $grbit |= $fPaged << 10;
1325 $grbit |= $fPageBreakPreview << 11;
1327 $header = pack("vv", $record, $length);
1328 $data = pack("vvv", $grbit, $rwTop, $colLeft);
1330 // FIXME !!!
1331 $rgbHdr = 0x0040; // Row/column heading and gridline color index
1332 $zoom_factor_page_break = ($fPageBreakPreview? $this->_phpSheet->getSheetView()->getZoomScale() : 0x0000);
1333 $zoom_factor_normal = $this->_phpSheet->getSheetView()->getZoomScaleNormal();
1335 $data .= pack("vvvvV", $rgbHdr, 0x0000, $zoom_factor_page_break, $zoom_factor_normal, 0x00000000);
1337 $this->_append($header.$data);
1341 * Write BIFF record DEFAULTROWHEIGHT.
1343 private function _writeDefaultRowHeight()
1345 $defaultRowHeight = $this->_phpSheet->getDefaultRowDimension()->getRowHeight();
1347 if ($defaultRowHeight < 0) {
1348 return;
1351 // convert to twips
1352 $defaultRowHeight = (int) 20 * $defaultRowHeight;
1354 $record = 0x0225; // Record identifier
1355 $length = 0x0004; // Number of bytes to follow
1357 $header = pack("vv", $record, $length);
1358 $data = pack("vv", 1, $defaultRowHeight);
1359 $this->_append($header . $data);
1363 * Write BIFF record DEFCOLWIDTH if COLINFO records are in use.
1365 private function _writeDefcol()
1367 $defaultColWidth = 8;
1369 $record = 0x0055; // Record identifier
1370 $length = 0x0002; // Number of bytes to follow
1372 $header = pack("vv", $record, $length);
1373 $data = pack("v", $defaultColWidth);
1374 $this->_append($header . $data);
1378 * Write BIFF record COLINFO to define column widths
1380 * Note: The SDK says the record length is 0x0B but Excel writes a 0x0C
1381 * length record.
1383 * @param array $col_array This is the only parameter received and is composed of the following:
1384 * 0 => First formatted column,
1385 * 1 => Last formatted column,
1386 * 2 => Col width (8.43 is Excel default),
1387 * 3 => The optional XF format of the column,
1388 * 4 => Option flags.
1389 * 5 => Optional outline level
1391 private function _writeColinfo($col_array)
1393 if (isset($col_array[0])) {
1394 $colFirst = $col_array[0];
1396 if (isset($col_array[1])) {
1397 $colLast = $col_array[1];
1399 if (isset($col_array[2])) {
1400 $coldx = $col_array[2];
1401 } else {
1402 $coldx = 8.43;
1404 if (isset($col_array[3])) {
1405 $xfIndex = $col_array[3];
1406 } else {
1407 $xfIndex = 15;
1409 if (isset($col_array[4])) {
1410 $grbit = $col_array[4];
1411 } else {
1412 $grbit = 0;
1414 if (isset($col_array[5])) {
1415 $level = $col_array[5];
1416 } else {
1417 $level = 0;
1419 $record = 0x007D; // Record identifier
1420 $length = 0x000C; // Number of bytes to follow
1422 $coldx *= 256; // Convert to units of 1/256 of a char
1424 $ixfe = $xfIndex;
1425 $reserved = 0x0000; // Reserved
1427 $level = max(0, min($level, 7));
1428 $grbit |= $level << 8;
1430 $header = pack("vv", $record, $length);
1431 $data = pack("vvvvvv", $colFirst, $colLast, $coldx,
1432 $ixfe, $grbit, $reserved);
1433 $this->_append($header.$data);
1437 * Write BIFF record SELECTION.
1439 private function _writeSelection()
1441 // look up the selected cell range
1442 $selectedCells = $this->_phpSheet->getSelectedCells();
1443 $selectedCells = PHPExcel_Cell::splitRange($this->_phpSheet->getSelectedCells());
1444 $selectedCells = $selectedCells[0];
1445 if (count($selectedCells) == 2) {
1446 list($first, $last) = $selectedCells;
1447 } else {
1448 $first = $selectedCells[0];
1449 $last = $selectedCells[0];
1452 list($colFirst, $rwFirst) = PHPExcel_Cell::coordinateFromString($first);
1453 $colFirst = PHPExcel_Cell::columnIndexFromString($colFirst) - 1; // base 0 column index
1454 --$rwFirst; // base 0 row index
1456 list($colLast, $rwLast) = PHPExcel_Cell::coordinateFromString($last);
1457 $colLast = PHPExcel_Cell::columnIndexFromString($colLast) - 1; // base 0 column index
1458 --$rwLast; // base 0 row index
1460 // make sure we are not out of bounds
1461 $colFirst = min($colFirst, 255);
1462 $colLast = min($colLast, 255);
1464 $rwFirst = min($rwFirst, 65535);
1465 $rwLast = min($rwLast, 65535);
1467 $record = 0x001D; // Record identifier
1468 $length = 0x000F; // Number of bytes to follow
1470 $pnn = $this->_active_pane; // Pane position
1471 $rwAct = $rwFirst; // Active row
1472 $colAct = $colFirst; // Active column
1473 $irefAct = 0; // Active cell ref
1474 $cref = 1; // Number of refs
1476 if (!isset($rwLast)) {
1477 $rwLast = $rwFirst; // Last row in reference
1479 if (!isset($colLast)) {
1480 $colLast = $colFirst; // Last col in reference
1483 // Swap last row/col for first row/col as necessary
1484 if ($rwFirst > $rwLast) {
1485 list($rwFirst, $rwLast) = array($rwLast, $rwFirst);
1488 if ($colFirst > $colLast) {
1489 list($colFirst, $colLast) = array($colLast, $colFirst);
1492 $header = pack("vv", $record, $length);
1493 $data = pack("CvvvvvvCC", $pnn, $rwAct, $colAct,
1494 $irefAct, $cref,
1495 $rwFirst, $rwLast,
1496 $colFirst, $colLast);
1497 $this->_append($header . $data);
1501 * Store the MERGEDCELLS records for all ranges of merged cells
1503 private function _writeMergedCells()
1505 $mergeCells = $this->_phpSheet->getMergeCells();
1506 $countMergeCells = count($mergeCells);
1508 if ($countMergeCells == 0) {
1509 return;
1512 // maximum allowed number of merged cells per record
1513 $maxCountMergeCellsPerRecord = 1027;
1515 // record identifier
1516 $record = 0x00E5;
1518 // counter for total number of merged cells treated so far by the writer
1519 $i = 0;
1521 // counter for number of merged cells written in record currently being written
1522 $j = 0;
1524 // initialize record data
1525 $recordData = '';
1527 // loop through the merged cells
1528 foreach ($mergeCells as $mergeCell) {
1529 ++$i;
1530 ++$j;
1532 // extract the row and column indexes
1533 $range = PHPExcel_Cell::splitRange($mergeCell);
1534 list($first, $last) = $range[0];
1535 list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($first);
1536 list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($last);
1538 $recordData .= pack('vvvv', $firstRow - 1, $lastRow - 1, PHPExcel_Cell::columnIndexFromString($firstColumn) - 1, PHPExcel_Cell::columnIndexFromString($lastColumn) - 1);
1540 // flush record if we have reached limit for number of merged cells, or reached final merged cell
1541 if ($j == $maxCountMergeCellsPerRecord or $i == $countMergeCells) {
1542 $recordData = pack('v', $j) . $recordData;
1543 $length = strlen($recordData);
1544 $header = pack('vv', $record, $length);
1545 $this->_append($header . $recordData);
1547 // initialize for next record, if any
1548 $recordData = '';
1549 $j = 0;
1555 * Write SHEETLAYOUT record
1557 private function _writeSheetLayout()
1559 if (!$this->_phpSheet->isTabColorSet()) {
1560 return;
1563 $recordData = pack(
1564 'vvVVVvv'
1565 , 0x0862
1566 , 0x0000 // unused
1567 , 0x00000000 // unused
1568 , 0x00000000 // unused
1569 , 0x00000014 // size of record data
1570 , $this->_colors[$this->_phpSheet->getTabColor()->getRGB()] // color index
1571 , 0x0000 // unused
1574 $length = strlen($recordData);
1576 $record = 0x0862; // Record identifier
1577 $header = pack('vv', $record, $length);
1578 $this->_append($header . $recordData);
1582 * Write SHEETPROTECTION
1584 private function _writeSheetProtection()
1586 // record identifier
1587 $record = 0x0867;
1589 // prepare options
1590 $options = (int) !$this->_phpSheet->getProtection()->getObjects()
1591 | (int) !$this->_phpSheet->getProtection()->getScenarios() << 1
1592 | (int) !$this->_phpSheet->getProtection()->getFormatCells() << 2
1593 | (int) !$this->_phpSheet->getProtection()->getFormatColumns() << 3
1594 | (int) !$this->_phpSheet->getProtection()->getFormatRows() << 4
1595 | (int) !$this->_phpSheet->getProtection()->getInsertColumns() << 5
1596 | (int) !$this->_phpSheet->getProtection()->getInsertRows() << 6
1597 | (int) !$this->_phpSheet->getProtection()->getInsertHyperlinks() << 7
1598 | (int) !$this->_phpSheet->getProtection()->getDeleteColumns() << 8
1599 | (int) !$this->_phpSheet->getProtection()->getDeleteRows() << 9
1600 | (int) !$this->_phpSheet->getProtection()->getSelectLockedCells() << 10
1601 | (int) !$this->_phpSheet->getProtection()->getSort() << 11
1602 | (int) !$this->_phpSheet->getProtection()->getAutoFilter() << 12
1603 | (int) !$this->_phpSheet->getProtection()->getPivotTables() << 13
1604 | (int) !$this->_phpSheet->getProtection()->getSelectUnlockedCells() << 14 ;
1606 // record data
1607 $recordData = pack(
1608 'vVVCVVvv'
1609 , 0x0867 // repeated record identifier
1610 , 0x0000 // not used
1611 , 0x0000 // not used
1612 , 0x00 // not used
1613 , 0x01000200 // unknown data
1614 , 0xFFFFFFFF // unknown data
1615 , $options // options
1616 , 0x0000 // not used
1619 $length = strlen($recordData);
1620 $header = pack('vv', $record, $length);
1622 $this->_append($header . $recordData);
1626 * Write BIFF record RANGEPROTECTION
1628 * Openoffice.org's Documentaion of the Microsoft Excel File Format uses term RANGEPROTECTION for these records
1629 * Microsoft Office Excel 97-2007 Binary File Format Specification uses term FEAT for these records
1631 private function _writeRangeProtection()
1633 foreach ($this->_phpSheet->getProtectedCells() as $range => $password) {
1634 // number of ranges, e.g. 'A1:B3 C20:D25'
1635 $cellRanges = explode(' ', $range);
1636 $cref = count($cellRanges);
1638 $recordData = pack(
1639 'vvVVvCVvVv',
1640 0x0868,
1641 0x00,
1642 0x0000,
1643 0x0000,
1644 0x02,
1645 0x0,
1646 0x0000,
1647 $cref,
1648 0x0000,
1649 0x00
1652 foreach ($cellRanges as $cellRange) {
1653 $recordData .= $this->_writeBIFF8CellRangeAddressFixed($cellRange);
1656 // the rgbFeat structure
1657 $recordData .= pack(
1658 'VV',
1659 0x0000,
1660 hexdec($password)
1663 $recordData .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong('p' . md5($recordData));
1665 $length = strlen($recordData);
1667 $record = 0x0868; // Record identifier
1668 $header = pack("vv", $record, $length);
1669 $this->_append($header . $recordData);
1674 * Write BIFF record EXTERNCOUNT to indicate the number of external sheet
1675 * references in a worksheet.
1677 * Excel only stores references to external sheets that are used in formulas.
1678 * For simplicity we store references to all the sheets in the workbook
1679 * regardless of whether they are used or not. This reduces the overall
1680 * complexity and eliminates the need for a two way dialogue between the formula
1681 * parser the worksheet objects.
1683 * @param integer $count The number of external sheet references in this worksheet
1685 private function _writeExterncount($count)
1687 $record = 0x0016; // Record identifier
1688 $length = 0x0002; // Number of bytes to follow
1690 $header = pack("vv", $record, $length);
1691 $data = pack("v", $count);
1692 $this->_append($header . $data);
1696 * Writes the Excel BIFF EXTERNSHEET record. These references are used by
1697 * formulas. A formula references a sheet name via an index. Since we store a
1698 * reference to all of the external worksheets the EXTERNSHEET index is the same
1699 * as the worksheet index.
1701 * @param string $sheetname The name of a external worksheet
1703 private function _writeExternsheet($sheetname)
1705 $record = 0x0017; // Record identifier
1707 // References to the current sheet are encoded differently to references to
1708 // external sheets.
1710 if ($this->_phpSheet->getTitle() == $sheetname) {
1711 $sheetname = '';
1712 $length = 0x02; // The following 2 bytes
1713 $cch = 1; // The following byte
1714 $rgch = 0x02; // Self reference
1715 } else {
1716 $length = 0x02 + strlen($sheetname);
1717 $cch = strlen($sheetname);
1718 $rgch = 0x03; // Reference to a sheet in the current workbook
1721 $header = pack("vv", $record, $length);
1722 $data = pack("CC", $cch, $rgch);
1723 $this->_append($header . $data . $sheetname);
1727 * Writes the Excel BIFF PANE record.
1728 * The panes can either be frozen or thawed (unfrozen).
1729 * Frozen panes are specified in terms of an integer number of rows and columns.
1730 * Thawed panes are specified in terms of Excel's units for rows and columns.
1732 private function _writePanes()
1734 $panes = array();
1735 if ($freezePane = $this->_phpSheet->getFreezePane()) {
1736 list($column, $row) = PHPExcel_Cell::coordinateFromString($freezePane);
1737 $panes[0] = $row - 1;
1738 $panes[1] = PHPExcel_Cell::columnIndexFromString($column) - 1;
1739 } else {
1740 // thaw panes
1741 return;
1744 $y = isset($panes[0]) ? $panes[0] : null;
1745 $x = isset($panes[1]) ? $panes[1] : null;
1746 $rwTop = isset($panes[2]) ? $panes[2] : null;
1747 $colLeft = isset($panes[3]) ? $panes[3] : null;
1748 if (count($panes) > 4) { // if Active pane was received
1749 $pnnAct = $panes[4];
1750 } else {
1751 $pnnAct = null;
1753 $record = 0x0041; // Record identifier
1754 $length = 0x000A; // Number of bytes to follow
1756 // Code specific to frozen or thawed panes.
1757 if ($this->_phpSheet->getFreezePane()) {
1758 // Set default values for $rwTop and $colLeft
1759 if (!isset($rwTop)) {
1760 $rwTop = $y;
1762 if (!isset($colLeft)) {
1763 $colLeft = $x;
1765 } else {
1766 // Set default values for $rwTop and $colLeft
1767 if (!isset($rwTop)) {
1768 $rwTop = 0;
1770 if (!isset($colLeft)) {
1771 $colLeft = 0;
1774 // Convert Excel's row and column units to the internal units.
1775 // The default row height is 12.75
1776 // The default column width is 8.43
1777 // The following slope and intersection values were interpolated.
1779 $y = 20*$y + 255;
1780 $x = 113.879*$x + 390;
1784 // Determine which pane should be active. There is also the undocumented
1785 // option to override this should it be necessary: may be removed later.
1787 if (!isset($pnnAct)) {
1788 if ($x != 0 && $y != 0) {
1789 $pnnAct = 0; // Bottom right
1791 if ($x != 0 && $y == 0) {
1792 $pnnAct = 1; // Top right
1794 if ($x == 0 && $y != 0) {
1795 $pnnAct = 2; // Bottom left
1797 if ($x == 0 && $y == 0) {
1798 $pnnAct = 3; // Top left
1802 $this->_active_pane = $pnnAct; // Used in _writeSelection
1804 $header = pack("vv", $record, $length);
1805 $data = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct);
1806 $this->_append($header . $data);
1810 * Store the page setup SETUP BIFF record.
1812 private function _writeSetup()
1814 $record = 0x00A1; // Record identifier
1815 $length = 0x0022; // Number of bytes to follow
1817 $iPaperSize = $this->_phpSheet->getPageSetup()->getPaperSize(); // Paper size
1819 $iScale = $this->_phpSheet->getPageSetup()->getScale() ?
1820 $this->_phpSheet->getPageSetup()->getScale() : 100; // Print scaling factor
1822 $iPageStart = 0x01; // Starting page number
1823 $iFitWidth = (int) $this->_phpSheet->getPageSetup()->getFitToWidth(); // Fit to number of pages wide
1824 $iFitHeight = (int) $this->_phpSheet->getPageSetup()->getFitToHeight(); // Fit to number of pages high
1825 $grbit = 0x00; // Option flags
1826 $iRes = 0x0258; // Print resolution
1827 $iVRes = 0x0258; // Vertical print resolution
1829 $numHdr = $this->_phpSheet->getPageMargins()->getHeader(); // Header Margin
1831 $numFtr = $this->_phpSheet->getPageMargins()->getFooter(); // Footer Margin
1832 $iCopies = 0x01; // Number of copies
1834 $fLeftToRight = 0x0; // Print over then down
1836 // Page orientation
1837 $fLandscape = ($this->_phpSheet->getPageSetup()->getOrientation() == PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE) ?
1838 0x0 : 0x1;
1840 $fNoPls = 0x0; // Setup not read from printer
1841 $fNoColor = 0x0; // Print black and white
1842 $fDraft = 0x0; // Print draft quality
1843 $fNotes = 0x0; // Print notes
1844 $fNoOrient = 0x0; // Orientation not set
1845 $fUsePage = 0x0; // Use custom starting page
1847 $grbit = $fLeftToRight;
1848 $grbit |= $fLandscape << 1;
1849 $grbit |= $fNoPls << 2;
1850 $grbit |= $fNoColor << 3;
1851 $grbit |= $fDraft << 4;
1852 $grbit |= $fNotes << 5;
1853 $grbit |= $fNoOrient << 6;
1854 $grbit |= $fUsePage << 7;
1856 $numHdr = pack("d", $numHdr);
1857 $numFtr = pack("d", $numFtr);
1858 if (self::getByteOrder()) { // if it's Big Endian
1859 $numHdr = strrev($numHdr);
1860 $numFtr = strrev($numFtr);
1863 $header = pack("vv", $record, $length);
1864 $data1 = pack("vvvvvvvv", $iPaperSize,
1865 $iScale,
1866 $iPageStart,
1867 $iFitWidth,
1868 $iFitHeight,
1869 $grbit,
1870 $iRes,
1871 $iVRes);
1872 $data2 = $numHdr.$numFtr;
1873 $data3 = pack("v", $iCopies);
1874 $this->_append($header . $data1 . $data2 . $data3);
1878 * Store the header caption BIFF record.
1880 private function _writeHeader()
1882 $record = 0x0014; // Record identifier
1884 /* removing for now
1885 // need to fix character count (multibyte!)
1886 if (strlen($this->_phpSheet->getHeaderFooter()->getOddHeader()) <= 255) {
1887 $str = $this->_phpSheet->getHeaderFooter()->getOddHeader(); // header string
1888 } else {
1889 $str = '';
1893 $recordData = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($this->_phpSheet->getHeaderFooter()->getOddHeader());
1894 $length = strlen($recordData);
1896 $header = pack("vv", $record, $length);
1898 $this->_append($header . $recordData);
1902 * Store the footer caption BIFF record.
1904 private function _writeFooter()
1906 $record = 0x0015; // Record identifier
1908 /* removing for now
1909 // need to fix character count (multibyte!)
1910 if (strlen($this->_phpSheet->getHeaderFooter()->getOddFooter()) <= 255) {
1911 $str = $this->_phpSheet->getHeaderFooter()->getOddFooter();
1912 } else {
1913 $str = '';
1917 $recordData = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($this->_phpSheet->getHeaderFooter()->getOddFooter());
1918 $length = strlen($recordData);
1920 $header = pack("vv", $record, $length);
1922 $this->_append($header . $recordData);
1926 * Store the horizontal centering HCENTER BIFF record.
1928 * @access private
1930 private function _writeHcenter()
1932 $record = 0x0083; // Record identifier
1933 $length = 0x0002; // Bytes to follow
1935 $fHCenter = $this->_phpSheet->getPageSetup()->getHorizontalCentered() ? 1 : 0; // Horizontal centering
1937 $header = pack("vv", $record, $length);
1938 $data = pack("v", $fHCenter);
1940 $this->_append($header.$data);
1944 * Store the vertical centering VCENTER BIFF record.
1946 private function _writeVcenter()
1948 $record = 0x0084; // Record identifier
1949 $length = 0x0002; // Bytes to follow
1951 $fVCenter = $this->_phpSheet->getPageSetup()->getVerticalCentered() ? 1 : 0; // Horizontal centering
1953 $header = pack("vv", $record, $length);
1954 $data = pack("v", $fVCenter);
1955 $this->_append($header . $data);
1959 * Store the LEFTMARGIN BIFF record.
1961 private function _writeMarginLeft()
1963 $record = 0x0026; // Record identifier
1964 $length = 0x0008; // Bytes to follow
1966 $margin = $this->_phpSheet->getPageMargins()->getLeft(); // Margin in inches
1968 $header = pack("vv", $record, $length);
1969 $data = pack("d", $margin);
1970 if (self::getByteOrder()) { // if it's Big Endian
1971 $data = strrev($data);
1974 $this->_append($header . $data);
1978 * Store the RIGHTMARGIN BIFF record.
1980 private function _writeMarginRight()
1982 $record = 0x0027; // Record identifier
1983 $length = 0x0008; // Bytes to follow
1985 $margin = $this->_phpSheet->getPageMargins()->getRight(); // Margin in inches
1987 $header = pack("vv", $record, $length);
1988 $data = pack("d", $margin);
1989 if (self::getByteOrder()) { // if it's Big Endian
1990 $data = strrev($data);
1993 $this->_append($header . $data);
1997 * Store the TOPMARGIN BIFF record.
1999 private function _writeMarginTop()
2001 $record = 0x0028; // Record identifier
2002 $length = 0x0008; // Bytes to follow
2004 $margin = $this->_phpSheet->getPageMargins()->getTop(); // Margin in inches
2006 $header = pack("vv", $record, $length);
2007 $data = pack("d", $margin);
2008 if (self::getByteOrder()) { // if it's Big Endian
2009 $data = strrev($data);
2012 $this->_append($header . $data);
2016 * Store the BOTTOMMARGIN BIFF record.
2018 private function _writeMarginBottom()
2020 $record = 0x0029; // Record identifier
2021 $length = 0x0008; // Bytes to follow
2023 $margin = $this->_phpSheet->getPageMargins()->getBottom(); // Margin in inches
2025 $header = pack("vv", $record, $length);
2026 $data = pack("d", $margin);
2027 if (self::getByteOrder()) { // if it's Big Endian
2028 $data = strrev($data);
2031 $this->_append($header . $data);
2035 * Write the PRINTHEADERS BIFF record.
2037 private function _writePrintHeaders()
2039 $record = 0x002a; // Record identifier
2040 $length = 0x0002; // Bytes to follow
2042 $fPrintRwCol = $this->_print_headers; // Boolean flag
2044 $header = pack("vv", $record, $length);
2045 $data = pack("v", $fPrintRwCol);
2046 $this->_append($header . $data);
2050 * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the
2051 * GRIDSET record.
2053 private function _writePrintGridlines()
2055 $record = 0x002b; // Record identifier
2056 $length = 0x0002; // Bytes to follow
2058 $fPrintGrid = $this->_phpSheet->getPrintGridlines() ? 1 : 0; // Boolean flag
2060 $header = pack("vv", $record, $length);
2061 $data = pack("v", $fPrintGrid);
2062 $this->_append($header . $data);
2066 * Write the GRIDSET BIFF record. Must be used in conjunction with the
2067 * PRINTGRIDLINES record.
2069 private function _writeGridset()
2071 $record = 0x0082; // Record identifier
2072 $length = 0x0002; // Bytes to follow
2074 $fGridSet = !$this->_phpSheet->getPrintGridlines(); // Boolean flag
2076 $header = pack("vv", $record, $length);
2077 $data = pack("v", $fGridSet);
2078 $this->_append($header . $data);
2082 * Write the AUTOFILTERINFO BIFF record. This is used to configure the number of autofilter select used in the sheet.
2084 private function _writeAutoFilterInfo(){
2085 $record = 0x009D; // Record identifier
2086 $length = 0x0002; // Bytes to follow
2088 $rangeBounds = PHPExcel_Cell::rangeBoundaries($this->_phpSheet->getAutoFilter()->getRange());
2089 $iNumFilters = 1 + $rangeBounds[1][0] - $rangeBounds[0][0];
2091 $header = pack("vv", $record, $length);
2092 $data = pack("v", $iNumFilters);
2093 $this->_append($header . $data);
2097 * Write the GUTS BIFF record. This is used to configure the gutter margins
2098 * where Excel outline symbols are displayed. The visibility of the gutters is
2099 * controlled by a flag in WSBOOL.
2101 * @see _writeWsbool()
2103 private function _writeGuts()
2105 $record = 0x0080; // Record identifier
2106 $length = 0x0008; // Bytes to follow
2108 $dxRwGut = 0x0000; // Size of row gutter
2109 $dxColGut = 0x0000; // Size of col gutter
2111 // determine maximum row outline level
2112 $maxRowOutlineLevel = 0;
2113 foreach ($this->_phpSheet->getRowDimensions() as $rowDimension) {
2114 $maxRowOutlineLevel = max($maxRowOutlineLevel, $rowDimension->getOutlineLevel());
2117 $col_level = 0;
2119 // Calculate the maximum column outline level. The equivalent calculation
2120 // for the row outline level is carried out in _writeRow().
2121 $colcount = count($this->_colinfo);
2122 for ($i = 0; $i < $colcount; ++$i) {
2123 $col_level = max($this->_colinfo[$i][5], $col_level);
2126 // Set the limits for the outline levels (0 <= x <= 7).
2127 $col_level = max(0, min($col_level, 7));
2129 // The displayed level is one greater than the max outline levels
2130 if ($maxRowOutlineLevel) {
2131 ++$maxRowOutlineLevel;
2133 if ($col_level) {
2134 ++$col_level;
2137 $header = pack("vv", $record, $length);
2138 $data = pack("vvvv", $dxRwGut, $dxColGut, $maxRowOutlineLevel, $col_level);
2140 $this->_append($header.$data);
2144 * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction
2145 * with the SETUP record.
2147 private function _writeWsbool()
2149 $record = 0x0081; // Record identifier
2150 $length = 0x0002; // Bytes to follow
2151 $grbit = 0x0000;
2153 // The only option that is of interest is the flag for fit to page. So we
2154 // set all the options in one go.
2156 // Set the option flags
2157 $grbit |= 0x0001; // Auto page breaks visible
2158 if ($this->_outline_style) {
2159 $grbit |= 0x0020; // Auto outline styles
2161 if ($this->_phpSheet->getShowSummaryBelow()) {
2162 $grbit |= 0x0040; // Outline summary below
2164 if ($this->_phpSheet->getShowSummaryRight()) {
2165 $grbit |= 0x0080; // Outline summary right
2167 if ($this->_phpSheet->getPageSetup()->getFitToPage()) {
2168 $grbit |= 0x0100; // Page setup fit to page
2170 if ($this->_outline_on) {
2171 $grbit |= 0x0400; // Outline symbols displayed
2174 $header = pack("vv", $record, $length);
2175 $data = pack("v", $grbit);
2176 $this->_append($header . $data);
2180 * Write the HORIZONTALPAGEBREAKS and VERTICALPAGEBREAKS BIFF records.
2182 private function _writeBreaks()
2184 // initialize
2185 $vbreaks = array();
2186 $hbreaks = array();
2188 foreach ($this->_phpSheet->getBreaks() as $cell => $breakType) {
2189 // Fetch coordinates
2190 $coordinates = PHPExcel_Cell::coordinateFromString($cell);
2192 // Decide what to do by the type of break
2193 switch ($breakType) {
2194 case PHPExcel_Worksheet::BREAK_COLUMN:
2195 // Add to list of vertical breaks
2196 $vbreaks[] = PHPExcel_Cell::columnIndexFromString($coordinates[0]) - 1;
2197 break;
2199 case PHPExcel_Worksheet::BREAK_ROW:
2200 // Add to list of horizontal breaks
2201 $hbreaks[] = $coordinates[1];
2202 break;
2204 case PHPExcel_Worksheet::BREAK_NONE:
2205 default:
2206 // Nothing to do
2207 break;
2211 //horizontal page breaks
2212 if (!empty($hbreaks)) {
2214 // Sort and filter array of page breaks
2215 sort($hbreaks, SORT_NUMERIC);
2216 if ($hbreaks[0] == 0) { // don't use first break if it's 0
2217 array_shift($hbreaks);
2220 $record = 0x001b; // Record identifier
2221 $cbrk = count($hbreaks); // Number of page breaks
2222 $length = 2 + 6 * $cbrk; // Bytes to follow
2224 $header = pack("vv", $record, $length);
2225 $data = pack("v", $cbrk);
2227 // Append each page break
2228 foreach ($hbreaks as $hbreak) {
2229 $data .= pack("vvv", $hbreak, 0x0000, 0x00ff);
2232 $this->_append($header . $data);
2235 // vertical page breaks
2236 if (!empty($vbreaks)) {
2238 // 1000 vertical pagebreaks appears to be an internal Excel 5 limit.
2239 // It is slightly higher in Excel 97/200, approx. 1026
2240 $vbreaks = array_slice($vbreaks, 0, 1000);
2242 // Sort and filter array of page breaks
2243 sort($vbreaks, SORT_NUMERIC);
2244 if ($vbreaks[0] == 0) { // don't use first break if it's 0
2245 array_shift($vbreaks);
2248 $record = 0x001a; // Record identifier
2249 $cbrk = count($vbreaks); // Number of page breaks
2250 $length = 2 + 6 * $cbrk; // Bytes to follow
2252 $header = pack("vv", $record, $length);
2253 $data = pack("v", $cbrk);
2255 // Append each page break
2256 foreach ($vbreaks as $vbreak) {
2257 $data .= pack("vvv", $vbreak, 0x0000, 0xffff);
2260 $this->_append($header . $data);
2265 * Set the Biff PROTECT record to indicate that the worksheet is protected.
2267 private function _writeProtect()
2269 // Exit unless sheet protection has been specified
2270 if (!$this->_phpSheet->getProtection()->getSheet()) {
2271 return;
2274 $record = 0x0012; // Record identifier
2275 $length = 0x0002; // Bytes to follow
2277 $fLock = 1; // Worksheet is protected
2279 $header = pack("vv", $record, $length);
2280 $data = pack("v", $fLock);
2282 $this->_append($header.$data);
2286 * Write SCENPROTECT
2288 private function _writeScenProtect()
2290 // Exit if sheet protection is not active
2291 if (!$this->_phpSheet->getProtection()->getSheet()) {
2292 return;
2295 // Exit if scenarios are not protected
2296 if (!$this->_phpSheet->getProtection()->getScenarios()) {
2297 return;
2300 $record = 0x00DD; // Record identifier
2301 $length = 0x0002; // Bytes to follow
2303 $header = pack('vv', $record, $length);
2304 $data = pack('v', 1);
2306 $this->_append($header . $data);
2310 * Write OBJECTPROTECT
2312 private function _writeObjectProtect()
2314 // Exit if sheet protection is not active
2315 if (!$this->_phpSheet->getProtection()->getSheet()) {
2316 return;
2319 // Exit if objects are not protected
2320 if (!$this->_phpSheet->getProtection()->getObjects()) {
2321 return;
2324 $record = 0x0063; // Record identifier
2325 $length = 0x0002; // Bytes to follow
2327 $header = pack('vv', $record, $length);
2328 $data = pack('v', 1);
2330 $this->_append($header . $data);
2334 * Write the worksheet PASSWORD record.
2336 private function _writePassword()
2338 // Exit unless sheet protection and password have been specified
2339 if (!$this->_phpSheet->getProtection()->getSheet() || !$this->_phpSheet->getProtection()->getPassword()) {
2340 return;
2343 $record = 0x0013; // Record identifier
2344 $length = 0x0002; // Bytes to follow
2346 $wPassword = hexdec($this->_phpSheet->getProtection()->getPassword()); // Encoded password
2348 $header = pack("vv", $record, $length);
2349 $data = pack("v", $wPassword);
2351 $this->_append($header . $data);
2355 * Insert a 24bit bitmap image in a worksheet.
2357 * @access public
2358 * @param integer $row The row we are going to insert the bitmap into
2359 * @param integer $col The column we are going to insert the bitmap into
2360 * @param mixed $bitmap The bitmap filename or GD-image resource
2361 * @param integer $x The horizontal position (offset) of the image inside the cell.
2362 * @param integer $y The vertical position (offset) of the image inside the cell.
2363 * @param float $scale_x The horizontal scale
2364 * @param float $scale_y The vertical scale
2366 function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1)
2368 $bitmap_array = (is_resource($bitmap) ? $this->_processBitmapGd($bitmap) : $this->_processBitmap($bitmap));
2369 list($width, $height, $size, $data) = $bitmap_array; //$this->_processBitmap($bitmap);
2371 // Scale the frame of the image.
2372 $width *= $scale_x;
2373 $height *= $scale_y;
2375 // Calculate the vertices of the image and write the OBJ record
2376 $this->_positionImage($col, $row, $x, $y, $width, $height);
2378 // Write the IMDATA record to store the bitmap data
2379 $record = 0x007f;
2380 $length = 8 + $size;
2381 $cf = 0x09;
2382 $env = 0x01;
2383 $lcb = $size;
2385 $header = pack("vvvvV", $record, $length, $cf, $env, $lcb);
2386 $this->_append($header.$data);
2390 * Calculate the vertices that define the position of the image as required by
2391 * the OBJ record.
2393 * +------------+------------+
2394 * | A | B |
2395 * +-----+------------+------------+
2396 * | |(x1,y1) | |
2397 * | 1 |(A1)._______|______ |
2398 * | | | | |
2399 * | | | | |
2400 * +-----+----| BITMAP |-----+
2401 * | | | | |
2402 * | 2 | |______________. |
2403 * | | | (B2)|
2404 * | | | (x2,y2)|
2405 * +---- +------------+------------+
2407 * Example of a bitmap that covers some of the area from cell A1 to cell B2.
2409 * Based on the width and height of the bitmap we need to calculate 8 vars:
2410 * $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
2411 * The width and height of the cells are also variable and have to be taken into
2412 * account.
2413 * The values of $col_start and $row_start are passed in from the calling
2414 * function. The values of $col_end and $row_end are calculated by subtracting
2415 * the width and height of the bitmap from the width and height of the
2416 * underlying cells.
2417 * The vertices are expressed as a percentage of the underlying cell width as
2418 * follows (rhs values are in pixels):
2420 * x1 = X / W *1024
2421 * y1 = Y / H *256
2422 * x2 = (X-1) / W *1024
2423 * y2 = (Y-1) / H *256
2425 * Where: X is distance from the left side of the underlying cell
2426 * Y is distance from the top of the underlying cell
2427 * W is the width of the cell
2428 * H is the height of the cell
2429 * The SDK incorrectly states that the height should be expressed as a
2430 * percentage of 1024.
2432 * @access private
2433 * @param integer $col_start Col containing upper left corner of object
2434 * @param integer $row_start Row containing top left corner of object
2435 * @param integer $x1 Distance to left side of object
2436 * @param integer $y1 Distance to top of object
2437 * @param integer $width Width of image frame
2438 * @param integer $height Height of image frame
2440 function _positionImage($col_start, $row_start, $x1, $y1, $width, $height)
2442 // Initialise end cell to the same as the start cell
2443 $col_end = $col_start; // Col containing lower right corner of object
2444 $row_end = $row_start; // Row containing bottom right corner of object
2446 // Zero the specified offset if greater than the cell dimensions
2447 if ($x1 >= PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_start))) {
2448 $x1 = 0;
2450 if ($y1 >= PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_start + 1)) {
2451 $y1 = 0;
2454 $width = $width + $x1 -1;
2455 $height = $height + $y1 -1;
2457 // Subtract the underlying cell widths to find the end cell of the image
2458 while ($width >= PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end))) {
2459 $width -= PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end));
2460 ++$col_end;
2463 // Subtract the underlying cell heights to find the end cell of the image
2464 while ($height >= PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_end + 1)) {
2465 $height -= PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_end + 1);
2466 ++$row_end;
2469 // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
2470 // with zero eight or width.
2472 if (PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_start)) == 0) {
2473 return;
2475 if (PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end)) == 0) {
2476 return;
2478 if (PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_start + 1) == 0) {
2479 return;
2481 if (PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_end + 1) == 0) {
2482 return;
2485 // Convert the pixel values to the percentage value expected by Excel
2486 $x1 = $x1 / PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_start)) * 1024;
2487 $y1 = $y1 / PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_start + 1) * 256;
2488 $x2 = $width / PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end)) * 1024; // Distance to right side of object
2489 $y2 = $height / PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_end + 1) * 256; // Distance to bottom of object
2491 $this->_writeObjPicture($col_start, $x1,
2492 $row_start, $y1,
2493 $col_end, $x2,
2494 $row_end, $y2);
2498 * Store the OBJ record that precedes an IMDATA record. This could be generalise
2499 * to support other Excel objects.
2501 * @param integer $colL Column containing upper left corner of object
2502 * @param integer $dxL Distance from left side of cell
2503 * @param integer $rwT Row containing top left corner of object
2504 * @param integer $dyT Distance from top of cell
2505 * @param integer $colR Column containing lower right corner of object
2506 * @param integer $dxR Distance from right of cell
2507 * @param integer $rwB Row containing bottom right corner of object
2508 * @param integer $dyB Distance from bottom of cell
2510 private function _writeObjPicture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB)
2512 $record = 0x005d; // Record identifier
2513 $length = 0x003c; // Bytes to follow
2515 $cObj = 0x0001; // Count of objects in file (set to 1)
2516 $OT = 0x0008; // Object type. 8 = Picture
2517 $id = 0x0001; // Object ID
2518 $grbit = 0x0614; // Option flags
2520 $cbMacro = 0x0000; // Length of FMLA structure
2521 $Reserved1 = 0x0000; // Reserved
2522 $Reserved2 = 0x0000; // Reserved
2524 $icvBack = 0x09; // Background colour
2525 $icvFore = 0x09; // Foreground colour
2526 $fls = 0x00; // Fill pattern
2527 $fAuto = 0x00; // Automatic fill
2528 $icv = 0x08; // Line colour
2529 $lns = 0xff; // Line style
2530 $lnw = 0x01; // Line weight
2531 $fAutoB = 0x00; // Automatic border
2532 $frs = 0x0000; // Frame style
2533 $cf = 0x0009; // Image format, 9 = bitmap
2534 $Reserved3 = 0x0000; // Reserved
2535 $cbPictFmla = 0x0000; // Length of FMLA structure
2536 $Reserved4 = 0x0000; // Reserved
2537 $grbit2 = 0x0001; // Option flags
2538 $Reserved5 = 0x0000; // Reserved
2541 $header = pack("vv", $record, $length);
2542 $data = pack("V", $cObj);
2543 $data .= pack("v", $OT);
2544 $data .= pack("v", $id);
2545 $data .= pack("v", $grbit);
2546 $data .= pack("v", $colL);
2547 $data .= pack("v", $dxL);
2548 $data .= pack("v", $rwT);
2549 $data .= pack("v", $dyT);
2550 $data .= pack("v", $colR);
2551 $data .= pack("v", $dxR);
2552 $data .= pack("v", $rwB);
2553 $data .= pack("v", $dyB);
2554 $data .= pack("v", $cbMacro);
2555 $data .= pack("V", $Reserved1);
2556 $data .= pack("v", $Reserved2);
2557 $data .= pack("C", $icvBack);
2558 $data .= pack("C", $icvFore);
2559 $data .= pack("C", $fls);
2560 $data .= pack("C", $fAuto);
2561 $data .= pack("C", $icv);
2562 $data .= pack("C", $lns);
2563 $data .= pack("C", $lnw);
2564 $data .= pack("C", $fAutoB);
2565 $data .= pack("v", $frs);
2566 $data .= pack("V", $cf);
2567 $data .= pack("v", $Reserved3);
2568 $data .= pack("v", $cbPictFmla);
2569 $data .= pack("v", $Reserved4);
2570 $data .= pack("v", $grbit2);
2571 $data .= pack("V", $Reserved5);
2573 $this->_append($header . $data);
2577 * Convert a GD-image into the internal format.
2579 * @access private
2580 * @param resource $image The image to process
2581 * @return array Array with data and properties of the bitmap
2583 function _processBitmapGd($image) {
2584 $width = imagesx($image);
2585 $height = imagesy($image);
2587 $data = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18);
2588 for ($j=$height; $j--; ) {
2589 for ($i=0; $i < $width; ++$i) {
2590 $color = imagecolorsforindex($image, imagecolorat($image, $i, $j));
2591 foreach (array("red", "green", "blue") as $key) {
2592 $color[$key] = $color[$key] + round((255 - $color[$key]) * $color["alpha"] / 127);
2594 $data .= chr($color["blue"]) . chr($color["green"]) . chr($color["red"]);
2596 if (3*$width % 4) {
2597 $data .= str_repeat("\x00", 4 - 3*$width % 4);
2601 return array($width, $height, strlen($data), $data);
2605 * Convert a 24 bit bitmap into the modified internal format used by Windows.
2606 * This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the
2607 * MSDN library.
2609 * @access private
2610 * @param string $bitmap The bitmap to process
2611 * @return array Array with data and properties of the bitmap
2613 function _processBitmap($bitmap)
2615 // Open file.
2616 $bmp_fd = @fopen($bitmap,"rb");
2617 if (!$bmp_fd) {
2618 throw new PHPExcel_Writer_Exception("Couldn't import $bitmap");
2621 // Slurp the file into a string.
2622 $data = fread($bmp_fd, filesize($bitmap));
2624 // Check that the file is big enough to be a bitmap.
2625 if (strlen($data) <= 0x36) {
2626 throw new PHPExcel_Writer_Exception("$bitmap doesn't contain enough data.\n");
2629 // The first 2 bytes are used to identify the bitmap.
2630 $identity = unpack("A2ident", $data);
2631 if ($identity['ident'] != "BM") {
2632 throw new PHPExcel_Writer_Exception("$bitmap doesn't appear to be a valid bitmap image.\n");
2635 // Remove bitmap data: ID.
2636 $data = substr($data, 2);
2638 // Read and remove the bitmap size. This is more reliable than reading
2639 // the data size at offset 0x22.
2641 $size_array = unpack("Vsa", substr($data, 0, 4));
2642 $size = $size_array['sa'];
2643 $data = substr($data, 4);
2644 $size -= 0x36; // Subtract size of bitmap header.
2645 $size += 0x0C; // Add size of BIFF header.
2647 // Remove bitmap data: reserved, offset, header length.
2648 $data = substr($data, 12);
2650 // Read and remove the bitmap width and height. Verify the sizes.
2651 $width_and_height = unpack("V2", substr($data, 0, 8));
2652 $width = $width_and_height[1];
2653 $height = $width_and_height[2];
2654 $data = substr($data, 8);
2655 if ($width > 0xFFFF) {
2656 throw new PHPExcel_Writer_Exception("$bitmap: largest image width supported is 65k.\n");
2658 if ($height > 0xFFFF) {
2659 throw new PHPExcel_Writer_Exception("$bitmap: largest image height supported is 65k.\n");
2662 // Read and remove the bitmap planes and bpp data. Verify them.
2663 $planes_and_bitcount = unpack("v2", substr($data, 0, 4));
2664 $data = substr($data, 4);
2665 if ($planes_and_bitcount[2] != 24) { // Bitcount
2666 throw new PHPExcel_Writer_Exception("$bitmap isn't a 24bit true color bitmap.\n");
2668 if ($planes_and_bitcount[1] != 1) {
2669 throw new PHPExcel_Writer_Exception("$bitmap: only 1 plane supported in bitmap image.\n");
2672 // Read and remove the bitmap compression. Verify compression.
2673 $compression = unpack("Vcomp", substr($data, 0, 4));
2674 $data = substr($data, 4);
2676 //$compression = 0;
2677 if ($compression['comp'] != 0) {
2678 throw new PHPExcel_Writer_Exception("$bitmap: compression not supported in bitmap image.\n");
2681 // Remove bitmap data: data size, hres, vres, colours, imp. colours.
2682 $data = substr($data, 20);
2684 // Add the BITMAPCOREHEADER data
2685 $header = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18);
2686 $data = $header . $data;
2688 return (array($width, $height, $size, $data));
2692 * Store the window zoom factor. This should be a reduced fraction but for
2693 * simplicity we will store all fractions with a numerator of 100.
2695 private function _writeZoom()
2697 // If scale is 100 we don't need to write a record
2698 if ($this->_phpSheet->getSheetView()->getZoomScale() == 100) {
2699 return;
2702 $record = 0x00A0; // Record identifier
2703 $length = 0x0004; // Bytes to follow
2705 $header = pack("vv", $record, $length);
2706 $data = pack("vv", $this->_phpSheet->getSheetView()->getZoomScale(), 100);
2707 $this->_append($header . $data);
2711 * Get Escher object
2713 * @return PHPExcel_Shared_Escher
2715 public function getEscher()
2717 return $this->_escher;
2721 * Set Escher object
2723 * @param PHPExcel_Shared_Escher $pValue
2725 public function setEscher(PHPExcel_Shared_Escher $pValue = null)
2727 $this->_escher = $pValue;
2731 * Write MSODRAWING record
2733 private function _writeMsoDrawing()
2735 // write the Escher stream if necessary
2736 if (isset($this->_escher)) {
2737 $writer = new PHPExcel_Writer_Excel5_Escher($this->_escher);
2738 $data = $writer->close();
2739 $spOffsets = $writer->getSpOffsets();
2740 $spTypes = $writer->getSpTypes();
2741 // write the neccesary MSODRAWING, OBJ records
2743 // split the Escher stream
2744 $spOffsets[0] = 0;
2745 $nm = count($spOffsets) - 1; // number of shapes excluding first shape
2746 for ($i = 1; $i <= $nm; ++$i) {
2747 // MSODRAWING record
2748 $record = 0x00EC; // Record identifier
2750 // chunk of Escher stream for one shape
2751 $dataChunk = substr($data, $spOffsets[$i -1], $spOffsets[$i] - $spOffsets[$i - 1]);
2753 $length = strlen($dataChunk);
2754 $header = pack("vv", $record, $length);
2756 $this->_append($header . $dataChunk);
2758 // OBJ record
2759 $record = 0x005D; // record identifier
2760 $objData = '';
2762 // ftCmo
2763 if($spTypes[$i] == 0x00C9){
2764 // Add ftCmo (common object data) subobject
2765 $objData .=
2766 pack('vvvvvVVV'
2767 , 0x0015 // 0x0015 = ftCmo
2768 , 0x0012 // length of ftCmo data
2769 , 0x0014 // object type, 0x0014 = filter
2770 , $i // object id number, Excel seems to use 1-based index, local for the sheet
2771 , 0x2101 // option flags, 0x2001 is what OpenOffice.org uses
2772 , 0 // reserved
2773 , 0 // reserved
2774 , 0 // reserved
2777 // Add ftSbs Scroll bar subobject
2778 $objData .= pack('vv', 0x00C, 0x0014);
2779 $objData .= pack('H*', '0000000000000000640001000A00000010000100');
2780 // Add ftLbsData (List box data) subobject
2781 $objData .= pack('vv', 0x0013, 0x1FEE);
2782 $objData .= pack('H*', '00000000010001030000020008005700');
2784 else {
2785 // Add ftCmo (common object data) subobject
2786 $objData .=
2787 pack('vvvvvVVV'
2788 , 0x0015 // 0x0015 = ftCmo
2789 , 0x0012 // length of ftCmo data
2790 , 0x0008 // object type, 0x0008 = picture
2791 , $i // object id number, Excel seems to use 1-based index, local for the sheet
2792 , 0x6011 // option flags, 0x6011 is what OpenOffice.org uses
2793 , 0 // reserved
2794 , 0 // reserved
2795 , 0 // reserved
2799 // ftEnd
2800 $objData .=
2801 pack('vv'
2802 , 0x0000 // 0x0000 = ftEnd
2803 , 0x0000 // length of ftEnd data
2806 $length = strlen($objData);
2807 $header = pack('vv', $record, $length);
2808 $this->_append($header . $objData);
2814 * Store the DATAVALIDATIONS and DATAVALIDATION records.
2816 private function _writeDataValidity()
2818 // Datavalidation collection
2819 $dataValidationCollection = $this->_phpSheet->getDataValidationCollection();
2821 // Write data validations?
2822 if (!empty($dataValidationCollection)) {
2824 // DATAVALIDATIONS record
2825 $record = 0x01B2; // Record identifier
2826 $length = 0x0012; // Bytes to follow
2828 $grbit = 0x0000; // Prompt box at cell, no cached validity data at DV records
2829 $horPos = 0x00000000; // Horizontal position of prompt box, if fixed position
2830 $verPos = 0x00000000; // Vertical position of prompt box, if fixed position
2831 $objId = 0xFFFFFFFF; // Object identifier of drop down arrow object, or -1 if not visible
2833 $header = pack('vv', $record, $length);
2834 $data = pack('vVVVV', $grbit, $horPos, $verPos, $objId,
2835 count($dataValidationCollection));
2836 $this->_append($header.$data);
2838 // DATAVALIDATION records
2839 $record = 0x01BE; // Record identifier
2841 foreach ($dataValidationCollection as $cellCoordinate => $dataValidation) {
2842 // initialize record data
2843 $data = '';
2845 // options
2846 $options = 0x00000000;
2848 // data type
2849 $type = $dataValidation->getType();
2850 switch ($type) {
2851 case PHPExcel_Cell_DataValidation::TYPE_NONE: $type = 0x00; break;
2852 case PHPExcel_Cell_DataValidation::TYPE_WHOLE: $type = 0x01; break;
2853 case PHPExcel_Cell_DataValidation::TYPE_DECIMAL: $type = 0x02; break;
2854 case PHPExcel_Cell_DataValidation::TYPE_LIST: $type = 0x03; break;
2855 case PHPExcel_Cell_DataValidation::TYPE_DATE: $type = 0x04; break;
2856 case PHPExcel_Cell_DataValidation::TYPE_TIME: $type = 0x05; break;
2857 case PHPExcel_Cell_DataValidation::TYPE_TEXTLENGTH: $type = 0x06; break;
2858 case PHPExcel_Cell_DataValidation::TYPE_CUSTOM: $type = 0x07; break;
2860 $options |= $type << 0;
2862 // error style
2863 $errorStyle = $dataValidation->getType();
2864 switch ($errorStyle) {
2865 case PHPExcel_Cell_DataValidation::STYLE_STOP: $errorStyle = 0x00; break;
2866 case PHPExcel_Cell_DataValidation::STYLE_WARNING: $errorStyle = 0x01; break;
2867 case PHPExcel_Cell_DataValidation::STYLE_INFORMATION: $errorStyle = 0x02; break;
2869 $options |= $errorStyle << 4;
2871 // explicit formula?
2872 if ($type == 0x03 && preg_match('/^\".*\"$/', $dataValidation->getFormula1())) {
2873 $options |= 0x01 << 7;
2876 // empty cells allowed
2877 $options |= $dataValidation->getAllowBlank() << 8;
2879 // show drop down
2880 $options |= (!$dataValidation->getShowDropDown()) << 9;
2882 // show input message
2883 $options |= $dataValidation->getShowInputMessage() << 18;
2885 // show error message
2886 $options |= $dataValidation->getShowErrorMessage() << 19;
2888 // condition operator
2889 $operator = $dataValidation->getOperator();
2890 switch ($operator) {
2891 case PHPExcel_Cell_DataValidation::OPERATOR_BETWEEN: $operator = 0x00 ; break;
2892 case PHPExcel_Cell_DataValidation::OPERATOR_NOTBETWEEN: $operator = 0x01 ; break;
2893 case PHPExcel_Cell_DataValidation::OPERATOR_EQUAL: $operator = 0x02 ; break;
2894 case PHPExcel_Cell_DataValidation::OPERATOR_NOTEQUAL: $operator = 0x03 ; break;
2895 case PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHAN: $operator = 0x04 ; break;
2896 case PHPExcel_Cell_DataValidation::OPERATOR_LESSTHAN: $operator = 0x05 ; break;
2897 case PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHANOREQUAL: $operator = 0x06; break;
2898 case PHPExcel_Cell_DataValidation::OPERATOR_LESSTHANOREQUAL: $operator = 0x07 ; break;
2900 $options |= $operator << 20;
2902 $data = pack('V', $options);
2904 // prompt title
2905 $promptTitle = $dataValidation->getPromptTitle() !== '' ?
2906 $dataValidation->getPromptTitle() : chr(0);
2907 $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($promptTitle);
2909 // error title
2910 $errorTitle = $dataValidation->getErrorTitle() !== '' ?
2911 $dataValidation->getErrorTitle() : chr(0);
2912 $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($errorTitle);
2914 // prompt text
2915 $prompt = $dataValidation->getPrompt() !== '' ?
2916 $dataValidation->getPrompt() : chr(0);
2917 $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($prompt);
2919 // error text
2920 $error = $dataValidation->getError() !== '' ?
2921 $dataValidation->getError() : chr(0);
2922 $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($error);
2924 // formula 1
2925 try {
2926 $formula1 = $dataValidation->getFormula1();
2927 if ($type == 0x03) { // list type
2928 $formula1 = str_replace(',', chr(0), $formula1);
2930 $this->_parser->parse($formula1);
2931 $formula1 = $this->_parser->toReversePolish();
2932 $sz1 = strlen($formula1);
2934 } catch(PHPExcel_Exception $e) {
2935 $sz1 = 0;
2936 $formula1 = '';
2938 $data .= pack('vv', $sz1, 0x0000);
2939 $data .= $formula1;
2941 // formula 2
2942 try {
2943 $formula2 = $dataValidation->getFormula2();
2944 if ($formula2 === '') {
2945 throw new PHPExcel_Writer_Exception('No formula2');
2947 $this->_parser->parse($formula2);
2948 $formula2 = $this->_parser->toReversePolish();
2949 $sz2 = strlen($formula2);
2951 } catch(PHPExcel_Exception $e) {
2952 $sz2 = 0;
2953 $formula2 = '';
2955 $data .= pack('vv', $sz2, 0x0000);
2956 $data .= $formula2;
2958 // cell range address list
2959 $data .= pack('v', 0x0001);
2960 $data .= $this->_writeBIFF8CellRangeAddressFixed($cellCoordinate);
2962 $length = strlen($data);
2963 $header = pack("vv", $record, $length);
2965 $this->_append($header . $data);
2971 * Map Error code
2973 * @param string $errorCode
2974 * @return int
2976 private static function _mapErrorCode($errorCode) {
2977 switch ($errorCode) {
2978 case '#NULL!': return 0x00;
2979 case '#DIV/0!': return 0x07;
2980 case '#VALUE!': return 0x0F;
2981 case '#REF!': return 0x17;
2982 case '#NAME?': return 0x1D;
2983 case '#NUM!': return 0x24;
2984 case '#N/A': return 0x2A;
2987 return 0;
2991 * Write PLV Record
2993 private function _writePageLayoutView(){
2994 $record = 0x088B; // Record identifier
2995 $length = 0x0010; // Bytes to follow
2997 $rt = 0x088B; // 2
2998 $grbitFrt = 0x0000; // 2
2999 $reserved = 0x0000000000000000; // 8
3000 $wScalvePLV = $this->_phpSheet->getSheetView()->getZoomScale(); // 2
3002 // The options flags that comprise $grbit
3003 if($this->_phpSheet->getSheetView()->getView() == PHPExcel_Worksheet_SheetView::SHEETVIEW_PAGE_LAYOUT){
3004 $fPageLayoutView = 1;
3005 } else {
3006 $fPageLayoutView = 0;
3008 $fRulerVisible = 0;
3009 $fWhitespaceHidden = 0;
3011 $grbit = $fPageLayoutView; // 2
3012 $grbit |= $fRulerVisible << 1;
3013 $grbit |= $fWhitespaceHidden << 3;
3015 $header = pack("vv", $record, $length);
3016 $data = pack("vvVVvv", $rt, $grbitFrt, 0x00000000, 0x00000000, $wScalvePLV, $grbit);
3017 $this->_append($header . $data);
3021 * Write CFRule Record
3022 * @param PHPExcel_Style_Conditional $conditional
3024 private function _writeCFRule(PHPExcel_Style_Conditional $conditional){
3025 $record = 0x01B1; // Record identifier
3027 // $type : Type of the CF
3028 // $operatorType : Comparison operator
3029 if($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_EXPRESSION){
3030 $type = 0x02;
3031 $operatorType = 0x00;
3032 } else if($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CELLIS){
3033 $type = 0x01;
3035 switch ($conditional->getOperatorType()){
3036 case PHPExcel_Style_Conditional::OPERATOR_NONE:
3037 $operatorType = 0x00;
3038 break;
3039 case PHPExcel_Style_Conditional::OPERATOR_EQUAL:
3040 $operatorType = 0x03;
3041 break;
3042 case PHPExcel_Style_Conditional::OPERATOR_GREATERTHAN:
3043 $operatorType = 0x05;
3044 break;
3045 case PHPExcel_Style_Conditional::OPERATOR_GREATERTHANOREQUAL:
3046 $operatorType = 0x07;
3047 break;
3048 case PHPExcel_Style_Conditional::OPERATOR_LESSTHAN:
3049 $operatorType = 0x06;
3050 break;
3051 case PHPExcel_Style_Conditional::OPERATOR_LESSTHANOREQUAL:
3052 $operatorType = 0x08;
3053 break;
3054 case PHPExcel_Style_Conditional::OPERATOR_NOTEQUAL:
3055 $operatorType = 0x04;
3056 break;
3057 case PHPExcel_Style_Conditional::OPERATOR_BETWEEN:
3058 $operatorType = 0x01;
3059 break;
3060 // not OPERATOR_NOTBETWEEN 0x02
3064 // $szValue1 : size of the formula data for first value or formula
3065 // $szValue2 : size of the formula data for second value or formula
3066 $arrConditions = $conditional->getConditions();
3067 $numConditions = sizeof($arrConditions);
3068 if($numConditions == 1){
3069 $szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
3070 $szValue2 = 0x0000;
3071 $operand1 = pack('Cv', 0x1E, $arrConditions[0]);
3072 $operand2 = null;
3073 } else if($numConditions == 2 && ($conditional->getOperatorType() == PHPExcel_Style_Conditional::OPERATOR_BETWEEN)){
3074 $szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
3075 $szValue2 = ($arrConditions[1] <= 65535 ? 3 : 0x0000);
3076 $operand1 = pack('Cv', 0x1E, $arrConditions[0]);
3077 $operand2 = pack('Cv', 0x1E, $arrConditions[1]);
3078 } else {
3079 $szValue1 = 0x0000;
3080 $szValue2 = 0x0000;
3081 $operand1 = null;
3082 $operand2 = null;
3085 // $flags : Option flags
3086 // Alignment
3087 $bAlignHz = ($conditional->getStyle()->getAlignment()->getHorizontal() == null ? 1 : 0);
3088 $bAlignVt = ($conditional->getStyle()->getAlignment()->getVertical() == null ? 1 : 0);
3089 $bAlignWrapTx = ($conditional->getStyle()->getAlignment()->getWrapText() == false ? 1 : 0);
3090 $bTxRotation = ($conditional->getStyle()->getAlignment()->getTextRotation() == null ? 1 : 0);
3091 $bIndent = ($conditional->getStyle()->getAlignment()->getIndent() == 0 ? 1 : 0);
3092 $bShrinkToFit = ($conditional->getStyle()->getAlignment()->getShrinkToFit() == false ? 1 : 0);
3093 if($bAlignHz == 0 || $bAlignVt == 0 || $bAlignWrapTx == 0 || $bTxRotation == 0 || $bIndent == 0 || $bShrinkToFit == 0){
3094 $bFormatAlign = 1;
3095 } else {
3096 $bFormatAlign = 0;
3098 // Protection
3099 $bProtLocked = ($conditional->getStyle()->getProtection()->getLocked() == null ? 1 : 0);
3100 $bProtHidden = ($conditional->getStyle()->getProtection()->getHidden() == null ? 1 : 0);
3101 if($bProtLocked == 0 || $bProtHidden == 0){
3102 $bFormatProt = 1;
3103 } else {
3104 $bFormatProt = 0;
3106 // Border
3107 $bBorderLeft = ($conditional->getStyle()->getBorders()->getLeft()->getColor()->getARGB() == PHPExcel_Style_Color::COLOR_BLACK
3108 && $conditional->getStyle()->getBorders()->getLeft()->getBorderStyle() == PHPExcel_Style_Border::BORDER_NONE ? 1 : 0);
3109 $bBorderRight = ($conditional->getStyle()->getBorders()->getRight()->getColor()->getARGB() == PHPExcel_Style_Color::COLOR_BLACK
3110 && $conditional->getStyle()->getBorders()->getRight()->getBorderStyle() == PHPExcel_Style_Border::BORDER_NONE ? 1 : 0);
3111 $bBorderTop = ($conditional->getStyle()->getBorders()->getTop()->getColor()->getARGB() == PHPExcel_Style_Color::COLOR_BLACK
3112 && $conditional->getStyle()->getBorders()->getTop()->getBorderStyle() == PHPExcel_Style_Border::BORDER_NONE ? 1 : 0);
3113 $bBorderBottom = ($conditional->getStyle()->getBorders()->getBottom()->getColor()->getARGB() == PHPExcel_Style_Color::COLOR_BLACK
3114 && $conditional->getStyle()->getBorders()->getBottom()->getBorderStyle() == PHPExcel_Style_Border::BORDER_NONE ? 1 : 0);
3115 if($bBorderLeft == 0 || $bBorderRight == 0 || $bBorderTop == 0 || $bBorderBottom == 0){
3116 $bFormatBorder = 1;
3117 } else {
3118 $bFormatBorder = 0;
3120 // Pattern
3121 $bFillStyle = ($conditional->getStyle()->getFill()->getFillType() == null ? 0 : 1);
3122 $bFillColor = ($conditional->getStyle()->getFill()->getStartColor()->getARGB() == null ? 0 : 1);
3123 $bFillColorBg = ($conditional->getStyle()->getFill()->getEndColor()->getARGB() == null ? 0 : 1);
3124 if($bFillStyle == 0 || $bFillColor == 0 || $bFillColorBg == 0){
3125 $bFormatFill = 1;
3126 } else {
3127 $bFormatFill = 0;
3129 // Font
3130 if($conditional->getStyle()->getFont()->getName() != null
3131 || $conditional->getStyle()->getFont()->getSize() != null
3132 || $conditional->getStyle()->getFont()->getBold() != null
3133 || $conditional->getStyle()->getFont()->getItalic() != null
3134 || $conditional->getStyle()->getFont()->getSuperScript() != null
3135 || $conditional->getStyle()->getFont()->getSubScript() != null
3136 || $conditional->getStyle()->getFont()->getUnderline() != null
3137 || $conditional->getStyle()->getFont()->getStrikethrough() != null
3138 || $conditional->getStyle()->getFont()->getColor()->getARGB() != null){
3139 $bFormatFont = 1;
3140 } else {
3141 $bFormatFont = 0;
3143 // Alignment
3144 $flags = 0;
3145 $flags |= (1 == $bAlignHz ? 0x00000001 : 0);
3146 $flags |= (1 == $bAlignVt ? 0x00000002 : 0);
3147 $flags |= (1 == $bAlignWrapTx ? 0x00000004 : 0);
3148 $flags |= (1 == $bTxRotation ? 0x00000008 : 0);
3149 // Justify last line flag
3150 $flags |= (1 == 1 ? 0x00000010 : 0);
3151 $flags |= (1 == $bIndent ? 0x00000020 : 0);
3152 $flags |= (1 == $bShrinkToFit ? 0x00000040 : 0);
3153 // Default
3154 $flags |= (1 == 1 ? 0x00000080 : 0);
3155 // Protection
3156 $flags |= (1 == $bProtLocked ? 0x00000100 : 0);
3157 $flags |= (1 == $bProtHidden ? 0x00000200 : 0);
3158 // Border
3159 $flags |= (1 == $bBorderLeft ? 0x00000400 : 0);
3160 $flags |= (1 == $bBorderRight ? 0x00000800 : 0);
3161 $flags |= (1 == $bBorderTop ? 0x00001000 : 0);
3162 $flags |= (1 == $bBorderBottom ? 0x00002000 : 0);
3163 $flags |= (1 == 1 ? 0x00004000 : 0); // Top left to Bottom right border
3164 $flags |= (1 == 1 ? 0x00008000 : 0); // Bottom left to Top right border
3165 // Pattern
3166 $flags |= (1 == $bFillStyle ? 0x00010000 : 0);
3167 $flags |= (1 == $bFillColor ? 0x00020000 : 0);
3168 $flags |= (1 == $bFillColorBg ? 0x00040000 : 0);
3169 $flags |= (1 == 1 ? 0x00380000 : 0);
3170 // Font
3171 $flags |= (1 == $bFormatFont ? 0x04000000 : 0);
3172 // Alignment :
3173 $flags |= (1 == $bFormatAlign ? 0x08000000 : 0);
3174 // Border
3175 $flags |= (1 == $bFormatBorder ? 0x10000000 : 0);
3176 // Pattern
3177 $flags |= (1 == $bFormatFill ? 0x20000000 : 0);
3178 // Protection
3179 $flags |= (1 == $bFormatProt ? 0x40000000 : 0);
3180 // Text direction
3181 $flags |= (1 == 0 ? 0x80000000 : 0);
3183 // Data Blocks
3184 if($bFormatFont == 1){
3185 // Font Name
3186 if($conditional->getStyle()->getFont()->getName() == null){
3187 $dataBlockFont = pack('VVVVVVVV', 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000);
3188 $dataBlockFont .= pack('VVVVVVVV', 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000);
3189 } else {
3190 $dataBlockFont = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($conditional->getStyle()->getFont()->getName());
3192 // Font Size
3193 if($conditional->getStyle()->getFont()->getSize() == null){
3194 $dataBlockFont .= pack('V', 20 * 11);
3195 } else {
3196 $dataBlockFont .= pack('V', 20 * $conditional->getStyle()->getFont()->getSize());
3198 // Font Options
3199 $dataBlockFont .= pack('V', 0);
3200 // Font weight
3201 if($conditional->getStyle()->getFont()->getBold() == true){
3202 $dataBlockFont .= pack('v', 0x02BC);
3203 } else {
3204 $dataBlockFont .= pack('v', 0x0190);
3206 // Escapement type
3207 if($conditional->getStyle()->getFont()->getSubScript() == true){
3208 $dataBlockFont .= pack('v', 0x02);
3209 $fontEscapement = 0;
3210 } else if($conditional->getStyle()->getFont()->getSuperScript() == true){
3211 $dataBlockFont .= pack('v', 0x01);
3212 $fontEscapement = 0;
3213 } else {
3214 $dataBlockFont .= pack('v', 0x00);
3215 $fontEscapement = 1;
3217 // Underline type
3218 switch ($conditional->getStyle()->getFont()->getUnderline()){
3219 case PHPExcel_Style_Font::UNDERLINE_NONE : $dataBlockFont .= pack('C', 0x00); $fontUnderline = 0; break;
3220 case PHPExcel_Style_Font::UNDERLINE_DOUBLE : $dataBlockFont .= pack('C', 0x02); $fontUnderline = 0; break;
3221 case PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING : $dataBlockFont .= pack('C', 0x22); $fontUnderline = 0; break;
3222 case PHPExcel_Style_Font::UNDERLINE_SINGLE : $dataBlockFont .= pack('C', 0x01); $fontUnderline = 0; break;
3223 case PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING : $dataBlockFont .= pack('C', 0x21); $fontUnderline = 0; break;
3224 default : $dataBlockFont .= pack('C', 0x00); $fontUnderline = 1; break;
3226 // Not used (3)
3227 $dataBlockFont .= pack('vC', 0x0000, 0x00);
3228 // Font color index
3229 switch ($conditional->getStyle()->getFont()->getColor()->getRGB()) {
3230 case '000000': $colorIdx = 0x08; break;
3231 case 'FFFFFF': $colorIdx = 0x09; break;
3232 case 'FF0000': $colorIdx = 0x0A; break;
3233 case '00FF00': $colorIdx = 0x0B; break;
3234 case '0000FF': $colorIdx = 0x0C; break;
3235 case 'FFFF00': $colorIdx = 0x0D; break;
3236 case 'FF00FF': $colorIdx = 0x0E; break;
3237 case '00FFFF': $colorIdx = 0x0F; break;
3238 case '800000': $colorIdx = 0x10; break;
3239 case '008000': $colorIdx = 0x11; break;
3240 case '000080': $colorIdx = 0x12; break;
3241 case '808000': $colorIdx = 0x13; break;
3242 case '800080': $colorIdx = 0x14; break;
3243 case '008080': $colorIdx = 0x15; break;
3244 case 'C0C0C0': $colorIdx = 0x16; break;
3245 case '808080': $colorIdx = 0x17; break;
3246 case '9999FF': $colorIdx = 0x18; break;
3247 case '993366': $colorIdx = 0x19; break;
3248 case 'FFFFCC': $colorIdx = 0x1A; break;
3249 case 'CCFFFF': $colorIdx = 0x1B; break;
3250 case '660066': $colorIdx = 0x1C; break;
3251 case 'FF8080': $colorIdx = 0x1D; break;
3252 case '0066CC': $colorIdx = 0x1E; break;
3253 case 'CCCCFF': $colorIdx = 0x1F; break;
3254 case '000080': $colorIdx = 0x20; break;
3255 case 'FF00FF': $colorIdx = 0x21; break;
3256 case 'FFFF00': $colorIdx = 0x22; break;
3257 case '00FFFF': $colorIdx = 0x23; break;
3258 case '800080': $colorIdx = 0x24; break;
3259 case '800000': $colorIdx = 0x25; break;
3260 case '008080': $colorIdx = 0x26; break;
3261 case '0000FF': $colorIdx = 0x27; break;
3262 case '00CCFF': $colorIdx = 0x28; break;
3263 case 'CCFFFF': $colorIdx = 0x29; break;
3264 case 'CCFFCC': $colorIdx = 0x2A; break;
3265 case 'FFFF99': $colorIdx = 0x2B; break;
3266 case '99CCFF': $colorIdx = 0x2C; break;
3267 case 'FF99CC': $colorIdx = 0x2D; break;
3268 case 'CC99FF': $colorIdx = 0x2E; break;
3269 case 'FFCC99': $colorIdx = 0x2F; break;
3270 case '3366FF': $colorIdx = 0x30; break;
3271 case '33CCCC': $colorIdx = 0x31; break;
3272 case '99CC00': $colorIdx = 0x32; break;
3273 case 'FFCC00': $colorIdx = 0x33; break;
3274 case 'FF9900': $colorIdx = 0x34; break;
3275 case 'FF6600': $colorIdx = 0x35; break;
3276 case '666699': $colorIdx = 0x36; break;
3277 case '969696': $colorIdx = 0x37; break;
3278 case '003366': $colorIdx = 0x38; break;
3279 case '339966': $colorIdx = 0x39; break;
3280 case '003300': $colorIdx = 0x3A; break;
3281 case '333300': $colorIdx = 0x3B; break;
3282 case '993300': $colorIdx = 0x3C; break;
3283 case '993366': $colorIdx = 0x3D; break;
3284 case '333399': $colorIdx = 0x3E; break;
3285 case '333333': $colorIdx = 0x3F; break;
3286 default: $colorIdx = 0x00; break;
3288 $dataBlockFont .= pack('V', $colorIdx);
3289 // Not used (4)
3290 $dataBlockFont .= pack('V', 0x00000000);
3291 // Options flags for modified font attributes
3292 $optionsFlags = 0;
3293 $optionsFlagsBold = ($conditional->getStyle()->getFont()->getBold() == null ? 1 : 0);
3294 $optionsFlags |= (1 == $optionsFlagsBold ? 0x00000002 : 0);
3295 $optionsFlags |= (1 == 1 ? 0x00000008 : 0);
3296 $optionsFlags |= (1 == 1 ? 0x00000010 : 0);
3297 $optionsFlags |= (1 == 0 ? 0x00000020 : 0);
3298 $optionsFlags |= (1 == 1 ? 0x00000080 : 0);
3299 $dataBlockFont .= pack('V', $optionsFlags);
3300 // Escapement type
3301 $dataBlockFont .= pack('V', $fontEscapement);
3302 // Underline type
3303 $dataBlockFont .= pack('V', $fontUnderline);
3304 // Always
3305 $dataBlockFont .= pack('V', 0x00000000);
3306 // Always
3307 $dataBlockFont .= pack('V', 0x00000000);
3308 // Not used (8)
3309 $dataBlockFont .= pack('VV', 0x00000000, 0x00000000);
3310 // Always
3311 $dataBlockFont .= pack('v', 0x0001);
3313 if($bFormatAlign == 1){
3314 $blockAlign = 0;
3315 // Alignment and text break
3316 switch ($conditional->getStyle()->getAlignment()->getHorizontal()){
3317 case PHPExcel_Style_Alignment::HORIZONTAL_GENERAL : $blockAlign = 0; break;
3318 case PHPExcel_Style_Alignment::HORIZONTAL_LEFT : $blockAlign = 1; break;
3319 case PHPExcel_Style_Alignment::HORIZONTAL_RIGHT : $blockAlign = 3; break;
3320 case PHPExcel_Style_Alignment::HORIZONTAL_CENTER : $blockAlign = 2; break;
3321 case PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS : $blockAlign = 6; break;
3322 case PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY : $blockAlign = 5; break;
3324 if($conditional->getStyle()->getAlignment()->getWrapText() == true){
3325 $blockAlign |= 1 << 3;
3326 } else {
3327 $blockAlign |= 0 << 3;
3329 switch ($conditional->getStyle()->getAlignment()->getVertical()){
3330 case PHPExcel_Style_Alignment::VERTICAL_BOTTOM : $blockAlign = 2 << 4; break;
3331 case PHPExcel_Style_Alignment::VERTICAL_TOP : $blockAlign = 0 << 4; break;
3332 case PHPExcel_Style_Alignment::VERTICAL_CENTER : $blockAlign = 1 << 4; break;
3333 case PHPExcel_Style_Alignment::VERTICAL_JUSTIFY : $blockAlign = 3 << 4; break;
3335 $blockAlign |= 0 << 7;
3337 // Text rotation angle
3338 $blockRotation = $conditional->getStyle()->getAlignment()->getTextRotation();
3340 // Indentation
3341 $blockIndent = $conditional->getStyle()->getAlignment()->getIndent();
3342 if($conditional->getStyle()->getAlignment()->getShrinkToFit() == true){
3343 $blockIndent |= 1 << 4;
3344 } else {
3345 $blockIndent |= 0 << 4;
3347 $blockIndent |= 0 << 6;
3349 // Relative indentation
3350 $blockIndentRelative = 255;
3352 $dataBlockAlign = pack('CCvvv', $blockAlign, $blockRotation, $blockIndent, $blockIndentRelative, 0x0000);
3354 if($bFormatBorder == 1){
3355 $blockLineStyle = 0;
3356 switch ($conditional->getStyle()->getBorders()->getLeft()->getBorderStyle()){
3357 case PHPExcel_Style_Border::BORDER_NONE : $blockLineStyle |= 0x00; break;
3358 case PHPExcel_Style_Border::BORDER_THIN : $blockLineStyle |= 0x01; break;
3359 case PHPExcel_Style_Border::BORDER_MEDIUM : $blockLineStyle |= 0x02; break;
3360 case PHPExcel_Style_Border::BORDER_DASHED : $blockLineStyle |= 0x03; break;
3361 case PHPExcel_Style_Border::BORDER_DOTTED : $blockLineStyle |= 0x04; break;
3362 case PHPExcel_Style_Border::BORDER_THICK : $blockLineStyle |= 0x05; break;
3363 case PHPExcel_Style_Border::BORDER_DOUBLE : $blockLineStyle |= 0x06; break;
3364 case PHPExcel_Style_Border::BORDER_HAIR : $blockLineStyle |= 0x07; break;
3365 case PHPExcel_Style_Border::BORDER_MEDIUMDASHED : $blockLineStyle |= 0x08; break;
3366 case PHPExcel_Style_Border::BORDER_DASHDOT : $blockLineStyle |= 0x09; break;
3367 case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT : $blockLineStyle |= 0x0A; break;
3368 case PHPExcel_Style_Border::BORDER_DASHDOTDOT : $blockLineStyle |= 0x0B; break;
3369 case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT : $blockLineStyle |= 0x0C; break;
3370 case PHPExcel_Style_Border::BORDER_SLANTDASHDOT : $blockLineStyle |= 0x0D; break;
3372 switch ($conditional->getStyle()->getBorders()->getRight()->getBorderStyle()){
3373 case PHPExcel_Style_Border::BORDER_NONE : $blockLineStyle |= 0x00 << 4; break;
3374 case PHPExcel_Style_Border::BORDER_THIN : $blockLineStyle |= 0x01 << 4; break;
3375 case PHPExcel_Style_Border::BORDER_MEDIUM : $blockLineStyle |= 0x02 << 4; break;
3376 case PHPExcel_Style_Border::BORDER_DASHED : $blockLineStyle |= 0x03 << 4; break;
3377 case PHPExcel_Style_Border::BORDER_DOTTED : $blockLineStyle |= 0x04 << 4; break;
3378 case PHPExcel_Style_Border::BORDER_THICK : $blockLineStyle |= 0x05 << 4; break;
3379 case PHPExcel_Style_Border::BORDER_DOUBLE : $blockLineStyle |= 0x06 << 4; break;
3380 case PHPExcel_Style_Border::BORDER_HAIR : $blockLineStyle |= 0x07 << 4; break;
3381 case PHPExcel_Style_Border::BORDER_MEDIUMDASHED : $blockLineStyle |= 0x08 << 4; break;
3382 case PHPExcel_Style_Border::BORDER_DASHDOT : $blockLineStyle |= 0x09 << 4; break;
3383 case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT : $blockLineStyle |= 0x0A << 4; break;
3384 case PHPExcel_Style_Border::BORDER_DASHDOTDOT : $blockLineStyle |= 0x0B << 4; break;
3385 case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT : $blockLineStyle |= 0x0C << 4; break;
3386 case PHPExcel_Style_Border::BORDER_SLANTDASHDOT : $blockLineStyle |= 0x0D << 4; break;
3388 switch ($conditional->getStyle()->getBorders()->getTop()->getBorderStyle()){
3389 case PHPExcel_Style_Border::BORDER_NONE : $blockLineStyle |= 0x00 << 8; break;
3390 case PHPExcel_Style_Border::BORDER_THIN : $blockLineStyle |= 0x01 << 8; break;
3391 case PHPExcel_Style_Border::BORDER_MEDIUM : $blockLineStyle |= 0x02 << 8; break;
3392 case PHPExcel_Style_Border::BORDER_DASHED : $blockLineStyle |= 0x03 << 8; break;
3393 case PHPExcel_Style_Border::BORDER_DOTTED : $blockLineStyle |= 0x04 << 8; break;
3394 case PHPExcel_Style_Border::BORDER_THICK : $blockLineStyle |= 0x05 << 8; break;
3395 case PHPExcel_Style_Border::BORDER_DOUBLE : $blockLineStyle |= 0x06 << 8; break;
3396 case PHPExcel_Style_Border::BORDER_HAIR : $blockLineStyle |= 0x07 << 8; break;
3397 case PHPExcel_Style_Border::BORDER_MEDIUMDASHED : $blockLineStyle |= 0x08 << 8; break;
3398 case PHPExcel_Style_Border::BORDER_DASHDOT : $blockLineStyle |= 0x09 << 8; break;
3399 case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT : $blockLineStyle |= 0x0A << 8; break;
3400 case PHPExcel_Style_Border::BORDER_DASHDOTDOT : $blockLineStyle |= 0x0B << 8; break;
3401 case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT : $blockLineStyle |= 0x0C << 8; break;
3402 case PHPExcel_Style_Border::BORDER_SLANTDASHDOT : $blockLineStyle |= 0x0D << 8; break;
3404 switch ($conditional->getStyle()->getBorders()->getBottom()->getBorderStyle()){
3405 case PHPExcel_Style_Border::BORDER_NONE : $blockLineStyle |= 0x00 << 12; break;
3406 case PHPExcel_Style_Border::BORDER_THIN : $blockLineStyle |= 0x01 << 12; break;
3407 case PHPExcel_Style_Border::BORDER_MEDIUM : $blockLineStyle |= 0x02 << 12; break;
3408 case PHPExcel_Style_Border::BORDER_DASHED : $blockLineStyle |= 0x03 << 12; break;
3409 case PHPExcel_Style_Border::BORDER_DOTTED : $blockLineStyle |= 0x04 << 12; break;
3410 case PHPExcel_Style_Border::BORDER_THICK : $blockLineStyle |= 0x05 << 12; break;
3411 case PHPExcel_Style_Border::BORDER_DOUBLE : $blockLineStyle |= 0x06 << 12; break;
3412 case PHPExcel_Style_Border::BORDER_HAIR : $blockLineStyle |= 0x07 << 12; break;
3413 case PHPExcel_Style_Border::BORDER_MEDIUMDASHED : $blockLineStyle |= 0x08 << 12; break;
3414 case PHPExcel_Style_Border::BORDER_DASHDOT : $blockLineStyle |= 0x09 << 12; break;
3415 case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT : $blockLineStyle |= 0x0A << 12; break;
3416 case PHPExcel_Style_Border::BORDER_DASHDOTDOT : $blockLineStyle |= 0x0B << 12; break;
3417 case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT : $blockLineStyle |= 0x0C << 12; break;
3418 case PHPExcel_Style_Border::BORDER_SLANTDASHDOT : $blockLineStyle |= 0x0D << 12; break;
3420 //@todo _writeCFRule() => $blockLineStyle => Index Color for left line
3421 //@todo _writeCFRule() => $blockLineStyle => Index Color for right line
3422 //@todo _writeCFRule() => $blockLineStyle => Top-left to bottom-right on/off
3423 //@todo _writeCFRule() => $blockLineStyle => Bottom-left to top-right on/off
3424 $blockColor = 0;
3425 //@todo _writeCFRule() => $blockColor => Index Color for top line
3426 //@todo _writeCFRule() => $blockColor => Index Color for bottom line
3427 //@todo _writeCFRule() => $blockColor => Index Color for diagonal line
3428 switch ($conditional->getStyle()->getBorders()->getDiagonal()->getBorderStyle()){
3429 case PHPExcel_Style_Border::BORDER_NONE : $blockColor |= 0x00 << 21; break;
3430 case PHPExcel_Style_Border::BORDER_THIN : $blockColor |= 0x01 << 21; break;
3431 case PHPExcel_Style_Border::BORDER_MEDIUM : $blockColor |= 0x02 << 21; break;
3432 case PHPExcel_Style_Border::BORDER_DASHED : $blockColor |= 0x03 << 21; break;
3433 case PHPExcel_Style_Border::BORDER_DOTTED : $blockColor |= 0x04 << 21; break;
3434 case PHPExcel_Style_Border::BORDER_THICK : $blockColor |= 0x05 << 21; break;
3435 case PHPExcel_Style_Border::BORDER_DOUBLE : $blockColor |= 0x06 << 21; break;
3436 case PHPExcel_Style_Border::BORDER_HAIR : $blockColor |= 0x07 << 21; break;
3437 case PHPExcel_Style_Border::BORDER_MEDIUMDASHED : $blockColor |= 0x08 << 21; break;
3438 case PHPExcel_Style_Border::BORDER_DASHDOT : $blockColor |= 0x09 << 21; break;
3439 case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT : $blockColor |= 0x0A << 21; break;
3440 case PHPExcel_Style_Border::BORDER_DASHDOTDOT : $blockColor |= 0x0B << 21; break;
3441 case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT : $blockColor |= 0x0C << 21; break;
3442 case PHPExcel_Style_Border::BORDER_SLANTDASHDOT : $blockColor |= 0x0D << 21; break;
3444 $dataBlockBorder = pack('vv', $blockLineStyle, $blockColor);
3446 if($bFormatFill == 1){
3447 // Fill Patern Style
3448 $blockFillPatternStyle = 0;
3449 switch ($conditional->getStyle()->getFill()->getFillType()){
3450 case PHPExcel_Style_Fill::FILL_NONE : $blockFillPatternStyle = 0x00; break;
3451 case PHPExcel_Style_Fill::FILL_SOLID : $blockFillPatternStyle = 0x01; break;
3452 case PHPExcel_Style_Fill::FILL_PATTERN_MEDIUMGRAY : $blockFillPatternStyle = 0x02; break;
3453 case PHPExcel_Style_Fill::FILL_PATTERN_DARKGRAY : $blockFillPatternStyle = 0x03; break;
3454 case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRAY : $blockFillPatternStyle = 0x04; break;
3455 case PHPExcel_Style_Fill::FILL_PATTERN_DARKHORIZONTAL : $blockFillPatternStyle = 0x05; break;
3456 case PHPExcel_Style_Fill::FILL_PATTERN_DARKVERTICAL : $blockFillPatternStyle = 0x06; break;
3457 case PHPExcel_Style_Fill::FILL_PATTERN_DARKDOWN : $blockFillPatternStyle = 0x07; break;
3458 case PHPExcel_Style_Fill::FILL_PATTERN_DARKUP : $blockFillPatternStyle = 0x08; break;
3459 case PHPExcel_Style_Fill::FILL_PATTERN_DARKGRID : $blockFillPatternStyle = 0x09; break;
3460 case PHPExcel_Style_Fill::FILL_PATTERN_DARKTRELLIS : $blockFillPatternStyle = 0x0A; break;
3461 case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTHORIZONTAL : $blockFillPatternStyle = 0x0B; break;
3462 case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTVERTICAL : $blockFillPatternStyle = 0x0C; break;
3463 case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTDOWN : $blockFillPatternStyle = 0x0D; break;
3464 case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTUP : $blockFillPatternStyle = 0x0E; break;
3465 case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRID : $blockFillPatternStyle = 0x0F; break;
3466 case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTTRELLIS : $blockFillPatternStyle = 0x10; break;
3467 case PHPExcel_Style_Fill::FILL_PATTERN_GRAY125 : $blockFillPatternStyle = 0x11; break;
3468 case PHPExcel_Style_Fill::FILL_PATTERN_GRAY0625 : $blockFillPatternStyle = 0x12; break;
3469 case PHPExcel_Style_Fill::FILL_GRADIENT_LINEAR : $blockFillPatternStyle = 0x00; break; // does not exist in BIFF8
3470 case PHPExcel_Style_Fill::FILL_GRADIENT_PATH : $blockFillPatternStyle = 0x00; break; // does not exist in BIFF8
3471 default : $blockFillPatternStyle = 0x00; break;
3473 // Color
3474 switch ($conditional->getStyle()->getFill()->getStartColor()->getRGB()) {
3475 case '000000': $colorIdxBg = 0x08; break;
3476 case 'FFFFFF': $colorIdxBg = 0x09; break;
3477 case 'FF0000': $colorIdxBg = 0x0A; break;
3478 case '00FF00': $colorIdxBg = 0x0B; break;
3479 case '0000FF': $colorIdxBg = 0x0C; break;
3480 case 'FFFF00': $colorIdxBg = 0x0D; break;
3481 case 'FF00FF': $colorIdxBg = 0x0E; break;
3482 case '00FFFF': $colorIdxBg = 0x0F; break;
3483 case '800000': $colorIdxBg = 0x10; break;
3484 case '008000': $colorIdxBg = 0x11; break;
3485 case '000080': $colorIdxBg = 0x12; break;
3486 case '808000': $colorIdxBg = 0x13; break;
3487 case '800080': $colorIdxBg = 0x14; break;
3488 case '008080': $colorIdxBg = 0x15; break;
3489 case 'C0C0C0': $colorIdxBg = 0x16; break;
3490 case '808080': $colorIdxBg = 0x17; break;
3491 case '9999FF': $colorIdxBg = 0x18; break;
3492 case '993366': $colorIdxBg = 0x19; break;
3493 case 'FFFFCC': $colorIdxBg = 0x1A; break;
3494 case 'CCFFFF': $colorIdxBg = 0x1B; break;
3495 case '660066': $colorIdxBg = 0x1C; break;
3496 case 'FF8080': $colorIdxBg = 0x1D; break;
3497 case '0066CC': $colorIdxBg = 0x1E; break;
3498 case 'CCCCFF': $colorIdxBg = 0x1F; break;
3499 case '000080': $colorIdxBg = 0x20; break;
3500 case 'FF00FF': $colorIdxBg = 0x21; break;
3501 case 'FFFF00': $colorIdxBg = 0x22; break;
3502 case '00FFFF': $colorIdxBg = 0x23; break;
3503 case '800080': $colorIdxBg = 0x24; break;
3504 case '800000': $colorIdxBg = 0x25; break;
3505 case '008080': $colorIdxBg = 0x26; break;
3506 case '0000FF': $colorIdxBg = 0x27; break;
3507 case '00CCFF': $colorIdxBg = 0x28; break;
3508 case 'CCFFFF': $colorIdxBg = 0x29; break;
3509 case 'CCFFCC': $colorIdxBg = 0x2A; break;
3510 case 'FFFF99': $colorIdxBg = 0x2B; break;
3511 case '99CCFF': $colorIdxBg = 0x2C; break;
3512 case 'FF99CC': $colorIdxBg = 0x2D; break;
3513 case 'CC99FF': $colorIdxBg = 0x2E; break;
3514 case 'FFCC99': $colorIdxBg = 0x2F; break;
3515 case '3366FF': $colorIdxBg = 0x30; break;
3516 case '33CCCC': $colorIdxBg = 0x31; break;
3517 case '99CC00': $colorIdxBg = 0x32; break;
3518 case 'FFCC00': $colorIdxBg = 0x33; break;
3519 case 'FF9900': $colorIdxBg = 0x34; break;
3520 case 'FF6600': $colorIdxBg = 0x35; break;
3521 case '666699': $colorIdxBg = 0x36; break;
3522 case '969696': $colorIdxBg = 0x37; break;
3523 case '003366': $colorIdxBg = 0x38; break;
3524 case '339966': $colorIdxBg = 0x39; break;
3525 case '003300': $colorIdxBg = 0x3A; break;
3526 case '333300': $colorIdxBg = 0x3B; break;
3527 case '993300': $colorIdxBg = 0x3C; break;
3528 case '993366': $colorIdxBg = 0x3D; break;
3529 case '333399': $colorIdxBg = 0x3E; break;
3530 case '333333': $colorIdxBg = 0x3F; break;
3531 default: $colorIdxBg = 0x41; break;
3533 // Fg Color
3534 switch ($conditional->getStyle()->getFill()->getEndColor()->getRGB()) {
3535 case '000000': $colorIdxFg = 0x08; break;
3536 case 'FFFFFF': $colorIdxFg = 0x09; break;
3537 case 'FF0000': $colorIdxFg = 0x0A; break;
3538 case '00FF00': $colorIdxFg = 0x0B; break;
3539 case '0000FF': $colorIdxFg = 0x0C; break;
3540 case 'FFFF00': $colorIdxFg = 0x0D; break;
3541 case 'FF00FF': $colorIdxFg = 0x0E; break;
3542 case '00FFFF': $colorIdxFg = 0x0F; break;
3543 case '800000': $colorIdxFg = 0x10; break;
3544 case '008000': $colorIdxFg = 0x11; break;
3545 case '000080': $colorIdxFg = 0x12; break;
3546 case '808000': $colorIdxFg = 0x13; break;
3547 case '800080': $colorIdxFg = 0x14; break;
3548 case '008080': $colorIdxFg = 0x15; break;
3549 case 'C0C0C0': $colorIdxFg = 0x16; break;
3550 case '808080': $colorIdxFg = 0x17; break;
3551 case '9999FF': $colorIdxFg = 0x18; break;
3552 case '993366': $colorIdxFg = 0x19; break;
3553 case 'FFFFCC': $colorIdxFg = 0x1A; break;
3554 case 'CCFFFF': $colorIdxFg = 0x1B; break;
3555 case '660066': $colorIdxFg = 0x1C; break;
3556 case 'FF8080': $colorIdxFg = 0x1D; break;
3557 case '0066CC': $colorIdxFg = 0x1E; break;
3558 case 'CCCCFF': $colorIdxFg = 0x1F; break;
3559 case '000080': $colorIdxFg = 0x20; break;
3560 case 'FF00FF': $colorIdxFg = 0x21; break;
3561 case 'FFFF00': $colorIdxFg = 0x22; break;
3562 case '00FFFF': $colorIdxFg = 0x23; break;
3563 case '800080': $colorIdxFg = 0x24; break;
3564 case '800000': $colorIdxFg = 0x25; break;
3565 case '008080': $colorIdxFg = 0x26; break;
3566 case '0000FF': $colorIdxFg = 0x27; break;
3567 case '00CCFF': $colorIdxFg = 0x28; break;
3568 case 'CCFFFF': $colorIdxFg = 0x29; break;
3569 case 'CCFFCC': $colorIdxFg = 0x2A; break;
3570 case 'FFFF99': $colorIdxFg = 0x2B; break;
3571 case '99CCFF': $colorIdxFg = 0x2C; break;
3572 case 'FF99CC': $colorIdxFg = 0x2D; break;
3573 case 'CC99FF': $colorIdxFg = 0x2E; break;
3574 case 'FFCC99': $colorIdxFg = 0x2F; break;
3575 case '3366FF': $colorIdxFg = 0x30; break;
3576 case '33CCCC': $colorIdxFg = 0x31; break;
3577 case '99CC00': $colorIdxFg = 0x32; break;
3578 case 'FFCC00': $colorIdxFg = 0x33; break;
3579 case 'FF9900': $colorIdxFg = 0x34; break;
3580 case 'FF6600': $colorIdxFg = 0x35; break;
3581 case '666699': $colorIdxFg = 0x36; break;
3582 case '969696': $colorIdxFg = 0x37; break;
3583 case '003366': $colorIdxFg = 0x38; break;
3584 case '339966': $colorIdxFg = 0x39; break;
3585 case '003300': $colorIdxFg = 0x3A; break;
3586 case '333300': $colorIdxFg = 0x3B; break;
3587 case '993300': $colorIdxFg = 0x3C; break;
3588 case '993366': $colorIdxFg = 0x3D; break;
3589 case '333399': $colorIdxFg = 0x3E; break;
3590 case '333333': $colorIdxFg = 0x3F; break;
3591 default: $colorIdxFg = 0x40; break;
3593 $dataBlockFill = pack('v', $blockFillPatternStyle);
3594 $dataBlockFill .= pack('v', $colorIdxFg | ($colorIdxBg << 7));
3596 if($bFormatProt == 1){
3597 $dataBlockProtection = 0;
3598 if($conditional->getStyle()->getProtection()->getLocked() == PHPExcel_Style_Protection::PROTECTION_PROTECTED){
3599 $dataBlockProtection = 1;
3601 if($conditional->getStyle()->getProtection()->getHidden() == PHPExcel_Style_Protection::PROTECTION_PROTECTED){
3602 $dataBlockProtection = 1 << 1;
3606 $data = pack('CCvvVv', $type, $operatorType, $szValue1, $szValue2, $flags, 0x0000);
3607 if($bFormatFont == 1){ // Block Formatting : OK
3608 $data .= $dataBlockFont;
3610 if($bFormatAlign == 1){
3611 $data .= $dataBlockAlign;
3613 if($bFormatBorder == 1){
3614 $data .= $dataBlockBorder;
3616 if($bFormatFill == 1){ // Block Formatting : OK
3617 $data .= $dataBlockFill;
3619 if($bFormatProt == 1){
3620 $data .= $dataBlockProtection;
3622 if(!is_null($operand1)){
3623 $data .= $operand1;
3625 if(!is_null($operand2)){
3626 $data .= $operand2;
3628 $header = pack('vv', $record, strlen($data));
3629 $this->_append($header . $data);
3633 * Write CFHeader record
3635 private function _writeCFHeader(){
3636 $record = 0x01B0; // Record identifier
3637 $length = 0x0016; // Bytes to follow
3639 $numColumnMin = null;
3640 $numColumnMax = null;
3641 $numRowMin = null;
3642 $numRowMax = null;
3643 $arrConditional = array();
3644 foreach ($this->_phpSheet->getConditionalStylesCollection() as $cellCoordinate => $conditionalStyles) {
3645 foreach ($conditionalStyles as $conditional) {
3646 if($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_EXPRESSION
3647 || $conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CELLIS){
3648 if(!in_array($conditional->getHashCode(), $arrConditional)){
3649 $arrConditional[] = $conditional->getHashCode();
3651 // Cells
3652 $arrCoord = PHPExcel_Cell::coordinateFromString($cellCoordinate);
3653 if(!is_numeric($arrCoord[0])){
3654 $arrCoord[0] = PHPExcel_Cell::columnIndexFromString($arrCoord[0]);
3656 if(is_null($numColumnMin) || ($numColumnMin > $arrCoord[0])){
3657 $numColumnMin = $arrCoord[0];
3659 if(is_null($numColumnMax) || ($numColumnMax < $arrCoord[0])){
3660 $numColumnMax = $arrCoord[0];
3662 if(is_null($numRowMin) || ($numRowMin > $arrCoord[1])){
3663 $numRowMin = $arrCoord[1];
3665 if(is_null($numRowMax) || ($numRowMax < $arrCoord[1])){
3666 $numRowMax = $arrCoord[1];
3671 $needRedraw = 1;
3672 $cellRange = pack('vvvv', $numRowMin-1, $numRowMax-1, $numColumnMin-1, $numColumnMax-1);
3674 $header = pack('vv', $record, $length);
3675 $data = pack('vv', count($arrConditional), $needRedraw);
3676 $data .= $cellRange;
3677 $data .= pack('v', 0x0001);
3678 $data .= $cellRange;
3679 $this->_append($header . $data);