source: sourcecode/application/libraries/PHPExcel/Writer/Excel5/Worksheet.php @ 1

Last change on this file since 1 was 1, checked in by dungnv, 11 years ago
File size: 121.1 KB
Line 
1<?php
2/**
3 * PHPExcel
4 *
5 * Copyright (c) 2006 - 2014 PHPExcel
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 *
21 * @category   PHPExcel
22 * @package    PHPExcel_Writer_Excel5
23 * @copyright  Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
24 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
25 * @version    1.8.0, 2014-03-02
26 */
27
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// */
62
63
64/**
65 * PHPExcel_Writer_Excel5_Worksheet
66 *
67 * @category   PHPExcel
68 * @package    PHPExcel_Writer_Excel5
69 * @copyright  Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
70 */
71class PHPExcel_Writer_Excel5_Worksheet extends PHPExcel_Writer_Excel5_BIFFwriter
72{
73        /**
74         * Formula parser
75         *
76         * @var PHPExcel_Writer_Excel5_Parser
77         */
78        private $_parser;
79
80        /**
81         * Maximum number of characters for a string (LABEL record in BIFF5)
82         * @var integer
83         */
84        public $_xls_strmax;
85
86        /**
87         * Array containing format information for columns
88         * @var array
89         */
90        public $_colinfo;
91
92        /**
93         * Array containing the selected area for the worksheet
94         * @var array
95         */
96        public $_selection;
97
98        /**
99         * The active pane for the worksheet
100         * @var integer
101         */
102        public $_active_pane;
103
104        /**
105         * Whether to use outline.
106         * @var integer
107         */
108        public $_outline_on;
109
110        /**
111         * Auto outline styles.
112         * @var bool
113         */
114        public $_outline_style;
115
116        /**
117         * Whether to have outline summary below.
118         * @var bool
119         */
120        public $_outline_below;
121
122        /**
123         * Whether to have outline summary at the right.
124         * @var bool
125         */
126        public $_outline_right;
127
128        /**
129         * Reference to the total number of strings in the workbook
130         * @var integer
131         */
132        public $_str_total;
133
134        /**
135         * Reference to the number of unique strings in the workbook
136         * @var integer
137         */
138        public $_str_unique;
139
140        /**
141         * Reference to the array containing all the unique strings in the workbook
142         * @var array
143         */
144        public $_str_table;
145
146        /**
147         * Color cache
148         */
149        private $_colors;
150
151        /**
152         * Index of first used row (at least 0)
153         * @var int
154         */
155        private $_firstRowIndex;
156
157        /**
158         * Index of last used row. (no used rows means -1)
159         * @var int
160         */
161        private $_lastRowIndex;
162
163        /**
164         * Index of first used column (at least 0)
165         * @var int
166         */
167        private $_firstColumnIndex;
168
169        /**
170         * Index of last used column (no used columns means -1)
171         * @var int
172         */
173        private $_lastColumnIndex;
174
175        /**
176         * Sheet object
177         * @var PHPExcel_Worksheet
178         */
179        public $_phpSheet;
180
181        /**
182         * Count cell style Xfs
183         *
184         * @var int
185         */
186        private $_countCellStyleXfs;
187
188        /**
189         * Escher object corresponding to MSODRAWING
190         *
191         * @var PHPExcel_Shared_Escher
192         */
193        private $_escher;
194
195        /**
196         * Array of font hashes associated to FONT records index
197         *
198         * @var array
199         */
200        public $_fntHashIndex;
201
202        /**
203         * Constructor
204         *
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
213         */
214        public function __construct(&$str_total, &$str_unique, &$str_table, &$colors,
215                                                                $parser, $preCalculateFormulas, $phpSheet)
216        {
217                // It needs to call its parent's constructor explicitly
218                parent::__construct();
219
220                // change BIFFwriter limit for CONTINUE records
221//              $this->_limit = 8224;
222
223
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;
230
231                $this->_phpSheet = $phpSheet;
232
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;
239
240                $this->_print_headers           = 0;
241
242                $this->_outline_style           = 0;
243                $this->_outline_below           = 1;
244                $this->_outline_right           = 1;
245                $this->_outline_on                      = 1;
246
247                $this->_fntHashIndex    = array();
248
249                // calculate values for DIMENSIONS record
250                $minR = 1;
251                $minC = 'A';
252
253                $maxR  = $this->_phpSheet->getHighestRow();
254                $maxC = $this->_phpSheet->getHighestColumn();
255
256                // Determine lowest and highest column and row
257//              $this->_firstRowIndex = ($minR > 65535) ? 65535 : $minR;
258                $this->_lastRowIndex = ($maxR > 65535) ? 65535 : $maxR ;
259
260                $this->_firstColumnIndex        = PHPExcel_Cell::columnIndexFromString($minC);
261                $this->_lastColumnIndex         = PHPExcel_Cell::columnIndexFromString($maxC);
262
263//              if ($this->_firstColumnIndex > 255) $this->_firstColumnIndex = 255;
264                if ($this->_lastColumnIndex > 255) $this->_lastColumnIndex = 255;
265
266                $this->_countCellStyleXfs = count($phpSheet->getParent()->getCellStyleXfCollection());
267        }
268
269        /**
270         * Add data to the beginning of the workbook (note the reverse order)
271         * and to the end of the workbook.
272         *
273         * @access public
274         * @see PHPExcel_Writer_Excel5_Workbook::storeWorkbook()
275         */
276        function close()
277        {
278                $_phpSheet = $this->_phpSheet;
279
280                $num_sheets = $_phpSheet->getParent()->getSheetCount();
281
282                // Write BOF record
283                $this->_storeBof(0x0010);
284
285                // Write PRINTHEADERS
286                $this->_writePrintHeaders();
287
288                // Write PRINTGRIDLINES
289                $this->_writePrintGridlines();
290
291                // Write GRIDSET
292                $this->_writeGridset();
293
294                // Calculate column widths
295                $_phpSheet->calculateColumnWidths();
296
297                // Column dimensions
298                if (($defaultWidth = $_phpSheet->getDefaultColumnDimension()->getWidth()) < 0) {
299                        $defaultWidth = PHPExcel_Shared_Font::getDefaultColumnWidthByFont($_phpSheet->getParent()->getDefaultStyle()->getFont());
300                }
301
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
308
309                        $width = $defaultWidth;
310
311                        $columnLetter = PHPExcel_Cell::stringFromColumnIndex($i);
312                        if (isset($columnDimensions[$columnLetter])) {
313                                $columnDimension = $columnDimensions[$columnLetter];
314                                if ($columnDimension->getWidth() >= 0) {
315                                        $width = $columnDimension->getWidth();
316                                }
317                                $hidden = $columnDimension->getVisible() ? 0 : 1;
318                                $level = $columnDimension->getOutlineLevel();
319                                $xfIndex = $columnDimension->getXfIndex() + 15; // there are 15 cell style Xfs
320                        }
321
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);
330                }
331
332                // Write GUTS
333                $this->_writeGuts();
334
335                // Write DEFAULTROWHEIGHT
336                $this->_writeDefaultRowHeight();
337
338                // Write WSBOOL
339                $this->_writeWsbool();
340
341                // Write horizontal and vertical page breaks
342                $this->_writeBreaks();
343
344                // Write page header
345                $this->_writeHeader();
346
347                // Write page footer
348                $this->_writeFooter();
349
350                // Write page horizontal centering
351                $this->_writeHcenter();
352
353                // Write page vertical centering
354                $this->_writeVcenter();
355
356                // Write left margin
357                $this->_writeMarginLeft();
358
359                // Write right margin
360                $this->_writeMarginRight();
361
362                // Write top margin
363                $this->_writeMarginTop();
364
365                // Write bottom margin
366                $this->_writeMarginBottom();
367
368                // Write page setup
369                $this->_writeSetup();
370
371                // Write sheet protection
372                $this->_writeProtect();
373
374                // Write SCENPROTECT
375                $this->_writeScenProtect();
376
377                // Write OBJECTPROTECT
378                $this->_writeObjectProtect();
379
380                // Write sheet password
381                $this->_writePassword();
382
383                // Write DEFCOLWIDTH record
384                $this->_writeDefcol();
385
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]);
391                        }
392                }
393                $autoFilterRange = $_phpSheet->getAutoFilter()->getRange();
394                if (!empty($autoFilterRange)) {
395                        // Write AUTOFILTERINFO
396                        $this->_writeAutoFilterInfo();
397                }
398
399                // Write sheet dimensions
400                $this->_writeDimensions();
401
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() );
406                }
407
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;
413
414                        // Don't break Excel!
415//                      if ($row + 1 > 65536 or $column + 1 > 256) {
416                        if ($row > 65535 || $column > 255) {
417                                break;
418                        }
419
420                        // Write cell value
421                        $xfIndex = $cell->getXfIndex() + 15; // there are 15 cell style Xfs
422
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()];
434                                        }
435                                        else {
436                                                $str_fontidx = 0;
437                                        }
438                                        $arrcRun[] = array('strlen' => $str_pos, 'fontidx' => $str_fontidx);
439                                        // Position FROM
440                                        $str_pos += PHPExcel_Shared_String::CountCharacters($element->getText(), 'UTF-8');
441                                }
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);
451                                                }
452                                                break;
453
454                                        case PHPExcel_Cell_DataType::TYPE_NUMERIC:
455                                                $this->_writeNumber($row, $column, $cVal, $xfIndex);
456                                                break;
457
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;
463
464                                        case PHPExcel_Cell_DataType::TYPE_BOOL:
465                                                $this->_writeBoolErr($row, $column, $cVal, 0, $xfIndex);
466                                                break;
467
468                                        case PHPExcel_Cell_DataType::TYPE_ERROR:
469                                                $this->_writeBoolErr($row, $column, self::_mapErrorCode($cVal), 1, $xfIndex);
470                                                break;
471
472                                }
473                        }
474                }
475
476                // Append
477                $this->_writeMsoDrawing();
478
479                // Write WINDOW2 record
480                $this->_writeWindow2();
481
482                // Write PLV record
483                $this->_writePageLayoutView();
484
485                // Write ZOOM record
486                $this->_writeZoom();
487                if ($_phpSheet->getFreezePane()) {
488                        $this->_writePanes();
489                }
490
491                // Write SELECTION record
492                $this->_writeSelection();
493
494                // Write MergedCellsTable Record
495                $this->_writeMergedCells();
496
497                // Hyperlinks
498                foreach ($_phpSheet->getHyperLinkCollection() as $coordinate => $hyperlink) {
499                        list($column, $row) = PHPExcel_Cell::coordinateFromString($coordinate);
500
501                        $url = $hyperlink->getUrl();
502
503                        if ( strpos($url, 'sheet://') !== false ) {
504                                // internal to current workbook
505                                $url = str_replace('sheet://', 'internal:', $url);
506
507                        } else if ( preg_match('/^(http:|https:|ftp:|mailto:)/', $url) ) {
508                                // URL
509                                // $url = $url;
510
511                        } else {
512                                // external (local file)
513                                $url = 'external:' . $url;
514                        }
515
516                        $this->_writeUrl($row - 1, PHPExcel_Cell::columnIndexFromString($column) - 1, $url);
517                }
518
519                $this->_writeDataValidity();
520                $this->_writeSheetLayout();
521
522                // Write SHEETPROTECTION record
523                $this->_writeSheetProtection();
524                $this->_writeRangeProtection();
525
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);
541                                                }
542                                        }
543                                }
544                        }
545                }
546
547                $this->_storeEof();
548        }
549
550        /**
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
554         *
555         * @param string $range E.g. 'A1' or 'A1:B6'
556         * @return string Binary data
557         */
558        private function _writeBIFF8CellRangeAddressFixed($range = 'A1')
559        {
560                $explodes = explode(':', $range);
561
562                // extract first cell, e.g. 'A1'
563                $firstCell = $explodes[0];
564
565                // extract last cell, e.g. 'B6'
566                if (count($explodes) == 1) {
567                        $lastCell = $firstCell;
568                } else {
569                        $lastCell = $explodes[1];
570                }
571
572                $firstCellCoordinates = PHPExcel_Cell::coordinateFromString($firstCell); // e.g. array(0, 1)
573                $lastCellCoordinates  = PHPExcel_Cell::coordinateFromString($lastCell);  // e.g. array(1, 6)
574
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
580                ));
581        }
582
583        /**
584         * Retrieves data from memory in one chunk, or from disk in $buffer
585         * sized chunks.
586         *
587         * @return string The data
588         */
589        function getData()
590        {
591                $buffer = 4096;
592
593                // Return data stored in memory
594                if (isset($this->_data)) {
595                        $tmp   = $this->_data;
596                        unset($this->_data);
597                        return $tmp;
598                }
599                // No data to return
600                return false;
601        }
602
603        /**
604         * Set the option to print the row and column headers on the printed page.
605         *
606         * @access public
607         * @param integer $print Whether to print the headers or not. Defaults to 1 (print).
608         */
609        function printRowColHeaders($print = 1)
610        {
611                $this->_print_headers = $print;
612        }
613
614        /**
615         * This method sets the properties for outlining and grouping. The defaults
616         * correspond to Excel's defaults.
617         *
618         * @param bool $visible
619         * @param bool $symbols_below
620         * @param bool $symbols_right
621         * @param bool $auto_style
622         */
623        function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false)
624        {
625                $this->_outline_on      = $visible;
626                $this->_outline_below = $symbols_below;
627                $this->_outline_right = $symbols_right;
628                $this->_outline_style = $auto_style;
629
630                // Ensure this is a boolean vale for Window2
631                if ($this->_outline_on) {
632                        $this->_outline_on = 1;
633                }
634         }
635
636        /**
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.
640         *
641         * Returns  0 : normal termination
642         *               -2 : row or column out of range
643         *
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
649         */
650        private function _writeNumber($row, $col, $num, $xfIndex)
651        {
652                $record = 0x0203;                                // Record identifier
653                $length = 0x000E;                                // Number of bytes to follow
654
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);
660                }
661
662                $this->_append($header.$data.$xl_double);
663                return(0);
664        }
665
666        /**
667         * Write a LABELSST record or a LABEL record. Which one depends on BIFF version
668         *
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
673         */
674        private function _writeString($row, $col, $str, $xfIndex)
675        {
676                $this->_writeLabelSst($row, $col, $str, $xfIndex);
677        }
678
679        /**
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
687         */
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);
692
693                /* check if string is already present */
694                if (!isset($this->_str_table[$str])) {
695                        $this->_str_table[$str] = $this->_str_unique++;
696                }
697                $this->_str_total++;
698
699                $header = pack('vv',   $record, $length);
700                $data   = pack('vvvV', $row, $col, $xfIndex, $this->_str_table[$str]);
701                $this->_append($header.$data);
702        }
703
704        /**
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
711         *
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
718         */
719        private function _writeLabel($row, $col, $str, $xfIndex)
720        {
721                $strlen = strlen($str);
722                $record = 0x0204;                                  // Record identifier
723                $length = 0x0008 + $strlen;              // Bytes to follow
724
725                $str_error = 0;
726
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;
732                }
733
734                $header = pack("vv",   $record, $length);
735                $data   = pack("vvvv", $row, $col, $xfIndex, $strlen);
736                $this->_append($header . $data . $str);
737                return($str_error);
738        }
739
740        /**
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
747         *
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
754         */
755        private function _writeLabelSst($row, $col, $str, $xfIndex)
756        {
757                $record = 0x00FD;                                  // Record identifier
758                $length = 0x000A;                                  // Bytes to follow
759
760                $str = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($str);
761
762                /* check if string is already present */
763                if (!isset($this->_str_table[$str])) {
764                        $this->_str_table[$str] = $this->_str_unique++;
765                }
766                $this->_str_total++;
767
768                $header = pack('vv',   $record, $length);
769                $data   = pack('vvvV', $row, $col, $xfIndex, $this->_str_table[$str]);
770                $this->_append($header.$data);
771        }
772
773        /**
774         * Writes a note associated with the cell given by the row and column.
775         * NOTE records don't have a length limit.
776         *
777         * @param integer $row  Zero indexed row
778         * @param integer $col  Zero indexed column
779         * @param string  $note   The note to write
780         */
781        private function _writeNote($row, $col, $note)
782        {
783                $note_length    = strlen($note);
784                $record                 = 0x001C;                       // Record identifier
785                $max_length             = 2048;                         // Maximun length for a NOTE record
786
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));
792
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);
799                }
800                return(0);
801        }
802
803        /**
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.
807         *
808         * A blank cell without a format serves no purpose. Therefore, we don't write
809         * a BLANK record unless a format is specified.
810         *
811         * Returns  0 : normal termination (including no format)
812         *               -1 : insufficient number of arguments
813         *               -2 : row or column out of range
814         *
815         * @param integer $row  Zero indexed row
816         * @param integer $col  Zero indexed column
817         * @param mixed   $xfIndex The XF format index
818         */
819        function _writeBlank($row, $col, $xfIndex)
820        {
821                $record = 0x0201;                                // Record identifier
822                $length = 0x0006;                                // Number of bytes to follow
823
824                $header = pack("vv",  $record, $length);
825                $data     = pack("vvv", $row, $col, $xfIndex);
826                $this->_append($header . $data);
827                return 0;
828        }
829
830        /**
831         * Write a boolean or an error type to the specified row and column (zero indexed)
832         *
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
838         */
839        private function _writeBoolErr($row, $col, $value, $isError, $xfIndex)
840        {
841                $record = 0x0205;
842                $length = 8;
843
844                $header = pack("vv",  $record, $length);
845                $data     = pack("vvvCC", $row, $col, $xfIndex, $value, $isError);
846                $this->_append($header . $data);
847                return 0;
848        }
849
850        /**
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.
854         *
855         * Returns  0 : normal termination
856         *               -1 : formula errors (bad formula)
857         *               -2 : row or column out of range
858         *
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
865         */
866        private function _writeFormula($row, $col, $formula, $xfIndex, $calculatedValue)
867        {
868                $record = 0x0006;        // Record identifier
869
870                // Initialize possible additional value for STRING record that should be written after the FORMULA record?
871                $stringValue = null;
872
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);
894                                }
895                        } else {
896                                // We are really not supposed to reach here
897                                $num = pack('d', 0x00);
898                        }
899                } else {
900                        $num = pack('d', 0x00);
901                }
902
903                $grbit          = 0x03;                         // Option flags
904                $unknown        = 0x0000;                       // Must be zero
905
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;
913                }
914
915                // Parse the formula using the parser in Parser.php
916                try {
917                        $error = $this->_parser->parse($formula);
918                        $formula = $this->_parser->toReversePolish();
919
920                        $formlen        = strlen($formula);     // Length of the binary string
921                        $length  = 0x16 + $formlen;      // Length of the record data
922
923                        $header = pack("vv",      $record, $length);
924
925                        $data     = pack("vvv", $row, $col, $xfIndex)
926                                                . $num
927                                                . pack("vVv", $grbit, $unknown, $formlen);
928                        $this->_append($header . $data . $formula);
929
930                        // Append also a STRING record if necessary
931                        if ($stringValue !== null) {
932                                $this->_writeStringRecord($stringValue);
933                        }
934
935                        return 0;
936
937                } catch (PHPExcel_Exception $e) {
938                        // do nothing
939                }
940
941        }
942
943        /**
944         * Write a STRING record. This
945         *
946         * @param string $stringValue
947         */
948        private function _writeStringRecord($stringValue)
949        {
950                $record = 0x0207;        // Record identifier
951                $data = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($stringValue);
952
953                $length = strlen($data);
954                $header = pack('vv', $record, $length);
955
956                $this->_append($header . $data);
957        }
958
959        /**
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.
966         *
967         * The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external
968         * directory url.
969         *
970         * Returns  0 : normal termination
971         *               -2 : row or column out of range
972         *               -3 : long string truncated to 255 chars
973         *
974         * @param integer $row  Row
975         * @param integer $col  Column
976         * @param string  $url  URL string
977         * @return integer
978         */
979        private function _writeUrl($row, $col, $url)
980        {
981                // Add start row and col to arg list
982                return($this->_writeUrlRange($row, $col, $row, $col, $url));
983        }
984
985        /**
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').
990         *
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
999         */
1000        function _writeUrlRange($row1, $col1, $row2, $col2, $url)
1001        {
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));
1005                }
1006                if (preg_match('[^external:]', $url)) {
1007                        return($this->_writeUrlExternal($row1, $col1, $row2, $col2, $url));
1008                }
1009                return($this->_writeUrlWeb($row1, $col1, $row2, $col2, $url));
1010        }
1011
1012        /**
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.
1016         *
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
1025         */
1026        function _writeUrlWeb($row1, $col1, $row2, $col2, $url)
1027        {
1028                $record   = 0x01B8;                                        // Record identifier
1029                $length   = 0x00000;                                      // Bytes to follow
1030
1031                // Pack the undocumented parts of the hyperlink stream
1032                $unknown1       = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
1033                $unknown2       = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B");
1034
1035                // Pack the option flags
1036                $options         = pack("V", 0x03);
1037
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";
1041
1042                // Pack the length of the URL
1043                $url_len         = pack("V", strlen($url));
1044
1045                // Calculate the data length
1046                $length   = 0x34 + strlen($url);
1047
1048                // Pack the header data
1049                $header   = pack("vv",   $record, $length);
1050                $data           = pack("vvvv", $row1, $row2, $col1, $col2);
1051
1052                // Write the packed data
1053                $this->_append($header . $data .
1054                                           $unknown1 . $options .
1055                                           $unknown2 . $url_len . $url);
1056                return 0;
1057        }
1058
1059        /**
1060         * Used to write internal reference hyperlinks such as "Sheet1!A1".
1061         *
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
1070         */
1071        function _writeUrlInternal($row1, $col1, $row2, $col2, $url)
1072        {
1073                $record   = 0x01B8;                                        // Record identifier
1074                $length   = 0x00000;                                      // Bytes to follow
1075
1076                // Strip URL type
1077                $url = preg_replace('/^internal:/', '', $url);
1078
1079                // Pack the undocumented parts of the hyperlink stream
1080                $unknown1       = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
1081
1082                // Pack the option flags
1083                $options         = pack("V", 0x08);
1084
1085                // Convert the URL type and to a null terminated wchar string
1086                $url .= "\0";
1087
1088                // character count
1089                $url_len = PHPExcel_Shared_String::CountCharacters($url);
1090                $url_len = pack('V', $url_len);
1091
1092                $url = PHPExcel_Shared_String::ConvertEncoding($url, 'UTF-16LE', 'UTF-8');
1093
1094                // Calculate the data length
1095                $length   = 0x24 + strlen($url);
1096
1097                // Pack the header data
1098                $header   = pack("vv",   $record, $length);
1099                $data           = pack("vvvv", $row1, $row2, $col1, $col2);
1100
1101                // Write the packed data
1102                $this->_append($header . $data .
1103                                           $unknown1 . $options .
1104                                           $url_len . $url);
1105                return 0;
1106        }
1107
1108        /**
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'.
1111         *
1112         * Note: Excel writes some relative links with the $dir_long string. We ignore
1113         * these cases for the sake of simpler code.
1114         *
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
1123         */
1124        function _writeUrlExternal($row1, $col1, $row2, $col2, $url)
1125        {
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));
1130                }
1131
1132                $record   = 0x01B8;                                        // Record identifier
1133                $length   = 0x00000;                                      // Bytes to follow
1134
1135                // Strip URL type and change Unix dir separator to Dos style (if needed)
1136                //
1137                $url = preg_replace('/^external:/', '', $url);
1138                $url = preg_replace('/\//', "\\", $url);
1139
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
1144
1145                $absolute = 0x00; // relative path
1146                if ( preg_match('/^[A-Z]:/', $url) ) {
1147                        $absolute = 0x02; // absolute path on Windows, e.g. C:\...
1148                }
1149                $link_type                         = 0x01 | $absolute;
1150
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;
1157                }
1158
1159
1160                // Pack the link type
1161                $link_type   = pack("V", $link_type);
1162
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);
1166
1167                // Store the short dos dir name (null terminated)
1168                $dir_short   = preg_replace("/\.\.\\\/", '', $dir_long) . "\0";
1169
1170                // Store the long dir name as a wchar string (non-null terminated)
1171                $dir_long          = $dir_long . "\0";
1172
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);
1177
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                                                                                     );
1183
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                ;*/
1199
1200                // Pack the header data
1201                $length   = strlen($data);
1202                $header   = pack("vv", $record, $length);
1203
1204                // Write the packed data
1205                $this->_append($header. $data);
1206                return 0;
1207        }
1208
1209        /**
1210         * This method is used to set the height and format for a row.
1211         *
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]
1218         */
1219        private function _writeRow($row, $height, $xfIndex, $hidden = false, $level = 0)
1220        {
1221                $record   = 0x0208;                        // Record identifier
1222                $length   = 0x0010;                        // Number of bytes to follow
1223
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;
1230
1231                if ( $height < 0 ){
1232                        $height = null;
1233                }
1234
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
1240                }
1241
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.
1247
1248                $grbit |= $level;
1249                if ($hidden) {
1250                        $grbit |= 0x0020;
1251                }
1252                if ($height !== null) {
1253                        $grbit |= 0x0040; // fUnsynced
1254                }
1255                if ($xfIndex !== 0xF) {
1256                        $grbit |= 0x0080;
1257                }
1258                $grbit |= 0x0100;
1259
1260                $header   = pack("vv",     $record, $length);
1261                $data    = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw,
1262                                                                         $irwMac,$reserved, $grbit, $ixfe);
1263                $this->_append($header.$data);
1264        }
1265
1266        /**
1267         * Writes Excel DIMENSIONS to define the area in which there is data.
1268         */
1269        private function _writeDimensions()
1270        {
1271                $record = 0x0200; // Record identifier
1272
1273                $length = 0x000E;
1274                $data = pack('VVvvv'
1275                                , $this->_firstRowIndex
1276                                , $this->_lastRowIndex + 1
1277                                , $this->_firstColumnIndex
1278                                , $this->_lastColumnIndex + 1
1279                                , 0x0000 // reserved
1280                        );
1281
1282                $header = pack("vv", $record, $length);
1283                $this->_append($header.$data);
1284        }
1285
1286        /**
1287         * Write BIFF record Window2.
1288         */
1289        private function _writeWindow2()
1290        {
1291                $record          = 0x023E;       // Record identifier
1292                $length          = 0x0012;
1293
1294                $grbit            = 0x00B6;      // Option flags
1295                $rwTop            = 0x0000;      // Top row visible in window
1296                $colLeft                = 0x0000;        // Leftmost column visible in window
1297
1298
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;
1313
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;
1326
1327                $header  = pack("vv",   $record, $length);
1328                $data   = pack("vvv", $grbit, $rwTop, $colLeft);
1329
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();
1334
1335                $data .= pack("vvvvV", $rgbHdr, 0x0000, $zoom_factor_page_break, $zoom_factor_normal, 0x00000000);
1336
1337                $this->_append($header.$data);
1338        }
1339
1340        /**
1341         * Write BIFF record DEFAULTROWHEIGHT.
1342         */
1343        private function _writeDefaultRowHeight()
1344        {
1345                $defaultRowHeight = $this->_phpSheet->getDefaultRowDimension()->getRowHeight();
1346
1347                if ($defaultRowHeight < 0) {
1348                        return;
1349                }
1350
1351                // convert to twips
1352                $defaultRowHeight = (int) 20 * $defaultRowHeight;
1353
1354                $record   = 0x0225;       // Record identifier
1355                $length   = 0x0004;       // Number of bytes to follow
1356
1357                $header   = pack("vv", $record, $length);
1358                $data    = pack("vv",  1, $defaultRowHeight);
1359                $this->_append($header . $data);
1360        }
1361
1362        /**
1363         * Write BIFF record DEFCOLWIDTH if COLINFO records are in use.
1364         */
1365        private function _writeDefcol()
1366        {
1367                $defaultColWidth = 8;
1368
1369                $record   = 0x0055;       // Record identifier
1370                $length   = 0x0002;       // Number of bytes to follow
1371
1372                $header = pack("vv", $record, $length);
1373                $data = pack("v", $defaultColWidth);
1374                $this->_append($header . $data);
1375        }
1376
1377        /**
1378         * Write BIFF record COLINFO to define column widths
1379         *
1380         * Note: The SDK says the record length is 0x0B but Excel writes a 0x0C
1381         * length record.
1382         *
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
1390         */
1391        private function _writeColinfo($col_array)
1392        {
1393                if (isset($col_array[0])) {
1394                        $colFirst = $col_array[0];
1395                }
1396                if (isset($col_array[1])) {
1397                        $colLast = $col_array[1];
1398                }
1399                if (isset($col_array[2])) {
1400                        $coldx = $col_array[2];
1401                } else {
1402                        $coldx = 8.43;
1403                }
1404                if (isset($col_array[3])) {
1405                        $xfIndex = $col_array[3];
1406                } else {
1407                        $xfIndex = 15;
1408                }
1409                if (isset($col_array[4])) {
1410                        $grbit = $col_array[4];
1411                } else {
1412                        $grbit = 0;
1413                }
1414                if (isset($col_array[5])) {
1415                        $level = $col_array[5];
1416                } else {
1417                        $level = 0;
1418                }
1419                $record   = 0x007D;               // Record identifier
1420                $length   = 0x000C;               // Number of bytes to follow
1421
1422                $coldx   *= 256;                         // Convert to units of 1/256 of a char
1423
1424                $ixfe    = $xfIndex;
1425                $reserved = 0x0000;                     // Reserved
1426
1427                $level = max(0, min($level, 7));
1428                $grbit |= $level << 8;
1429
1430                $header   = pack("vv",   $record, $length);
1431                $data    = pack("vvvvvv", $colFirst, $colLast, $coldx,
1432                                                                   $ixfe, $grbit, $reserved);
1433                $this->_append($header.$data);
1434        }
1435
1436        /**
1437         * Write BIFF record SELECTION.
1438         */
1439        private function _writeSelection()
1440        {
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];
1450                }
1451
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
1455
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
1459
1460                // make sure we are not out of bounds
1461                $colFirst = min($colFirst, 255);
1462                $colLast  = min($colLast,  255);
1463
1464                $rwFirst = min($rwFirst, 65535);
1465                $rwLast  = min($rwLast,  65535);
1466
1467                $record   = 0x001D;                               // Record identifier
1468                $length   = 0x000F;                               // Number of bytes to follow
1469
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
1475
1476                if (!isset($rwLast)) {
1477                        $rwLast   = $rwFirst;      // Last  row in reference
1478                }
1479                if (!isset($colLast)) {
1480                        $colLast  = $colFirst;    // Last  col in reference
1481                }
1482
1483                // Swap last row/col for first row/col as necessary
1484                if ($rwFirst > $rwLast) {
1485                        list($rwFirst, $rwLast) = array($rwLast, $rwFirst);
1486                }
1487
1488                if ($colFirst > $colLast) {
1489                        list($colFirst, $colLast) = array($colLast, $colFirst);
1490                }
1491
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);
1498        }
1499
1500        /**
1501         * Store the MERGEDCELLS records for all ranges of merged cells
1502         */
1503        private function _writeMergedCells()
1504        {
1505                $mergeCells = $this->_phpSheet->getMergeCells();
1506                $countMergeCells = count($mergeCells);
1507
1508                if ($countMergeCells == 0) {
1509                        return;
1510                }
1511
1512                // maximum allowed number of merged cells per record
1513                $maxCountMergeCellsPerRecord = 1027;
1514
1515                // record identifier
1516                $record = 0x00E5;
1517
1518                // counter for total number of merged cells treated so far by the writer
1519                $i = 0;
1520
1521                // counter for number of merged cells written in record currently being written
1522                $j = 0;
1523
1524                // initialize record data
1525                $recordData = '';
1526
1527                // loop through the merged cells
1528                foreach ($mergeCells as $mergeCell) {
1529                        ++$i;
1530                        ++$j;
1531
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);
1537
1538                        $recordData .= pack('vvvv', $firstRow - 1, $lastRow - 1, PHPExcel_Cell::columnIndexFromString($firstColumn) - 1, PHPExcel_Cell::columnIndexFromString($lastColumn) - 1);
1539
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);
1546
1547                                // initialize for next record, if any
1548                                $recordData = '';
1549                                $j = 0;
1550                        }
1551                }
1552        }
1553
1554        /**
1555         * Write SHEETLAYOUT record
1556         */
1557        private function _writeSheetLayout()
1558        {
1559                if (!$this->_phpSheet->isTabColorSet()) {
1560                        return;
1561                }
1562
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
1572                );
1573
1574                $length = strlen($recordData);
1575
1576                $record = 0x0862; // Record identifier
1577                $header = pack('vv', $record, $length);
1578                $this->_append($header . $recordData);
1579        }
1580
1581        /**
1582         * Write SHEETPROTECTION
1583         */
1584        private function _writeSheetProtection()
1585        {
1586                // record identifier
1587                $record = 0x0867;
1588
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 ;
1605
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
1617                );
1618
1619                $length = strlen($recordData);
1620                $header = pack('vv', $record, $length);
1621
1622                $this->_append($header . $recordData);
1623        }
1624
1625        /**
1626         * Write BIFF record RANGEPROTECTION
1627         *
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
1630         */
1631        private function _writeRangeProtection()
1632        {
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);
1637
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
1650                        );
1651
1652                        foreach ($cellRanges as $cellRange) {
1653                                $recordData .= $this->_writeBIFF8CellRangeAddressFixed($cellRange);
1654                        }
1655
1656                        // the rgbFeat structure
1657                        $recordData .= pack(
1658                                'VV',
1659                                0x0000,
1660                                hexdec($password)
1661                        );
1662
1663                        $recordData .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong('p' . md5($recordData));
1664
1665                        $length = strlen($recordData);
1666
1667                        $record = 0x0868;               // Record identifier
1668                        $header = pack("vv", $record, $length);
1669                        $this->_append($header . $recordData);
1670                }
1671        }
1672
1673        /**
1674         * Write BIFF record EXTERNCOUNT to indicate the number of external sheet
1675         * references in a worksheet.
1676         *
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.
1682         *
1683         * @param integer $count The number of external sheet references in this worksheet
1684         */
1685        private function _writeExterncount($count)
1686        {
1687                $record = 0x0016;                 // Record identifier
1688                $length = 0x0002;                 // Number of bytes to follow
1689
1690                $header = pack("vv", $record, $length);
1691                $data   = pack("v",  $count);
1692                $this->_append($header . $data);
1693        }
1694
1695        /**
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.
1700         *
1701         * @param string $sheetname The name of a external worksheet
1702         */
1703        private function _writeExternsheet($sheetname)
1704        {
1705                $record = 0x0017;                // Record identifier
1706
1707                // References to the current sheet are encoded differently to references to
1708                // external sheets.
1709                //
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
1719                }
1720
1721                $header = pack("vv",  $record, $length);
1722                $data   = pack("CC", $cch, $rgch);
1723                $this->_append($header . $data . $sheetname);
1724        }
1725
1726        /**
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.
1731         */
1732        private function _writePanes()
1733        {
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;
1742                }
1743
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;
1752                }
1753                $record  = 0x0041;         // Record identifier
1754                $length  = 0x000A;         // Number of bytes to follow
1755
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;
1761                        }
1762                        if (!isset($colLeft)) {
1763                                $colLeft = $x;
1764                        }
1765                } else {
1766                        // Set default values for $rwTop and $colLeft
1767                        if (!isset($rwTop)) {
1768                                $rwTop   = 0;
1769                        }
1770                        if (!isset($colLeft)) {
1771                                $colLeft = 0;
1772                        }
1773
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.
1778                        //
1779                        $y = 20*$y + 255;
1780                        $x = 113.879*$x + 390;
1781                }
1782
1783
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.
1786                //
1787                if (!isset($pnnAct)) {
1788                        if ($x != 0 && $y != 0) {
1789                                $pnnAct = 0; // Bottom right
1790                        }
1791                        if ($x != 0 && $y == 0) {
1792                                $pnnAct = 1; // Top right
1793                        }
1794                        if ($x == 0 && $y != 0) {
1795                                $pnnAct = 2; // Bottom left
1796                        }
1797                        if ($x == 0 && $y == 0) {
1798                                $pnnAct = 3; // Top left
1799                        }
1800                }
1801
1802                $this->_active_pane = $pnnAct; // Used in _writeSelection
1803
1804                $header  = pack("vv",   $record, $length);
1805                $data      = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct);
1806                $this->_append($header . $data);
1807        }
1808
1809        /**
1810         * Store the page setup SETUP BIFF record.
1811         */
1812        private function _writeSetup()
1813        {
1814                $record    = 0x00A1;                              // Record identifier
1815                $length    = 0x0022;                              // Number of bytes to follow
1816
1817                $iPaperSize   = $this->_phpSheet->getPageSetup()->getPaperSize();       // Paper size
1818
1819                $iScale = $this->_phpSheet->getPageSetup()->getScale() ?
1820                        $this->_phpSheet->getPageSetup()->getScale() : 100;   // Print scaling factor
1821
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
1828
1829                $numHdr    = $this->_phpSheet->getPageMargins()->getHeader();  // Header Margin
1830
1831                $numFtr    = $this->_phpSheet->getPageMargins()->getFooter();   // Footer Margin
1832                $iCopies          = 0x01;                                // Number of copies
1833
1834                $fLeftToRight = 0x0;                                     // Print over then down
1835
1836                // Page orientation
1837                $fLandscape = ($this->_phpSheet->getPageSetup()->getOrientation() == PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE) ?
1838                        0x0 : 0x1;
1839
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
1846
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;
1855
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);
1861                }
1862
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);
1875        }
1876
1877        /**
1878         * Store the header caption BIFF record.
1879         */
1880        private function _writeHeader()
1881        {
1882                $record  = 0x0014;                         // Record identifier
1883
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 = '';
1890                }
1891                */
1892
1893                $recordData = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($this->_phpSheet->getHeaderFooter()->getOddHeader());
1894                $length = strlen($recordData);
1895
1896                $header   = pack("vv", $record, $length);
1897
1898                $this->_append($header . $recordData);
1899        }
1900
1901        /**
1902         * Store the footer caption BIFF record.
1903         */
1904        private function _writeFooter()
1905        {
1906                $record  = 0x0015;                         // Record identifier
1907
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 = '';
1914                }
1915                */
1916
1917                $recordData = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($this->_phpSheet->getHeaderFooter()->getOddFooter());
1918                $length = strlen($recordData);
1919
1920                $header = pack("vv", $record, $length);
1921
1922                $this->_append($header . $recordData);
1923        }
1924
1925        /**
1926         * Store the horizontal centering HCENTER BIFF record.
1927         *
1928         * @access private
1929         */
1930        private function _writeHcenter()
1931        {
1932                $record   = 0x0083;                       // Record identifier
1933                $length   = 0x0002;                       // Bytes to follow
1934
1935                $fHCenter = $this->_phpSheet->getPageSetup()->getHorizontalCentered() ? 1 : 0;   // Horizontal centering
1936
1937                $header = pack("vv", $record, $length);
1938                $data     = pack("v",  $fHCenter);
1939
1940                $this->_append($header.$data);
1941        }
1942
1943        /**
1944         * Store the vertical centering VCENTER BIFF record.
1945         */
1946        private function _writeVcenter()
1947        {
1948                $record   = 0x0084;                       // Record identifier
1949                $length   = 0x0002;                       // Bytes to follow
1950
1951                $fVCenter = $this->_phpSheet->getPageSetup()->getVerticalCentered() ? 1 : 0;     // Horizontal centering
1952
1953                $header = pack("vv", $record, $length);
1954                $data     = pack("v",  $fVCenter);
1955                $this->_append($header . $data);
1956        }
1957
1958        /**
1959         * Store the LEFTMARGIN BIFF record.
1960         */
1961        private function _writeMarginLeft()
1962        {
1963                $record  = 0x0026;                                 // Record identifier
1964                $length  = 0x0008;                                 // Bytes to follow
1965
1966                $margin  = $this->_phpSheet->getPageMargins()->getLeft();        // Margin in inches
1967
1968                $header = pack("vv",  $record, $length);
1969                $data     = pack("d",   $margin);
1970                if (self::getByteOrder()) { // if it's Big Endian
1971                        $data = strrev($data);
1972                }
1973
1974                $this->_append($header . $data);
1975        }
1976
1977        /**
1978         * Store the RIGHTMARGIN BIFF record.
1979         */
1980        private function _writeMarginRight()
1981        {
1982                $record  = 0x0027;                                 // Record identifier
1983                $length  = 0x0008;                                 // Bytes to follow
1984
1985                $margin  = $this->_phpSheet->getPageMargins()->getRight();       // Margin in inches
1986
1987                $header = pack("vv",  $record, $length);
1988                $data     = pack("d",   $margin);
1989                if (self::getByteOrder()) { // if it's Big Endian
1990                        $data = strrev($data);
1991                }
1992
1993                $this->_append($header . $data);
1994        }
1995
1996        /**
1997         * Store the TOPMARGIN BIFF record.
1998         */
1999        private function _writeMarginTop()
2000        {
2001                $record  = 0x0028;                                 // Record identifier
2002                $length  = 0x0008;                                 // Bytes to follow
2003
2004                $margin  = $this->_phpSheet->getPageMargins()->getTop();         // Margin in inches
2005
2006                $header = pack("vv",  $record, $length);
2007                $data     = pack("d",   $margin);
2008                if (self::getByteOrder()) { // if it's Big Endian
2009                        $data = strrev($data);
2010                }
2011
2012                $this->_append($header . $data);
2013        }
2014
2015        /**
2016         * Store the BOTTOMMARGIN BIFF record.
2017         */
2018        private function _writeMarginBottom()
2019        {
2020                $record  = 0x0029;                                 // Record identifier
2021                $length  = 0x0008;                                 // Bytes to follow
2022
2023                $margin  = $this->_phpSheet->getPageMargins()->getBottom();      // Margin in inches
2024
2025                $header = pack("vv",  $record, $length);
2026                $data     = pack("d",   $margin);
2027                if (self::getByteOrder()) { // if it's Big Endian
2028                        $data = strrev($data);
2029                }
2030
2031                $this->_append($header . $data);
2032        }
2033
2034        /**
2035         * Write the PRINTHEADERS BIFF record.
2036         */
2037        private function _writePrintHeaders()
2038        {
2039                $record   = 0x002a;                                // Record identifier
2040                $length   = 0x0002;                                // Bytes to follow
2041
2042                $fPrintRwCol = $this->_print_headers;    // Boolean flag
2043
2044                $header   = pack("vv", $record, $length);
2045                $data           = pack("v", $fPrintRwCol);
2046                $this->_append($header . $data);
2047        }
2048
2049        /**
2050         * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the
2051         * GRIDSET record.
2052         */
2053        private function _writePrintGridlines()
2054        {
2055                $record   = 0x002b;                                     // Record identifier
2056                $length   = 0x0002;                                     // Bytes to follow
2057
2058                $fPrintGrid  = $this->_phpSheet->getPrintGridlines() ? 1 : 0;   // Boolean flag
2059
2060                $header   = pack("vv", $record, $length);
2061                $data     = pack("v", $fPrintGrid);
2062                $this->_append($header . $data);
2063        }
2064
2065        /**
2066         * Write the GRIDSET BIFF record. Must be used in conjunction with the
2067         * PRINTGRIDLINES record.
2068         */
2069        private function _writeGridset()
2070        {
2071                $record   = 0x0082;                                             // Record identifier
2072                $length   = 0x0002;                                             // Bytes to follow
2073
2074                $fGridSet       = !$this->_phpSheet->getPrintGridlines();        // Boolean flag
2075
2076                $header   = pack("vv",  $record, $length);
2077                $data           = pack("v",   $fGridSet);
2078                $this->_append($header . $data);
2079        }
2080
2081        /**
2082         * Write the AUTOFILTERINFO BIFF record. This is used to configure the number of autofilter select used in the sheet.
2083         */
2084        private function _writeAutoFilterInfo(){
2085                $record   = 0x009D;                                             // Record identifier
2086                $length   = 0x0002;                                             // Bytes to follow
2087
2088                $rangeBounds = PHPExcel_Cell::rangeBoundaries($this->_phpSheet->getAutoFilter()->getRange());
2089                $iNumFilters = 1 + $rangeBounds[1][0] - $rangeBounds[0][0];
2090
2091                $header   = pack("vv", $record, $length);
2092                $data     = pack("v",  $iNumFilters);
2093                $this->_append($header . $data);
2094        }
2095
2096        /**
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.
2100         *
2101         * @see _writeWsbool()
2102         */
2103        private  function _writeGuts()
2104        {
2105                $record   = 0x0080;   // Record identifier
2106                $length   = 0x0008;   // Bytes to follow
2107
2108                $dxRwGut         = 0x0000;   // Size of row gutter
2109                $dxColGut       = 0x0000;   // Size of col gutter
2110
2111                // determine maximum row outline level
2112                $maxRowOutlineLevel = 0;
2113                foreach ($this->_phpSheet->getRowDimensions() as $rowDimension) {
2114                        $maxRowOutlineLevel = max($maxRowOutlineLevel, $rowDimension->getOutlineLevel());
2115                }
2116
2117                $col_level   = 0;
2118
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);
2124                }
2125
2126                // Set the limits for the outline levels (0 <= x <= 7).
2127                $col_level = max(0, min($col_level, 7));
2128
2129                // The displayed level is one greater than the max outline levels
2130                if ($maxRowOutlineLevel) {
2131                        ++$maxRowOutlineLevel;
2132                }
2133                if ($col_level) {
2134                        ++$col_level;
2135                }
2136
2137                $header   = pack("vv",   $record, $length);
2138                $data           = pack("vvvv", $dxRwGut, $dxColGut, $maxRowOutlineLevel, $col_level);
2139
2140                $this->_append($header.$data);
2141        }
2142
2143        /**
2144         * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction
2145         * with the SETUP record.
2146         */
2147        private function _writeWsbool()
2148        {
2149                $record   = 0x0081;   // Record identifier
2150                $length   = 0x0002;   // Bytes to follow
2151                $grbit     = 0x0000;
2152
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.
2155                //
2156                // Set the option flags
2157                $grbit |= 0x0001;                                                  // Auto page breaks visible
2158                if ($this->_outline_style) {
2159                        $grbit |= 0x0020; // Auto outline styles
2160                }
2161                if ($this->_phpSheet->getShowSummaryBelow()) {
2162                        $grbit |= 0x0040; // Outline summary below
2163                }
2164                if ($this->_phpSheet->getShowSummaryRight()) {
2165                        $grbit |= 0x0080; // Outline summary right
2166                }
2167                if ($this->_phpSheet->getPageSetup()->getFitToPage()) {
2168                        $grbit |= 0x0100; // Page setup fit to page
2169                }
2170                if ($this->_outline_on) {
2171                        $grbit |= 0x0400; // Outline symbols displayed
2172                }
2173
2174                $header   = pack("vv", $record, $length);
2175                $data           = pack("v",  $grbit);
2176                $this->_append($header . $data);
2177        }
2178
2179        /**
2180         * Write the HORIZONTALPAGEBREAKS and VERTICALPAGEBREAKS BIFF records.
2181         */
2182        private function _writeBreaks()
2183        {
2184                // initialize
2185                $vbreaks = array();
2186                $hbreaks = array();
2187
2188                foreach ($this->_phpSheet->getBreaks() as $cell => $breakType) {
2189                        // Fetch coordinates
2190                        $coordinates = PHPExcel_Cell::coordinateFromString($cell);
2191
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;
2198
2199                                case PHPExcel_Worksheet::BREAK_ROW:
2200                                        // Add to list of horizontal breaks
2201                                        $hbreaks[] = $coordinates[1];
2202                                        break;
2203
2204                                case PHPExcel_Worksheet::BREAK_NONE:
2205                                default:
2206                                        // Nothing to do
2207                                        break;
2208                        }
2209                }
2210
2211                //horizontal page breaks
2212                if (!empty($hbreaks)) {
2213
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);
2218                        }
2219
2220                        $record  = 0x001b;                         // Record identifier
2221                        $cbrk   = count($hbreaks);         // Number of page breaks
2222                        $length  = 2 + 6 * $cbrk;         // Bytes to follow
2223
2224                        $header  = pack("vv", $record, $length);
2225                        $data   = pack("v",  $cbrk);
2226
2227                        // Append each page break
2228                        foreach ($hbreaks as $hbreak) {
2229                                $data .= pack("vvv", $hbreak, 0x0000, 0x00ff);
2230                        }
2231
2232                        $this->_append($header . $data);
2233                }
2234
2235                // vertical page breaks
2236                if (!empty($vbreaks)) {
2237
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);
2241
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);
2246                        }
2247
2248                        $record  = 0x001a;                         // Record identifier
2249                        $cbrk   = count($vbreaks);         // Number of page breaks
2250                        $length  = 2 + 6 * $cbrk;         // Bytes to follow
2251
2252                        $header  = pack("vv",  $record, $length);
2253                        $data   = pack("v",   $cbrk);
2254
2255                        // Append each page break
2256                        foreach ($vbreaks as $vbreak) {
2257                                $data .= pack("vvv", $vbreak, 0x0000, 0xffff);
2258                        }
2259
2260                        $this->_append($header . $data);
2261                }
2262        }
2263
2264        /**
2265         * Set the Biff PROTECT record to indicate that the worksheet is protected.
2266         */
2267        private function _writeProtect()
2268        {
2269                // Exit unless sheet protection has been specified
2270                if (!$this->_phpSheet->getProtection()->getSheet()) {
2271                        return;
2272                }
2273
2274                $record   = 0x0012;                      // Record identifier
2275                $length   = 0x0002;                      // Bytes to follow
2276
2277                $fLock    = 1;  // Worksheet is protected
2278
2279                $header   = pack("vv", $record, $length);
2280                $data     = pack("v",  $fLock);
2281
2282                $this->_append($header.$data);
2283        }
2284
2285        /**
2286         * Write SCENPROTECT
2287         */
2288        private function _writeScenProtect()
2289        {
2290                // Exit if sheet protection is not active
2291                if (!$this->_phpSheet->getProtection()->getSheet()) {
2292                        return;
2293                }
2294
2295                // Exit if scenarios are not protected
2296                if (!$this->_phpSheet->getProtection()->getScenarios()) {
2297                        return;
2298                }
2299
2300                $record = 0x00DD; // Record identifier
2301                $length = 0x0002; // Bytes to follow
2302
2303                $header = pack('vv', $record, $length);
2304                $data = pack('v', 1);
2305
2306                $this->_append($header . $data);
2307        }
2308
2309        /**
2310         * Write OBJECTPROTECT
2311         */
2312        private function _writeObjectProtect()
2313        {
2314                // Exit if sheet protection is not active
2315                if (!$this->_phpSheet->getProtection()->getSheet()) {
2316                        return;
2317                }
2318
2319                // Exit if objects are not protected
2320                if (!$this->_phpSheet->getProtection()->getObjects()) {
2321                        return;
2322                }
2323
2324                $record = 0x0063; // Record identifier
2325                $length = 0x0002; // Bytes to follow
2326
2327                $header = pack('vv', $record, $length);
2328                $data = pack('v', 1);
2329
2330                $this->_append($header . $data);
2331        }
2332
2333        /**
2334         * Write the worksheet PASSWORD record.
2335         */
2336        private function _writePassword()
2337        {
2338                // Exit unless sheet protection and password have been specified
2339                if (!$this->_phpSheet->getProtection()->getSheet() || !$this->_phpSheet->getProtection()->getPassword()) {
2340                        return;
2341                }
2342
2343                $record   = 0x0013;                        // Record identifier
2344                $length   = 0x0002;                        // Bytes to follow
2345
2346                $wPassword   = hexdec($this->_phpSheet->getProtection()->getPassword());         // Encoded password
2347
2348                $header   = pack("vv", $record, $length);
2349                $data           = pack("v",  $wPassword);
2350
2351                $this->_append($header . $data);
2352        }
2353
2354        /**
2355         * Insert a 24bit bitmap image in a worksheet.
2356         *
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
2365         */
2366        function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1)
2367        {
2368                $bitmap_array = (is_resource($bitmap) ? $this->_processBitmapGd($bitmap) : $this->_processBitmap($bitmap));
2369                list($width, $height, $size, $data) = $bitmap_array; //$this->_processBitmap($bitmap);
2370
2371                // Scale the frame of the image.
2372                $width  *= $scale_x;
2373                $height *= $scale_y;
2374
2375                // Calculate the vertices of the image and write the OBJ record
2376                $this->_positionImage($col, $row, $x, $y, $width, $height);
2377
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;
2384
2385                $header   = pack("vvvvV", $record, $length, $cf, $env, $lcb);
2386                $this->_append($header.$data);
2387        }
2388
2389        /**
2390         * Calculate the vertices that define the position of the image as required by
2391         * the OBJ record.
2392         *
2393         *               +------------+------------+
2394         *               |       A        |       B      |
2395         *   +-----+------------+------------+
2396         *   |   |(x1,y1)        |                      |
2397         *   |  1  |(A1)._______|______   |
2398         *   |   |      |                         |      |
2399         *   |   |      |                         |      |
2400         *   +-----+----|       BITMAP  |-----+
2401         *   |   |      |                         |      |
2402         *   |  2  |    |______________.         |
2403         *   |   |                      |               (B2)|
2404         *   |   |                      |        (x2,y2)|
2405         *   +---- +------------+------------+
2406         *
2407         * Example of a bitmap that covers some of the area from cell A1 to cell B2.
2408         *
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):
2419         *
2420         *         x1 = X / W *1024
2421         *         y1 = Y / H *256
2422         *         x2 = (X-1) / W *1024
2423         *         y2 = (Y-1) / H *256
2424         *
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.
2431         *
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
2439         */
2440        function _positionImage($col_start, $row_start, $x1, $y1, $width, $height)
2441        {
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
2445
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;
2449                }
2450                if ($y1 >= PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_start + 1)) {
2451                        $y1 = 0;
2452                }
2453
2454                $width    = $width  + $x1 -1;
2455                $height  = $height + $y1 -1;
2456
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;
2461                }
2462
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;
2467                }
2468
2469                // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
2470                // with zero eight or width.
2471                //
2472                if (PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_start)) == 0) {
2473                        return;
2474                }
2475                if (PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end))   == 0) {
2476                        return;
2477                }
2478                if (PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_start + 1) == 0) {
2479                        return;
2480                }
2481                if (PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_end + 1)   == 0) {
2482                        return;
2483                }
2484
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
2490
2491                $this->_writeObjPicture($col_start, $x1,
2492                                                                 $row_start, $y1,
2493                                                                 $col_end, $x2,
2494                                                                 $row_end, $y2);
2495        }
2496
2497        /**
2498         * Store the OBJ record that precedes an IMDATA record. This could be generalise
2499         * to support other Excel objects.
2500         *
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
2509         */
2510        private function _writeObjPicture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB)
2511        {
2512                $record   = 0x005d;   // Record identifier
2513                $length   = 0x003c;   // Bytes to follow
2514
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
2519
2520                $cbMacro         = 0x0000;   // Length of FMLA structure
2521                $Reserved1   = 0x0000;   // Reserved
2522                $Reserved2   = 0x0000;   // Reserved
2523
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
2539
2540
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);
2572
2573                $this->_append($header . $data);
2574        }
2575
2576        /**
2577         * Convert a GD-image into the internal format.
2578         *
2579         * @access private
2580         * @param resource $image The image to process
2581         * @return array Array with data and properties of the bitmap
2582         */
2583        function _processBitmapGd($image) {
2584                $width = imagesx($image);
2585                $height = imagesy($image);
2586
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);
2593                                }
2594                                $data .= chr($color["blue"]) . chr($color["green"]) . chr($color["red"]);
2595                        }
2596                        if (3*$width % 4) {
2597                                $data .= str_repeat("\x00", 4 - 3*$width % 4);
2598                        }
2599                }
2600
2601                return array($width, $height, strlen($data), $data);
2602        }
2603
2604        /**
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.
2608         *
2609         * @access private
2610         * @param string $bitmap The bitmap to process
2611         * @return array Array with data and properties of the bitmap
2612         */
2613        function _processBitmap($bitmap)
2614        {
2615                // Open file.
2616                $bmp_fd = @fopen($bitmap,"rb");
2617                if (!$bmp_fd) {
2618                        throw new PHPExcel_Writer_Exception("Couldn't import $bitmap");
2619                }
2620
2621                // Slurp the file into a string.
2622                $data = fread($bmp_fd, filesize($bitmap));
2623
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");
2627                }
2628
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");
2633                }
2634
2635                // Remove bitmap data: ID.
2636                $data = substr($data, 2);
2637
2638                // Read and remove the bitmap size. This is more reliable than reading
2639                // the data size at offset 0x22.
2640                //
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.
2646
2647                // Remove bitmap data: reserved, offset, header length.
2648                $data = substr($data, 12);
2649
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");
2657                }
2658                if ($height > 0xFFFF) {
2659                        throw new PHPExcel_Writer_Exception("$bitmap: largest image height supported is 65k.\n");
2660                }
2661
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");
2667                }
2668                if ($planes_and_bitcount[1] != 1) {
2669                        throw new PHPExcel_Writer_Exception("$bitmap: only 1 plane supported in bitmap image.\n");
2670                }
2671
2672                // Read and remove the bitmap compression. Verify compression.
2673                $compression = unpack("Vcomp", substr($data, 0, 4));
2674                $data = substr($data, 4);
2675
2676                //$compression = 0;
2677                if ($compression['comp'] != 0) {
2678                        throw new PHPExcel_Writer_Exception("$bitmap: compression not supported in bitmap image.\n");
2679                }
2680
2681                // Remove bitmap data: data size, hres, vres, colours, imp. colours.
2682                $data = substr($data, 20);
2683
2684                // Add the BITMAPCOREHEADER data
2685                $header  = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18);
2686                $data   = $header . $data;
2687
2688                return (array($width, $height, $size, $data));
2689        }
2690
2691        /**
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.
2694         */
2695        private function _writeZoom()
2696        {
2697                // If scale is 100 we don't need to write a record
2698                if ($this->_phpSheet->getSheetView()->getZoomScale() == 100) {
2699                        return;
2700                }
2701
2702                $record   = 0x00A0;                        // Record identifier
2703                $length   = 0x0004;                        // Bytes to follow
2704
2705                $header   = pack("vv", $record, $length);
2706                $data           = pack("vv", $this->_phpSheet->getSheetView()->getZoomScale(), 100);
2707                $this->_append($header . $data);
2708        }
2709
2710        /**
2711         * Get Escher object
2712         *
2713         * @return PHPExcel_Shared_Escher
2714         */
2715        public function getEscher()
2716        {
2717                return $this->_escher;
2718        }
2719
2720        /**
2721         * Set Escher object
2722         *
2723         * @param PHPExcel_Shared_Escher $pValue
2724         */
2725        public function setEscher(PHPExcel_Shared_Escher $pValue = null)
2726        {
2727                $this->_escher = $pValue;
2728        }
2729
2730        /**
2731         * Write MSODRAWING record
2732         */
2733        private function _writeMsoDrawing()
2734        {
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
2742
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
2749
2750                                // chunk of Escher stream for one shape
2751                                $dataChunk = substr($data, $spOffsets[$i -1], $spOffsets[$i] - $spOffsets[$i - 1]);
2752
2753                                $length = strlen($dataChunk);
2754                                $header = pack("vv", $record, $length);
2755
2756                                $this->_append($header . $dataChunk);
2757
2758                                // OBJ record
2759                                $record = 0x005D; // record identifier
2760                                $objData = '';
2761
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
2775                                                );
2776
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');
2783                                }
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
2796                                                );
2797                                }
2798
2799                                // ftEnd
2800                                $objData .=
2801                                        pack('vv'
2802                                                , 0x0000        // 0x0000 = ftEnd
2803                                                , 0x0000        // length of ftEnd data
2804                                        );
2805
2806                                $length = strlen($objData);
2807                                $header = pack('vv', $record, $length);
2808                                $this->_append($header . $objData);
2809                        }
2810                }
2811        }
2812
2813        /**
2814         * Store the DATAVALIDATIONS and DATAVALIDATION records.
2815         */
2816        private function _writeDataValidity()
2817        {
2818                // Datavalidation collection
2819                $dataValidationCollection = $this->_phpSheet->getDataValidationCollection();
2820
2821                // Write data validations?
2822                if (!empty($dataValidationCollection)) {
2823
2824                        // DATAVALIDATIONS record
2825                        $record = 0x01B2;         // Record identifier
2826                        $length   = 0x0012;       // Bytes to follow
2827
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
2832
2833                        $header   = pack('vv', $record, $length);
2834                        $data           = pack('vVVVV', $grbit, $horPos, $verPos, $objId,
2835                                                                                 count($dataValidationCollection));
2836                        $this->_append($header.$data);
2837
2838                        // DATAVALIDATION records
2839                        $record = 0x01BE;                         // Record identifier
2840
2841                        foreach ($dataValidationCollection as $cellCoordinate => $dataValidation) {
2842                                // initialize record data
2843                                $data = '';
2844
2845                                // options
2846                                $options = 0x00000000;
2847
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;
2859                                }
2860                                $options |= $type << 0;
2861
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;
2868                                }
2869                                $options |= $errorStyle << 4;
2870
2871                                // explicit formula?
2872                                if ($type == 0x03 && preg_match('/^\".*\"$/', $dataValidation->getFormula1())) {
2873                                        $options |= 0x01                                << 7;
2874                                }
2875
2876                                // empty cells allowed
2877                                $options |= $dataValidation->getAllowBlank() << 8;
2878
2879                                // show drop down
2880                                $options |= (!$dataValidation->getShowDropDown()) << 9;
2881
2882                                // show input message
2883                                $options |= $dataValidation->getShowInputMessage() << 18;
2884
2885                                // show error message
2886                                $options |= $dataValidation->getShowErrorMessage() << 19;
2887
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;
2899                                }
2900                                $options |= $operator << 20;
2901
2902                                $data           = pack('V', $options);
2903
2904                                // prompt title
2905                                $promptTitle = $dataValidation->getPromptTitle() !== '' ?
2906                                        $dataValidation->getPromptTitle() : chr(0);
2907                                $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($promptTitle);
2908
2909                                // error title
2910                                $errorTitle = $dataValidation->getErrorTitle() !== '' ?
2911                                        $dataValidation->getErrorTitle() : chr(0);
2912                                $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($errorTitle);
2913
2914                                // prompt text
2915                                $prompt = $dataValidation->getPrompt() !== '' ?
2916                                        $dataValidation->getPrompt() : chr(0);
2917                                $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($prompt);
2918
2919                                // error text
2920                                $error = $dataValidation->getError() !== '' ?
2921                                        $dataValidation->getError() : chr(0);
2922                                $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($error);
2923
2924                                // formula 1
2925                                try {
2926                                        $formula1 = $dataValidation->getFormula1();
2927                                        if ($type == 0x03) { // list type
2928                                                $formula1 = str_replace(',', chr(0), $formula1);
2929                                        }
2930                                        $this->_parser->parse($formula1);
2931                                        $formula1 = $this->_parser->toReversePolish();
2932                                        $sz1 = strlen($formula1);
2933
2934                                } catch(PHPExcel_Exception $e) {
2935                                        $sz1 = 0;
2936                                        $formula1 = '';
2937                                }
2938                                $data .= pack('vv', $sz1, 0x0000);
2939                                $data .= $formula1;
2940
2941                                // formula 2
2942                                try {
2943                                        $formula2 = $dataValidation->getFormula2();
2944                                        if ($formula2 === '') {
2945                                                throw new PHPExcel_Writer_Exception('No formula2');
2946                                        }
2947                                        $this->_parser->parse($formula2);
2948                                        $formula2 = $this->_parser->toReversePolish();
2949                                        $sz2 = strlen($formula2);
2950
2951                                } catch(PHPExcel_Exception $e) {
2952                                        $sz2 = 0;
2953                                        $formula2 = '';
2954                                }
2955                                $data .= pack('vv', $sz2, 0x0000);
2956                                $data .= $formula2;
2957
2958                                // cell range address list
2959                                $data .= pack('v', 0x0001);
2960                                $data .= $this->_writeBIFF8CellRangeAddressFixed($cellCoordinate);
2961
2962                                $length = strlen($data);
2963                        $header = pack("vv", $record, $length);
2964
2965                                $this->_append($header . $data);
2966                        }
2967                }
2968        }
2969
2970        /**
2971         * Map Error code
2972         *
2973         * @param string $errorCode
2974         * @return int
2975         */
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;
2985                }
2986
2987                return 0;
2988        }
2989
2990        /**
2991         * Write PLV Record
2992         */
2993        private function _writePageLayoutView(){
2994                $record   = 0x088B;                        // Record identifier
2995                $length   = 0x0010;                        // Bytes to follow
2996
2997                $rt         = 0x088B; // 2
2998                $grbitFrt   = 0x0000; // 2
2999                $reserved   = 0x0000000000000000; // 8
3000                $wScalvePLV = $this->_phpSheet->getSheetView()->getZoomScale(); // 2
3001
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;
3007                }
3008                $fRulerVisible     = 0;
3009                $fWhitespaceHidden = 0;
3010
3011                $grbit      = $fPageLayoutView; // 2
3012                $grbit          |= $fRulerVisible          << 1;
3013                $grbit          |= $fWhitespaceHidden  << 3;
3014
3015                $header   = pack("vv", $record, $length);
3016                $data     = pack("vvVVvv", $rt, $grbitFrt, 0x00000000, 0x00000000, $wScalvePLV, $grbit);
3017                $this->_append($header . $data);
3018        }
3019
3020        /**
3021         * Write CFRule Record
3022         * @param PHPExcel_Style_Conditional $conditional
3023         */
3024        private function _writeCFRule(PHPExcel_Style_Conditional $conditional){
3025                $record   = 0x01B1;                        // Record identifier
3026
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;
3034
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
3061                        }
3062                }
3063
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;
3083                }
3084
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;
3097                }
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;
3105                }
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;
3119                }
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;
3128                }
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;
3142                }
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);
3182
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());
3191                        }
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());
3197                        }
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);
3205                        }
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;
3216                        }
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;
3225                        }
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;
3287                        }
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);
3312                }
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;
3323                        }
3324                        if($conditional->getStyle()->getAlignment()->getWrapText() == true){
3325                                $blockAlign |= 1 << 3;
3326                        } else {
3327                                $blockAlign |= 0 << 3;
3328                        }
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;
3334                        }
3335                        $blockAlign |= 0 << 7;
3336
3337                        // Text rotation angle
3338                        $blockRotation = $conditional->getStyle()->getAlignment()->getTextRotation();
3339
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;
3346                        }
3347                        $blockIndent |= 0 << 6;
3348
3349                        // Relative indentation
3350                        $blockIndentRelative = 255;
3351
3352                        $dataBlockAlign = pack('CCvvv', $blockAlign, $blockRotation, $blockIndent, $blockIndentRelative, 0x0000);
3353                }
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;
3371                        }
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;
3387                        }
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;
3403                        }
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;
3419                        }
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;
3443                        }
3444                        $dataBlockBorder = pack('vv', $blockLineStyle, $blockColor);
3445                }
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;
3472                        }
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;
3532                        }
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;
3592                        }
3593                        $dataBlockFill = pack('v', $blockFillPatternStyle);
3594                        $dataBlockFill .= pack('v', $colorIdxFg | ($colorIdxBg << 7));
3595                }
3596                if($bFormatProt == 1){
3597                        $dataBlockProtection = 0;
3598                        if($conditional->getStyle()->getProtection()->getLocked() == PHPExcel_Style_Protection::PROTECTION_PROTECTED){
3599                                $dataBlockProtection = 1;
3600                        }
3601                        if($conditional->getStyle()->getProtection()->getHidden() == PHPExcel_Style_Protection::PROTECTION_PROTECTED){
3602                                $dataBlockProtection = 1 << 1;
3603                        }
3604                }
3605
3606                $data     = pack('CCvvVv', $type, $operatorType, $szValue1, $szValue2, $flags, 0x0000);
3607                if($bFormatFont == 1){ // Block Formatting : OK
3608                        $data .= $dataBlockFont;
3609                }
3610                if($bFormatAlign == 1){
3611                        $data .= $dataBlockAlign;
3612                }
3613                if($bFormatBorder == 1){
3614                        $data .= $dataBlockBorder;
3615                }
3616                if($bFormatFill == 1){ // Block Formatting : OK
3617                        $data .= $dataBlockFill;
3618                }
3619                if($bFormatProt == 1){
3620                        $data .= $dataBlockProtection;
3621                }
3622                if(!is_null($operand1)){
3623                        $data .= $operand1;
3624                }
3625                if(!is_null($operand2)){
3626                        $data .= $operand2;
3627                }
3628                $header   = pack('vv', $record, strlen($data));
3629                $this->_append($header . $data);
3630        }
3631
3632        /**
3633         * Write CFHeader record
3634         */
3635        private function _writeCFHeader(){
3636                $record   = 0x01B0;                        // Record identifier
3637                $length   = 0x0016;                        // Bytes to follow
3638
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();
3650                                        }
3651                                        // Cells
3652                                        $arrCoord = PHPExcel_Cell::coordinateFromString($cellCoordinate);
3653                                        if(!is_numeric($arrCoord[0])){
3654                                                $arrCoord[0] = PHPExcel_Cell::columnIndexFromString($arrCoord[0]);
3655                                        }
3656                                        if(is_null($numColumnMin) || ($numColumnMin > $arrCoord[0])){
3657                                                $numColumnMin = $arrCoord[0];
3658                                        }
3659                                        if(is_null($numColumnMax) || ($numColumnMax < $arrCoord[0])){
3660                                                $numColumnMax = $arrCoord[0];
3661                                        }
3662                                        if(is_null($numRowMin) || ($numRowMin > $arrCoord[1])){
3663                                                $numRowMin = $arrCoord[1];
3664                                        }
3665                                        if(is_null($numRowMax) || ($numRowMax < $arrCoord[1])){
3666                                                $numRowMax = $arrCoord[1];
3667                                        }
3668                                }
3669                        }
3670                }
3671                $needRedraw = 1;
3672                $cellRange = pack('vvvv', $numRowMin-1, $numRowMax-1, $numColumnMin-1, $numColumnMax-1);
3673
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);
3680        }
3681}
Note: See TracBrowser for help on using the repository browser.