You are here

Worksheet.php in Loft Data Grids 7.2

File

vendor/phpoffice/phpexcel/Classes/PHPExcel/Writer/Excel5/Worksheet.php
View source
<?php

/**
 * PHPExcel
 *
 * Copyright (c) 2006 - 2014 PHPExcel
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category   PHPExcel
 * @package    PHPExcel_Writer_Excel5
 * @copyright  Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt	LGPL
 * @version    ##VERSION##, ##DATE##
 */

// Original file header of PEAR::Spreadsheet_Excel_Writer_Worksheet (used as the base for this class):
// -----------------------------------------------------------------------------------------
// /*
// *  Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
// *
// *  The majority of this is _NOT_ my code.  I simply ported it from the
// *  PERL Spreadsheet::WriteExcel module.
// *
// *  The author of the Spreadsheet::WriteExcel module is John McNamara
// *  <jmcnamara@cpan.org>
// *
// *  I _DO_ maintain this code, and John McNamara has nothing to do with the
// *  porting of this code to PHP.  Any questions directly related to this
// *  class library should be directed to me.
// *
// *  License Information:
// *
// *    Spreadsheet_Excel_Writer:  A library for generating Excel Spreadsheets
// *    Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
// *
// *    This library is free software; you can redistribute it and/or
// *    modify it under the terms of the GNU Lesser General Public
// *    License as published by the Free Software Foundation; either
// *    version 2.1 of the License, or (at your option) any later version.
// *
// *    This library is distributed in the hope that it will be useful,
// *    but WITHOUT ANY WARRANTY; without even the implied warranty of
// *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// *    Lesser General Public License for more details.
// *
// *    You should have received a copy of the GNU Lesser General Public
// *    License along with this library; if not, write to the Free Software
// *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// */

/**
 * PHPExcel_Writer_Excel5_Worksheet
 *
 * @category   PHPExcel
 * @package    PHPExcel_Writer_Excel5
 * @copyright  Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
 */
class PHPExcel_Writer_Excel5_Worksheet extends PHPExcel_Writer_Excel5_BIFFwriter {

  /**
   * Formula parser
   *
   * @var PHPExcel_Writer_Excel5_Parser
   */
  private $_parser;

  /**
   * Maximum number of characters for a string (LABEL record in BIFF5)
   * @var integer
   */
  public $_xls_strmax;

  /**
   * Array containing format information for columns
   * @var array
   */
  public $_colinfo;

  /**
   * Array containing the selected area for the worksheet
   * @var array
   */
  public $_selection;

  /**
   * The active pane for the worksheet
   * @var integer
   */
  public $_active_pane;

  /**
   * Whether to use outline.
   * @var integer
   */
  public $_outline_on;

  /**
   * Auto outline styles.
   * @var bool
   */
  public $_outline_style;

  /**
   * Whether to have outline summary below.
   * @var bool
   */
  public $_outline_below;

  /**
   * Whether to have outline summary at the right.
   * @var bool
   */
  public $_outline_right;

  /**
   * Reference to the total number of strings in the workbook
   * @var integer
   */
  public $_str_total;

  /**
   * Reference to the number of unique strings in the workbook
   * @var integer
   */
  public $_str_unique;

  /**
   * Reference to the array containing all the unique strings in the workbook
   * @var array
   */
  public $_str_table;

  /**
   * Color cache
   */
  private $_colors;

  /**
   * Index of first used row (at least 0)
   * @var int
   */
  private $_firstRowIndex;

  /**
   * Index of last used row. (no used rows means -1)
   * @var int
   */
  private $_lastRowIndex;

  /**
   * Index of first used column (at least 0)
   * @var int
   */
  private $_firstColumnIndex;

  /**
   * Index of last used column (no used columns means -1)
   * @var int
   */
  private $_lastColumnIndex;

  /**
   * Sheet object
   * @var PHPExcel_Worksheet
   */
  public $_phpSheet;

  /**
   * Count cell style Xfs
   *
   * @var int
   */
  private $_countCellStyleXfs;

  /**
   * Escher object corresponding to MSODRAWING
   *
   * @var PHPExcel_Shared_Escher
   */
  private $_escher;

  /**
   * Array of font hashes associated to FONT records index
   *
   * @var array
   */
  public $_fntHashIndex;

  /**
   * Constructor
   *
   * @param int		&$str_total		Total number of strings
   * @param int		&$str_unique	Total number of unique strings
   * @param array		&$str_table		String Table
   * @param array		&$colors		Colour Table
   * @param mixed		$parser			The formula parser created for the Workbook
   * @param boolean	$preCalculateFormulas	Flag indicating whether formulas should be calculated or just written
   * @param string	$phpSheet		The worksheet to write
   * @param PHPExcel_Worksheet $phpSheet
   */
  public function __construct(&$str_total, &$str_unique, &$str_table, &$colors, $parser, $preCalculateFormulas, $phpSheet) {

    // It needs to call its parent's constructor explicitly
    parent::__construct();

    // change BIFFwriter limit for CONTINUE records
    //		$this->_limit = 8224;
    $this->_preCalculateFormulas = $preCalculateFormulas;
    $this->_str_total =& $str_total;
    $this->_str_unique =& $str_unique;
    $this->_str_table =& $str_table;
    $this->_colors =& $colors;
    $this->_parser = $parser;
    $this->_phpSheet = $phpSheet;

    //$this->ext_sheets		= array();

    //$this->offset			= 0;
    $this->_xls_strmax = 255;
    $this->_colinfo = array();
    $this->_selection = array(
      0,
      0,
      0,
      0,
    );
    $this->_active_pane = 3;
    $this->_print_headers = 0;
    $this->_outline_style = 0;
    $this->_outline_below = 1;
    $this->_outline_right = 1;
    $this->_outline_on = 1;
    $this->_fntHashIndex = array();

    // calculate values for DIMENSIONS record
    $minR = 1;
    $minC = 'A';
    $maxR = $this->_phpSheet
      ->getHighestRow();
    $maxC = $this->_phpSheet
      ->getHighestColumn();

    // Determine lowest and highest column and row
    //		$this->_firstRowIndex = ($minR > 65535) ? 65535 : $minR;
    $this->_lastRowIndex = $maxR > 65535 ? 65535 : $maxR;
    $this->_firstColumnIndex = PHPExcel_Cell::columnIndexFromString($minC);
    $this->_lastColumnIndex = PHPExcel_Cell::columnIndexFromString($maxC);

    //		if ($this->_firstColumnIndex > 255) $this->_firstColumnIndex = 255;
    if ($this->_lastColumnIndex > 255) {
      $this->_lastColumnIndex = 255;
    }
    $this->_countCellStyleXfs = count($phpSheet
      ->getParent()
      ->getCellStyleXfCollection());
  }

  /**
   * Add data to the beginning of the workbook (note the reverse order)
   * and to the end of the workbook.
   *
   * @access public
   * @see PHPExcel_Writer_Excel5_Workbook::storeWorkbook()
   */
  function close() {
    $_phpSheet = $this->_phpSheet;
    $num_sheets = $_phpSheet
      ->getParent()
      ->getSheetCount();

    // Write BOF record
    $this
      ->_storeBof(0x10);

    // Write PRINTHEADERS
    $this
      ->_writePrintHeaders();

    // Write PRINTGRIDLINES
    $this
      ->_writePrintGridlines();

    // Write GRIDSET
    $this
      ->_writeGridset();

    // Calculate column widths
    $_phpSheet
      ->calculateColumnWidths();

    // Column dimensions
    if (($defaultWidth = $_phpSheet
      ->getDefaultColumnDimension()
      ->getWidth()) < 0) {
      $defaultWidth = PHPExcel_Shared_Font::getDefaultColumnWidthByFont($_phpSheet
        ->getParent()
        ->getDefaultStyle()
        ->getFont());
    }
    $columnDimensions = $_phpSheet
      ->getColumnDimensions();
    $maxCol = $this->_lastColumnIndex - 1;
    for ($i = 0; $i <= $maxCol; ++$i) {
      $hidden = 0;
      $level = 0;
      $xfIndex = 15;

      // there are 15 cell style Xfs
      $width = $defaultWidth;
      $columnLetter = PHPExcel_Cell::stringFromColumnIndex($i);
      if (isset($columnDimensions[$columnLetter])) {
        $columnDimension = $columnDimensions[$columnLetter];
        if ($columnDimension
          ->getWidth() >= 0) {
          $width = $columnDimension
            ->getWidth();
        }
        $hidden = $columnDimension
          ->getVisible() ? 0 : 1;
        $level = $columnDimension
          ->getOutlineLevel();
        $xfIndex = $columnDimension
          ->getXfIndex() + 15;

        // there are 15 cell style Xfs
      }

      // Components of _colinfo:
      // $firstcol first column on the range
      // $lastcol  last column on the range
      // $width	width to set
      // $xfIndex  The optional cell style Xf index to apply to the columns
      // $hidden   The optional hidden atribute
      // $level	The optional outline level
      $this->_colinfo[] = array(
        $i,
        $i,
        $width,
        $xfIndex,
        $hidden,
        $level,
      );
    }

    // Write GUTS
    $this
      ->_writeGuts();

    // Write DEFAULTROWHEIGHT
    $this
      ->_writeDefaultRowHeight();

    // Write WSBOOL
    $this
      ->_writeWsbool();

    // Write horizontal and vertical page breaks
    $this
      ->_writeBreaks();

    // Write page header
    $this
      ->_writeHeader();

    // Write page footer
    $this
      ->_writeFooter();

    // Write page horizontal centering
    $this
      ->_writeHcenter();

    // Write page vertical centering
    $this
      ->_writeVcenter();

    // Write left margin
    $this
      ->_writeMarginLeft();

    // Write right margin
    $this
      ->_writeMarginRight();

    // Write top margin
    $this
      ->_writeMarginTop();

    // Write bottom margin
    $this
      ->_writeMarginBottom();

    // Write page setup
    $this
      ->_writeSetup();

    // Write sheet protection
    $this
      ->_writeProtect();

    // Write SCENPROTECT
    $this
      ->_writeScenProtect();

    // Write OBJECTPROTECT
    $this
      ->_writeObjectProtect();

    // Write sheet password
    $this
      ->_writePassword();

    // Write DEFCOLWIDTH record
    $this
      ->_writeDefcol();

    // Write the COLINFO records if they exist
    if (!empty($this->_colinfo)) {
      $colcount = count($this->_colinfo);
      for ($i = 0; $i < $colcount; ++$i) {
        $this
          ->_writeColinfo($this->_colinfo[$i]);
      }
    }
    $autoFilterRange = $_phpSheet
      ->getAutoFilter()
      ->getRange();
    if (!empty($autoFilterRange)) {

      // Write AUTOFILTERINFO
      $this
        ->_writeAutoFilterInfo();
    }

    // Write sheet dimensions
    $this
      ->_writeDimensions();

    // Row dimensions
    foreach ($_phpSheet
      ->getRowDimensions() as $rowDimension) {
      $xfIndex = $rowDimension
        ->getXfIndex() + 15;

      // there are 15 cellXfs
      $this
        ->_writeRow($rowDimension
        ->getRowIndex() - 1, $rowDimension
        ->getRowHeight(), $xfIndex, $rowDimension
        ->getVisible() ? '0' : '1', $rowDimension
        ->getOutlineLevel());
    }

    // Write Cells
    foreach ($_phpSheet
      ->getCellCollection() as $cellID) {
      $cell = $_phpSheet
        ->getCell($cellID);
      $row = $cell
        ->getRow() - 1;
      $column = PHPExcel_Cell::columnIndexFromString($cell
        ->getColumn()) - 1;

      // Don't break Excel!
      //			if ($row + 1 > 65536 or $column + 1 > 256) {
      if ($row > 65535 || $column > 255) {
        break;
      }

      // Write cell value
      $xfIndex = $cell
        ->getXfIndex() + 15;

      // there are 15 cell style Xfs
      $cVal = $cell
        ->getValue();
      if ($cVal instanceof PHPExcel_RichText) {

        // $this->_writeString($row, $column, $cVal->getPlainText(), $xfIndex);
        $arrcRun = array();
        $str_len = PHPExcel_Shared_String::CountCharacters($cVal
          ->getPlainText(), 'UTF-8');
        $str_pos = 0;
        $elements = $cVal
          ->getRichTextElements();
        foreach ($elements as $element) {

          // FONT Index
          if ($element instanceof PHPExcel_RichText_Run) {
            $str_fontidx = $this->_fntHashIndex[$element
              ->getFont()
              ->getHashCode()];
          }
          else {
            $str_fontidx = 0;
          }
          $arrcRun[] = array(
            'strlen' => $str_pos,
            'fontidx' => $str_fontidx,
          );

          // Position FROM
          $str_pos += PHPExcel_Shared_String::CountCharacters($element
            ->getText(), 'UTF-8');
        }
        $this
          ->_writeRichTextString($row, $column, $cVal
          ->getPlainText(), $xfIndex, $arrcRun);
      }
      else {
        switch ($cell
          ->getDatatype()) {
          case PHPExcel_Cell_DataType::TYPE_STRING:
          case PHPExcel_Cell_DataType::TYPE_NULL:
            if ($cVal === '' || $cVal === null) {
              $this
                ->_writeBlank($row, $column, $xfIndex);
            }
            else {
              $this
                ->_writeString($row, $column, $cVal, $xfIndex);
            }
            break;
          case PHPExcel_Cell_DataType::TYPE_NUMERIC:
            $this
              ->_writeNumber($row, $column, $cVal, $xfIndex);
            break;
          case PHPExcel_Cell_DataType::TYPE_FORMULA:
            $calculatedValue = $this->_preCalculateFormulas ? $cell
              ->getCalculatedValue() : null;
            $this
              ->_writeFormula($row, $column, $cVal, $xfIndex, $calculatedValue);
            break;
          case PHPExcel_Cell_DataType::TYPE_BOOL:
            $this
              ->_writeBoolErr($row, $column, $cVal, 0, $xfIndex);
            break;
          case PHPExcel_Cell_DataType::TYPE_ERROR:
            $this
              ->_writeBoolErr($row, $column, self::_mapErrorCode($cVal), 1, $xfIndex);
            break;
        }
      }
    }

    // Append
    $this
      ->_writeMsoDrawing();

    // Write WINDOW2 record
    $this
      ->_writeWindow2();

    // Write PLV record
    $this
      ->_writePageLayoutView();

    // Write ZOOM record
    $this
      ->_writeZoom();
    if ($_phpSheet
      ->getFreezePane()) {
      $this
        ->_writePanes();
    }

    // Write SELECTION record
    $this
      ->_writeSelection();

    // Write MergedCellsTable Record
    $this
      ->_writeMergedCells();

    // Hyperlinks
    foreach ($_phpSheet
      ->getHyperLinkCollection() as $coordinate => $hyperlink) {
      list($column, $row) = PHPExcel_Cell::coordinateFromString($coordinate);
      $url = $hyperlink
        ->getUrl();
      if (strpos($url, 'sheet://') !== false) {

        // internal to current workbook
        $url = str_replace('sheet://', 'internal:', $url);
      }
      else {
        if (preg_match('/^(http:|https:|ftp:|mailto:)/', $url)) {

          // URL
          // $url = $url;
        }
        else {

          // external (local file)
          $url = 'external:' . $url;
        }
      }
      $this
        ->_writeUrl($row - 1, PHPExcel_Cell::columnIndexFromString($column) - 1, $url);
    }
    $this
      ->_writeDataValidity();
    $this
      ->_writeSheetLayout();

    // Write SHEETPROTECTION record
    $this
      ->_writeSheetProtection();
    $this
      ->_writeRangeProtection();
    $arrConditionalStyles = $_phpSheet
      ->getConditionalStylesCollection();
    if (!empty($arrConditionalStyles)) {
      $arrConditional = array();

      // @todo CFRule & CFHeader
      // Write CFHEADER record
      $this
        ->_writeCFHeader();

      // Write ConditionalFormattingTable records
      foreach ($arrConditionalStyles as $cellCoordinate => $conditionalStyles) {
        foreach ($conditionalStyles as $conditional) {
          if ($conditional
            ->getConditionType() == PHPExcel_Style_Conditional::CONDITION_EXPRESSION || $conditional
            ->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CELLIS) {
            if (!in_array($conditional
              ->getHashCode(), $arrConditional)) {
              $arrConditional[] = $conditional
                ->getHashCode();

              // Write CFRULE record
              $this
                ->_writeCFRule($conditional);
            }
          }
        }
      }
    }
    $this
      ->_storeEof();
  }

  /**
   * Write a cell range address in BIFF8
   * always fixed range
   * See section 2.5.14 in OpenOffice.org's Documentation of the Microsoft Excel File Format
   *
   * @param string $range E.g. 'A1' or 'A1:B6'
   * @return string Binary data
   */
  private function _writeBIFF8CellRangeAddressFixed($range = 'A1') {
    $explodes = explode(':', $range);

    // extract first cell, e.g. 'A1'
    $firstCell = $explodes[0];

    // extract last cell, e.g. 'B6'
    if (count($explodes) == 1) {
      $lastCell = $firstCell;
    }
    else {
      $lastCell = $explodes[1];
    }
    $firstCellCoordinates = PHPExcel_Cell::coordinateFromString($firstCell);

    // e.g. array(0, 1)
    $lastCellCoordinates = PHPExcel_Cell::coordinateFromString($lastCell);

    // e.g. array(1, 6)
    return pack('vvvv', $firstCellCoordinates[1] - 1, $lastCellCoordinates[1] - 1, PHPExcel_Cell::columnIndexFromString($firstCellCoordinates[0]) - 1, PHPExcel_Cell::columnIndexFromString($lastCellCoordinates[0]) - 1);
  }

  /**
   * Retrieves data from memory in one chunk, or from disk in $buffer
   * sized chunks.
   *
   * @return string The data
   */
  function getData() {
    $buffer = 4096;

    // Return data stored in memory
    if (isset($this->_data)) {
      $tmp = $this->_data;
      unset($this->_data);
      return $tmp;
    }

    // No data to return
    return false;
  }

  /**
   * Set the option to print the row and column headers on the printed page.
   *
   * @access public
   * @param integer $print Whether to print the headers or not. Defaults to 1 (print).
   */
  function printRowColHeaders($print = 1) {
    $this->_print_headers = $print;
  }

  /**
   * This method sets the properties for outlining and grouping. The defaults
   * correspond to Excel's defaults.
   *
   * @param bool $visible
   * @param bool $symbols_below
   * @param bool $symbols_right
   * @param bool $auto_style
   */
  function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false) {
    $this->_outline_on = $visible;
    $this->_outline_below = $symbols_below;
    $this->_outline_right = $symbols_right;
    $this->_outline_style = $auto_style;

    // Ensure this is a boolean vale for Window2
    if ($this->_outline_on) {
      $this->_outline_on = 1;
    }
  }

  /**
   * Write a double to the specified row and column (zero indexed).
   * An integer can be written as a double. Excel will display an
   * integer. $format is optional.
   *
   * Returns  0 : normal termination
   *		 -2 : row or column out of range
   *
   * @param integer $row	Zero indexed row
   * @param integer $col	Zero indexed column
   * @param float   $num	The number to write
   * @param mixed   $xfIndex The optional XF format
   * @return integer
   */
  private function _writeNumber($row, $col, $num, $xfIndex) {
    $record = 0x203;

    // Record identifier
    $length = 0xe;

    // Number of bytes to follow
    $header = pack("vv", $record, $length);
    $data = pack("vvv", $row, $col, $xfIndex);
    $xl_double = pack("d", $num);
    if (self::getByteOrder()) {

      // if it's Big Endian
      $xl_double = strrev($xl_double);
    }
    $this
      ->_append($header . $data . $xl_double);
    return 0;
  }

  /**
   * Write a LABELSST record or a LABEL record. Which one depends on BIFF version
   *
   * @param int $row Row index (0-based)
   * @param int $col Column index (0-based)
   * @param string $str The string
   * @param int $xfIndex Index to XF record
   */
  private function _writeString($row, $col, $str, $xfIndex) {
    $this
      ->_writeLabelSst($row, $col, $str, $xfIndex);
  }

  /**
   * Write a LABELSST record or a LABEL record. Which one depends on BIFF version
   * It differs from _writeString by the writing of rich text strings.
   * @param int $row Row index (0-based)
   * @param int $col Column index (0-based)
   * @param string $str The string
   * @param mixed   $xfIndex The XF format index for the cell
   * @param array $arrcRun Index to Font record and characters beginning
   */
  private function _writeRichTextString($row, $col, $str, $xfIndex, $arrcRun) {
    $record = 0xfd;

    // Record identifier
    $length = 0xa;

    // Bytes to follow
    $str = PHPExcel_Shared_String::UTF8toBIFF8UnicodeShort($str, $arrcRun);

    /* check if string is already present */
    if (!isset($this->_str_table[$str])) {
      $this->_str_table[$str] = $this->_str_unique++;
    }
    $this->_str_total++;
    $header = pack('vv', $record, $length);
    $data = pack('vvvV', $row, $col, $xfIndex, $this->_str_table[$str]);
    $this
      ->_append($header . $data);
  }

  /**
   * Write a string to the specified row and column (zero indexed).
   * NOTE: there is an Excel 5 defined limit of 255 characters.
   * $format is optional.
   * Returns  0 : normal termination
   *		 -2 : row or column out of range
   *		 -3 : long string truncated to 255 chars
   *
   * @access public
   * @param integer $row	Zero indexed row
   * @param integer $col	Zero indexed column
   * @param string  $str	The string to write
   * @param mixed   $xfIndex The XF format index for the cell
   * @return integer
   */
  private function _writeLabel($row, $col, $str, $xfIndex) {
    $strlen = strlen($str);
    $record = 0x204;

    // Record identifier
    $length = 0x8 + $strlen;

    // Bytes to follow
    $str_error = 0;
    if ($strlen > $this->_xls_strmax) {

      // LABEL must be < 255 chars
      $str = substr($str, 0, $this->_xls_strmax);
      $length = 0x8 + $this->_xls_strmax;
      $strlen = $this->_xls_strmax;
      $str_error = -3;
    }
    $header = pack("vv", $record, $length);
    $data = pack("vvvv", $row, $col, $xfIndex, $strlen);
    $this
      ->_append($header . $data . $str);
    return $str_error;
  }

  /**
   * Write a string to the specified row and column (zero indexed).
   * This is the BIFF8 version (no 255 chars limit).
   * $format is optional.
   * Returns  0 : normal termination
   *		 -2 : row or column out of range
   *		 -3 : long string truncated to 255 chars
   *
   * @access public
   * @param integer $row	Zero indexed row
   * @param integer $col	Zero indexed column
   * @param string  $str	The string to write
   * @param mixed   $xfIndex The XF format index for the cell
   * @return integer
   */
  private function _writeLabelSst($row, $col, $str, $xfIndex) {
    $record = 0xfd;

    // Record identifier
    $length = 0xa;

    // Bytes to follow
    $str = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($str);

    /* check if string is already present */
    if (!isset($this->_str_table[$str])) {
      $this->_str_table[$str] = $this->_str_unique++;
    }
    $this->_str_total++;
    $header = pack('vv', $record, $length);
    $data = pack('vvvV', $row, $col, $xfIndex, $this->_str_table[$str]);
    $this
      ->_append($header . $data);
  }

  /**
   * Writes a note associated with the cell given by the row and column.
   * NOTE records don't have a length limit.
   *
   * @param integer $row	Zero indexed row
   * @param integer $col	Zero indexed column
   * @param string  $note   The note to write
   */
  private function _writeNote($row, $col, $note) {
    $note_length = strlen($note);
    $record = 0x1c;

    // Record identifier
    $max_length = 2048;

    // Maximun length for a NOTE record
    // Length for this record is no more than 2048 + 6
    $length = 0x6 + min($note_length, 2048);
    $header = pack("vv", $record, $length);
    $data = pack("vvv", $row, $col, $note_length);
    $this
      ->_append($header . $data . substr($note, 0, 2048));
    for ($i = $max_length; $i < $note_length; $i += $max_length) {
      $chunk = substr($note, $i, $max_length);
      $length = 0x6 + strlen($chunk);
      $header = pack("vv", $record, $length);
      $data = pack("vvv", -1, 0, strlen($chunk));
      $this
        ->_append($header . $data . $chunk);
    }
    return 0;
  }

  /**
   * Write a blank cell to the specified row and column (zero indexed).
   * A blank cell is used to specify formatting without adding a string
   * or a number.
   *
   * A blank cell without a format serves no purpose. Therefore, we don't write
   * a BLANK record unless a format is specified.
   *
   * Returns  0 : normal termination (including no format)
   *		 -1 : insufficient number of arguments
   *		 -2 : row or column out of range
   *
   * @param integer $row	Zero indexed row
   * @param integer $col	Zero indexed column
   * @param mixed   $xfIndex The XF format index
   */
  function _writeBlank($row, $col, $xfIndex) {
    $record = 0x201;

    // Record identifier
    $length = 0x6;

    // Number of bytes to follow
    $header = pack("vv", $record, $length);
    $data = pack("vvv", $row, $col, $xfIndex);
    $this
      ->_append($header . $data);
    return 0;
  }

  /**
   * Write a boolean or an error type to the specified row and column (zero indexed)
   *
   * @param int $row Row index (0-based)
   * @param int $col Column index (0-based)
   * @param int $value
   * @param boolean $isError Error or Boolean?
   * @param int $xfIndex
   */
  private function _writeBoolErr($row, $col, $value, $isError, $xfIndex) {
    $record = 0x205;
    $length = 8;
    $header = pack("vv", $record, $length);
    $data = pack("vvvCC", $row, $col, $xfIndex, $value, $isError);
    $this
      ->_append($header . $data);
    return 0;
  }

  /**
   * Write a formula to the specified row and column (zero indexed).
   * The textual representation of the formula is passed to the parser in
   * Parser.php which returns a packed binary string.
   *
   * Returns  0 : normal termination
   *		 -1 : formula errors (bad formula)
   *		 -2 : row or column out of range
   *
   * @param integer $row	 Zero indexed row
   * @param integer $col	 Zero indexed column
   * @param string  $formula The formula text string
   * @param mixed   $xfIndex  The XF format index
   * @param mixed   $calculatedValue  Calculated value
   * @return integer
   */
  private function _writeFormula($row, $col, $formula, $xfIndex, $calculatedValue) {
    $record = 0x6;

    // Record identifier
    // Initialize possible additional value for STRING record that should be written after the FORMULA record?
    $stringValue = null;

    // calculated value
    if (isset($calculatedValue)) {

      // Since we can't yet get the data type of the calculated value,
      // we use best effort to determine data type
      if (is_bool($calculatedValue)) {

        // Boolean value
        $num = pack('CCCvCv', 0x1, 0x0, (int) $calculatedValue, 0x0, 0x0, 0xffff);
      }
      elseif (is_int($calculatedValue) || is_float($calculatedValue)) {

        // Numeric value
        $num = pack('d', $calculatedValue);
      }
      elseif (is_string($calculatedValue)) {
        if (array_key_exists($calculatedValue, PHPExcel_Cell_DataType::getErrorCodes())) {

          // Error value
          $num = pack('CCCvCv', 0x2, 0x0, self::_mapErrorCode($calculatedValue), 0x0, 0x0, 0xffff);
        }
        elseif ($calculatedValue === '') {

          // Empty string (and BIFF8)
          $num = pack('CCCvCv', 0x3, 0x0, 0x0, 0x0, 0x0, 0xffff);
        }
        else {

          // Non-empty string value (or empty string BIFF5)
          $stringValue = $calculatedValue;
          $num = pack('CCCvCv', 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff);
        }
      }
      else {

        // We are really not supposed to reach here
        $num = pack('d', 0x0);
      }
    }
    else {
      $num = pack('d', 0x0);
    }
    $grbit = 0x3;

    // Option flags
    $unknown = 0x0;

    // Must be zero
    // Strip the '=' or '@' sign at the beginning of the formula string
    if ($formula[0] == '=') {
      $formula = substr($formula, 1);
    }
    else {

      // Error handling
      $this
        ->_writeString($row, $col, 'Unrecognised character for formula');
      return -1;
    }

    // Parse the formula using the parser in Parser.php
    try {
      $error = $this->_parser
        ->parse($formula);
      $formula = $this->_parser
        ->toReversePolish();
      $formlen = strlen($formula);

      // Length of the binary string
      $length = 0x16 + $formlen;

      // Length of the record data
      $header = pack("vv", $record, $length);
      $data = pack("vvv", $row, $col, $xfIndex) . $num . pack("vVv", $grbit, $unknown, $formlen);
      $this
        ->_append($header . $data . $formula);

      // Append also a STRING record if necessary
      if ($stringValue !== null) {
        $this
          ->_writeStringRecord($stringValue);
      }
      return 0;
    } catch (PHPExcel_Exception $e) {

      // do nothing
    }
  }

  /**
   * Write a STRING record. This
   *
   * @param string $stringValue
   */
  private function _writeStringRecord($stringValue) {
    $record = 0x207;

    // Record identifier
    $data = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($stringValue);
    $length = strlen($data);
    $header = pack('vv', $record, $length);
    $this
      ->_append($header . $data);
  }

  /**
   * Write a hyperlink.
   * This is comprised of two elements: the visible label and
   * the invisible link. The visible label is the same as the link unless an
   * alternative string is specified. The label is written using the
   * _writeString() method. Therefore the 255 characters string limit applies.
   * $string and $format are optional.
   *
   * The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external
   * directory url.
   *
   * Returns  0 : normal termination
   *		 -2 : row or column out of range
   *		 -3 : long string truncated to 255 chars
   *
   * @param integer $row	Row
   * @param integer $col	Column
   * @param string  $url	URL string
   * @return integer
   */
  private function _writeUrl($row, $col, $url) {

    // Add start row and col to arg list
    return $this
      ->_writeUrlRange($row, $col, $row, $col, $url);
  }

  /**
   * This is the more general form of _writeUrl(). It allows a hyperlink to be
   * written to a range of cells. This function also decides the type of hyperlink
   * to be written. These are either, Web (http, ftp, mailto), Internal
   * (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1').
   *
   * @access private
   * @see _writeUrl()
   * @param integer $row1   Start row
   * @param integer $col1   Start column
   * @param integer $row2   End row
   * @param integer $col2   End column
   * @param string  $url	URL string
   * @return integer
   */
  function _writeUrlRange($row1, $col1, $row2, $col2, $url) {

    // Check for internal/external sheet links or default to web link
    if (preg_match('[^internal:]', $url)) {
      return $this
        ->_writeUrlInternal($row1, $col1, $row2, $col2, $url);
    }
    if (preg_match('[^external:]', $url)) {
      return $this
        ->_writeUrlExternal($row1, $col1, $row2, $col2, $url);
    }
    return $this
      ->_writeUrlWeb($row1, $col1, $row2, $col2, $url);
  }

  /**
   * Used to write http, ftp and mailto hyperlinks.
   * The link type ($options) is 0x03 is the same as absolute dir ref without
   * sheet. However it is differentiated by the $unknown2 data stream.
   *
   * @access private
   * @see _writeUrl()
   * @param integer $row1   Start row
   * @param integer $col1   Start column
   * @param integer $row2   End row
   * @param integer $col2   End column
   * @param string  $url	URL string
   * @return integer
   */
  function _writeUrlWeb($row1, $col1, $row2, $col2, $url) {
    $record = 0x1b8;

    // Record identifier
    $length = 0x0;

    // Bytes to follow
    // Pack the undocumented parts of the hyperlink stream
    $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
    $unknown2 = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B");

    // Pack the option flags
    $options = pack("V", 0x3);

    // Convert URL to a null terminated wchar string
    $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
    $url = $url . "\0\0\0";

    // Pack the length of the URL
    $url_len = pack("V", strlen($url));

    // Calculate the data length
    $length = 0x34 + strlen($url);

    // Pack the header data
    $header = pack("vv", $record, $length);
    $data = pack("vvvv", $row1, $row2, $col1, $col2);

    // Write the packed data
    $this
      ->_append($header . $data . $unknown1 . $options . $unknown2 . $url_len . $url);
    return 0;
  }

  /**
   * Used to write internal reference hyperlinks such as "Sheet1!A1".
   *
   * @access private
   * @see _writeUrl()
   * @param integer $row1   Start row
   * @param integer $col1   Start column
   * @param integer $row2   End row
   * @param integer $col2   End column
   * @param string  $url	URL string
   * @return integer
   */
  function _writeUrlInternal($row1, $col1, $row2, $col2, $url) {
    $record = 0x1b8;

    // Record identifier
    $length = 0x0;

    // Bytes to follow
    // Strip URL type
    $url = preg_replace('/^internal:/', '', $url);

    // Pack the undocumented parts of the hyperlink stream
    $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");

    // Pack the option flags
    $options = pack("V", 0x8);

    // Convert the URL type and to a null terminated wchar string
    $url .= "\0";

    // character count
    $url_len = PHPExcel_Shared_String::CountCharacters($url);
    $url_len = pack('V', $url_len);
    $url = PHPExcel_Shared_String::ConvertEncoding($url, 'UTF-16LE', 'UTF-8');

    // Calculate the data length
    $length = 0x24 + strlen($url);

    // Pack the header data
    $header = pack("vv", $record, $length);
    $data = pack("vvvv", $row1, $row2, $col1, $col2);

    // Write the packed data
    $this
      ->_append($header . $data . $unknown1 . $options . $url_len . $url);
    return 0;
  }

  /**
   * Write links to external directory names such as 'c:\foo.xls',
   * c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'.
   *
   * Note: Excel writes some relative links with the $dir_long string. We ignore
   * these cases for the sake of simpler code.
   *
   * @access private
   * @see _writeUrl()
   * @param integer $row1   Start row
   * @param integer $col1   Start column
   * @param integer $row2   End row
   * @param integer $col2   End column
   * @param string  $url	URL string
   * @return integer
   */
  function _writeUrlExternal($row1, $col1, $row2, $col2, $url) {

    // Network drives are different. We will handle them separately
    // MS/Novell network drives and shares start with \\
    if (preg_match('[^external:\\\\]', $url)) {
      return;

      //($this->_writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format));
    }
    $record = 0x1b8;

    // Record identifier
    $length = 0x0;

    // Bytes to follow
    // Strip URL type and change Unix dir separator to Dos style (if needed)
    //
    $url = preg_replace('/^external:/', '', $url);
    $url = preg_replace('/\\//', "\\", $url);

    // Determine if the link is relative or absolute:
    //   relative if link contains no dir separator, "somefile.xls"
    //   relative if link starts with up-dir, "..\..\somefile.xls"
    //   otherwise, absolute
    $absolute = 0x0;

    // relative path
    if (preg_match('/^[A-Z]:/', $url)) {
      $absolute = 0x2;

      // absolute path on Windows, e.g. C:\...
    }
    $link_type = 0x1 | $absolute;

    // Determine if the link contains a sheet reference and change some of the
    // parameters accordingly.
    // Split the dir name and sheet name (if it exists)
    $dir_long = $url;
    if (preg_match("/\\#/", $url)) {
      $link_type |= 0x8;
    }

    // Pack the link type
    $link_type = pack("V", $link_type);

    // Calculate the up-level dir count e.g.. (..\..\..\ == 3)
    $up_count = preg_match_all("/\\.\\.\\\\/", $dir_long, $useless);
    $up_count = pack("v", $up_count);

    // Store the short dos dir name (null terminated)
    $dir_short = preg_replace("/\\.\\.\\\\/", '', $dir_long) . "\0";

    // Store the long dir name as a wchar string (non-null terminated)
    $dir_long = $dir_long . "\0";

    // Pack the lengths of the dir strings
    $dir_short_len = pack("V", strlen($dir_short));
    $dir_long_len = pack("V", strlen($dir_long));
    $stream_len = pack("V", 0);

    //strlen($dir_long) + 0x06);

    // Pack the undocumented parts of the hyperlink stream
    $unknown1 = pack("H*", 'D0C9EA79F9BACE118C8200AA004BA90B02000000');
    $unknown2 = pack("H*", '0303000000000000C000000000000046');
    $unknown3 = pack("H*", 'FFFFADDE000000000000000000000000000000000000000');
    $unknown4 = pack("v", 0x3);

    // Pack the main data stream
    $data = pack("vvvv", $row1, $row2, $col1, $col2) . $unknown1 . $link_type . $unknown2 . $up_count . $dir_short_len . $dir_short . $unknown3 . $stream_len;

    /*.
      $dir_long_len .
      $unknown4	 .
      $dir_long	 .
      $sheet_len	.
      $sheet		;*/

    // Pack the header data
    $length = strlen($data);
    $header = pack("vv", $record, $length);

    // Write the packed data
    $this
      ->_append($header . $data);
    return 0;
  }

  /**
   * This method is used to set the height and format for a row.
   *
   * @param integer $row	The row to set
   * @param integer $height Height we are giving to the row.
   *						Use null to set XF without setting height
   * @param integer $xfIndex  The optional cell style Xf index to apply to the columns
   * @param bool	$hidden The optional hidden attribute
   * @param integer $level  The optional outline level for row, in range [0,7]
   */
  private function _writeRow($row, $height, $xfIndex, $hidden = false, $level = 0) {
    $record = 0x208;

    // Record identifier
    $length = 0x10;

    // Number of bytes to follow
    $colMic = 0x0;

    // First defined column
    $colMac = 0x0;

    // Last defined column
    $irwMac = 0x0;

    // Used by Excel to optimise loading
    $reserved = 0x0;

    // Reserved
    $grbit = 0x0;

    // Option flags
    $ixfe = $xfIndex;
    if ($height < 0) {
      $height = null;
    }

    // Use _writeRow($row, null, $XF) to set XF format without setting height
    if ($height != null) {
      $miyRw = $height * 20;

      // row height
    }
    else {
      $miyRw = 0xff;

      // default row height is 256
    }

    // Set the options flags. fUnsynced is used to show that the font and row
    // heights are not compatible. This is usually the case for WriteExcel.
    // The collapsed flag 0x10 doesn't seem to be used to indicate that a row
    // is collapsed. Instead it is used to indicate that the previous row is
    // collapsed. The zero height flag, 0x20, is used to collapse a row.
    $grbit |= $level;
    if ($hidden) {
      $grbit |= 0x30;
    }
    if ($height !== null) {
      $grbit |= 0x40;

      // fUnsynced
    }
    if ($xfIndex !== 0xf) {
      $grbit |= 0x80;
    }
    $grbit |= 0x100;
    $header = pack("vv", $record, $length);
    $data = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw, $irwMac, $reserved, $grbit, $ixfe);
    $this
      ->_append($header . $data);
  }

  /**
   * Writes Excel DIMENSIONS to define the area in which there is data.
   */
  private function _writeDimensions() {
    $record = 0x200;

    // Record identifier
    $length = 0xe;
    $data = pack('VVvvv', $this->_firstRowIndex, $this->_lastRowIndex + 1, $this->_firstColumnIndex, $this->_lastColumnIndex + 1, 0x0);
    $header = pack("vv", $record, $length);
    $this
      ->_append($header . $data);
  }

  /**
   * Write BIFF record Window2.
   */
  private function _writeWindow2() {
    $record = 0x23e;

    // Record identifier
    $length = 0x12;
    $grbit = 0xb6;

    // Option flags
    $rwTop = 0x0;

    // Top row visible in window
    $colLeft = 0x0;

    // Leftmost column visible in window
    // The options flags that comprise $grbit
    $fDspFmla = 0;

    // 0 - bit
    $fDspGrid = $this->_phpSheet
      ->getShowGridlines() ? 1 : 0;

    // 1
    $fDspRwCol = $this->_phpSheet
      ->getShowRowColHeaders() ? 1 : 0;

    // 2
    $fFrozen = $this->_phpSheet
      ->getFreezePane() ? 1 : 0;

    // 3
    $fDspZeros = 1;

    // 4
    $fDefaultHdr = 1;

    // 5
    $fArabic = $this->_phpSheet
      ->getRightToLeft() ? 1 : 0;

    // 6
    $fDspGuts = $this->_outline_on;

    // 7
    $fFrozenNoSplit = 0;

    // 0 - bit
    // no support in PHPExcel for selected sheet, therefore sheet is only selected if it is the active sheet
    $fSelected = $this->_phpSheet === $this->_phpSheet
      ->getParent()
      ->getActiveSheet() ? 1 : 0;
    $fPaged = 1;

    // 2
    $fPageBreakPreview = $this->_phpSheet
      ->getSheetView()
      ->getView() === PHPExcel_Worksheet_SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW;
    $grbit = $fDspFmla;
    $grbit |= $fDspGrid << 1;
    $grbit |= $fDspRwCol << 2;
    $grbit |= $fFrozen << 3;
    $grbit |= $fDspZeros << 4;
    $grbit |= $fDefaultHdr << 5;
    $grbit |= $fArabic << 6;
    $grbit |= $fDspGuts << 7;
    $grbit |= $fFrozenNoSplit << 8;
    $grbit |= $fSelected << 9;
    $grbit |= $fPaged << 10;
    $grbit |= $fPageBreakPreview << 11;
    $header = pack("vv", $record, $length);
    $data = pack("vvv", $grbit, $rwTop, $colLeft);

    // FIXME !!!
    $rgbHdr = 0x40;

    // Row/column heading and gridline color index
    $zoom_factor_page_break = $fPageBreakPreview ? $this->_phpSheet
      ->getSheetView()
      ->getZoomScale() : 0x0;
    $zoom_factor_normal = $this->_phpSheet
      ->getSheetView()
      ->getZoomScaleNormal();
    $data .= pack("vvvvV", $rgbHdr, 0x0, $zoom_factor_page_break, $zoom_factor_normal, 0x0);
    $this
      ->_append($header . $data);
  }

  /**
   * Write BIFF record DEFAULTROWHEIGHT.
   */
  private function _writeDefaultRowHeight() {
    $defaultRowHeight = $this->_phpSheet
      ->getDefaultRowDimension()
      ->getRowHeight();
    if ($defaultRowHeight < 0) {
      return;
    }

    // convert to twips
    $defaultRowHeight = (int) 20 * $defaultRowHeight;
    $record = 0x225;

    // Record identifier
    $length = 0x4;

    // Number of bytes to follow
    $header = pack("vv", $record, $length);
    $data = pack("vv", 1, $defaultRowHeight);
    $this
      ->_append($header . $data);
  }

  /**
   * Write BIFF record DEFCOLWIDTH if COLINFO records are in use.
   */
  private function _writeDefcol() {
    $defaultColWidth = 8;
    $record = 0x55;

    // Record identifier
    $length = 0x2;

    // Number of bytes to follow
    $header = pack("vv", $record, $length);
    $data = pack("v", $defaultColWidth);
    $this
      ->_append($header . $data);
  }

  /**
   * Write BIFF record COLINFO to define column widths
   *
   * Note: The SDK says the record length is 0x0B but Excel writes a 0x0C
   * length record.
   *
   * @param array $col_array This is the only parameter received and is composed of the following:
   *				0 => First formatted column,
   *				1 => Last formatted column,
   *				2 => Col width (8.43 is Excel default),
   *				3 => The optional XF format of the column,
   *				4 => Option flags.
   *				5 => Optional outline level
   */
  private function _writeColinfo($col_array) {
    if (isset($col_array[0])) {
      $colFirst = $col_array[0];
    }
    if (isset($col_array[1])) {
      $colLast = $col_array[1];
    }
    if (isset($col_array[2])) {
      $coldx = $col_array[2];
    }
    else {
      $coldx = 8.43;
    }
    if (isset($col_array[3])) {
      $xfIndex = $col_array[3];
    }
    else {
      $xfIndex = 15;
    }
    if (isset($col_array[4])) {
      $grbit = $col_array[4];
    }
    else {
      $grbit = 0;
    }
    if (isset($col_array[5])) {
      $level = $col_array[5];
    }
    else {
      $level = 0;
    }
    $record = 0x7d;

    // Record identifier
    $length = 0xc;

    // Number of bytes to follow
    $coldx *= 256;

    // Convert to units of 1/256 of a char
    $ixfe = $xfIndex;
    $reserved = 0x0;

    // Reserved
    $level = max(0, min($level, 7));
    $grbit |= $level << 8;
    $header = pack("vv", $record, $length);
    $data = pack("vvvvvv", $colFirst, $colLast, $coldx, $ixfe, $grbit, $reserved);
    $this
      ->_append($header . $data);
  }

  /**
   * Write BIFF record SELECTION.
   */
  private function _writeSelection() {

    // look up the selected cell range
    $selectedCells = $this->_phpSheet
      ->getSelectedCells();
    $selectedCells = PHPExcel_Cell::splitRange($this->_phpSheet
      ->getSelectedCells());
    $selectedCells = $selectedCells[0];
    if (count($selectedCells) == 2) {
      list($first, $last) = $selectedCells;
    }
    else {
      $first = $selectedCells[0];
      $last = $selectedCells[0];
    }
    list($colFirst, $rwFirst) = PHPExcel_Cell::coordinateFromString($first);
    $colFirst = PHPExcel_Cell::columnIndexFromString($colFirst) - 1;

    // base 0 column index
    --$rwFirst;

    // base 0 row index
    list($colLast, $rwLast) = PHPExcel_Cell::coordinateFromString($last);
    $colLast = PHPExcel_Cell::columnIndexFromString($colLast) - 1;

    // base 0 column index
    --$rwLast;

    // base 0 row index
    // make sure we are not out of bounds
    $colFirst = min($colFirst, 255);
    $colLast = min($colLast, 255);
    $rwFirst = min($rwFirst, 65535);
    $rwLast = min($rwLast, 65535);
    $record = 0x1d;

    // Record identifier
    $length = 0xf;

    // Number of bytes to follow
    $pnn = $this->_active_pane;

    // Pane position
    $rwAct = $rwFirst;

    // Active row
    $colAct = $colFirst;

    // Active column
    $irefAct = 0;

    // Active cell ref
    $cref = 1;

    // Number of refs
    if (!isset($rwLast)) {
      $rwLast = $rwFirst;

      // Last  row in reference
    }
    if (!isset($colLast)) {
      $colLast = $colFirst;

      // Last  col in reference
    }

    // Swap last row/col for first row/col as necessary
    if ($rwFirst > $rwLast) {
      list($rwFirst, $rwLast) = array(
        $rwLast,
        $rwFirst,
      );
    }
    if ($colFirst > $colLast) {
      list($colFirst, $colLast) = array(
        $colLast,
        $colFirst,
      );
    }
    $header = pack("vv", $record, $length);
    $data = pack("CvvvvvvCC", $pnn, $rwAct, $colAct, $irefAct, $cref, $rwFirst, $rwLast, $colFirst, $colLast);
    $this
      ->_append($header . $data);
  }

  /**
   * Store the MERGEDCELLS records for all ranges of merged cells
   */
  private function _writeMergedCells() {
    $mergeCells = $this->_phpSheet
      ->getMergeCells();
    $countMergeCells = count($mergeCells);
    if ($countMergeCells == 0) {
      return;
    }

    // maximum allowed number of merged cells per record
    $maxCountMergeCellsPerRecord = 1027;

    // record identifier
    $record = 0xe5;

    // counter for total number of merged cells treated so far by the writer
    $i = 0;

    // counter for number of merged cells written in record currently being written
    $j = 0;

    // initialize record data
    $recordData = '';

    // loop through the merged cells
    foreach ($mergeCells as $mergeCell) {
      ++$i;
      ++$j;

      // extract the row and column indexes
      $range = PHPExcel_Cell::splitRange($mergeCell);
      list($first, $last) = $range[0];
      list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($first);
      list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($last);
      $recordData .= pack('vvvv', $firstRow - 1, $lastRow - 1, PHPExcel_Cell::columnIndexFromString($firstColumn) - 1, PHPExcel_Cell::columnIndexFromString($lastColumn) - 1);

      // flush record if we have reached limit for number of merged cells, or reached final merged cell
      if ($j == $maxCountMergeCellsPerRecord or $i == $countMergeCells) {
        $recordData = pack('v', $j) . $recordData;
        $length = strlen($recordData);
        $header = pack('vv', $record, $length);
        $this
          ->_append($header . $recordData);

        // initialize for next record, if any
        $recordData = '';
        $j = 0;
      }
    }
  }

  /**
   * Write SHEETLAYOUT record
   */
  private function _writeSheetLayout() {
    if (!$this->_phpSheet
      ->isTabColorSet()) {
      return;
    }
    $recordData = pack('vvVVVvv', 0x862, 0x0, 0x0, 0x0, 0x14, $this->_colors[$this->_phpSheet
      ->getTabColor()
      ->getRGB()], 0x0);
    $length = strlen($recordData);
    $record = 0x862;

    // Record identifier
    $header = pack('vv', $record, $length);
    $this
      ->_append($header . $recordData);
  }

  /**
   * Write SHEETPROTECTION
   */
  private function _writeSheetProtection() {

    // record identifier
    $record = 0x867;

    // prepare options
    $options = (int) (!$this->_phpSheet
      ->getProtection()
      ->getObjects()) | (int) (!$this->_phpSheet
      ->getProtection()
      ->getScenarios()) << 1 | (int) (!$this->_phpSheet
      ->getProtection()
      ->getFormatCells()) << 2 | (int) (!$this->_phpSheet
      ->getProtection()
      ->getFormatColumns()) << 3 | (int) (!$this->_phpSheet
      ->getProtection()
      ->getFormatRows()) << 4 | (int) (!$this->_phpSheet
      ->getProtection()
      ->getInsertColumns()) << 5 | (int) (!$this->_phpSheet
      ->getProtection()
      ->getInsertRows()) << 6 | (int) (!$this->_phpSheet
      ->getProtection()
      ->getInsertHyperlinks()) << 7 | (int) (!$this->_phpSheet
      ->getProtection()
      ->getDeleteColumns()) << 8 | (int) (!$this->_phpSheet
      ->getProtection()
      ->getDeleteRows()) << 9 | (int) (!$this->_phpSheet
      ->getProtection()
      ->getSelectLockedCells()) << 10 | (int) (!$this->_phpSheet
      ->getProtection()
      ->getSort()) << 11 | (int) (!$this->_phpSheet
      ->getProtection()
      ->getAutoFilter()) << 12 | (int) (!$this->_phpSheet
      ->getProtection()
      ->getPivotTables()) << 13 | (int) (!$this->_phpSheet
      ->getProtection()
      ->getSelectUnlockedCells()) << 14;

    // record data
    $recordData = pack('vVVCVVvv', 0x867, 0x0, 0x0, 0x0, 0x1000200, 0xffffffff, $options, 0x0);
    $length = strlen($recordData);
    $header = pack('vv', $record, $length);
    $this
      ->_append($header . $recordData);
  }

  /**
   * Write BIFF record RANGEPROTECTION
   *
   * Openoffice.org's Documentaion of the Microsoft Excel File Format uses term RANGEPROTECTION for these records
   * Microsoft Office Excel 97-2007 Binary File Format Specification uses term FEAT for these records
   */
  private function _writeRangeProtection() {
    foreach ($this->_phpSheet
      ->getProtectedCells() as $range => $password) {

      // number of ranges, e.g. 'A1:B3 C20:D25'
      $cellRanges = explode(' ', $range);
      $cref = count($cellRanges);
      $recordData = pack('vvVVvCVvVv', 0x868, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, $cref, 0x0, 0x0);
      foreach ($cellRanges as $cellRange) {
        $recordData .= $this
          ->_writeBIFF8CellRangeAddressFixed($cellRange);
      }

      // the rgbFeat structure
      $recordData .= pack('VV', 0x0, hexdec($password));
      $recordData .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong('p' . md5($recordData));
      $length = strlen($recordData);
      $record = 0x868;

      // Record identifier
      $header = pack("vv", $record, $length);
      $this
        ->_append($header . $recordData);
    }
  }

  /**
   * Write BIFF record EXTERNCOUNT to indicate the number of external sheet
   * references in a worksheet.
   *
   * Excel only stores references to external sheets that are used in formulas.
   * For simplicity we store references to all the sheets in the workbook
   * regardless of whether they are used or not. This reduces the overall
   * complexity and eliminates the need for a two way dialogue between the formula
   * parser the worksheet objects.
   *
   * @param integer $count The number of external sheet references in this worksheet
   */
  private function _writeExterncount($count) {
    $record = 0x16;

    // Record identifier
    $length = 0x2;

    // Number of bytes to follow
    $header = pack("vv", $record, $length);
    $data = pack("v", $count);
    $this
      ->_append($header . $data);
  }

  /**
   * Writes the Excel BIFF EXTERNSHEET record. These references are used by
   * formulas. A formula references a sheet name via an index. Since we store a
   * reference to all of the external worksheets the EXTERNSHEET index is the same
   * as the worksheet index.
   *
   * @param string $sheetname The name of a external worksheet
   */
  private function _writeExternsheet($sheetname) {
    $record = 0x17;

    // Record identifier
    // References to the current sheet are encoded differently to references to
    // external sheets.
    //
    if ($this->_phpSheet
      ->getTitle() == $sheetname) {
      $sheetname = '';
      $length = 0x2;

      // The following 2 bytes
      $cch = 1;

      // The following byte
      $rgch = 0x2;

      // Self reference
    }
    else {
      $length = 0x2 + strlen($sheetname);
      $cch = strlen($sheetname);
      $rgch = 0x3;

      // Reference to a sheet in the current workbook
    }
    $header = pack("vv", $record, $length);
    $data = pack("CC", $cch, $rgch);
    $this
      ->_append($header . $data . $sheetname);
  }

  /**
   * Writes the Excel BIFF PANE record.
   * The panes can either be frozen or thawed (unfrozen).
   * Frozen panes are specified in terms of an integer number of rows and columns.
   * Thawed panes are specified in terms of Excel's units for rows and columns.
   */
  private function _writePanes() {
    $panes = array();
    if ($freezePane = $this->_phpSheet
      ->getFreezePane()) {
      list($column, $row) = PHPExcel_Cell::coordinateFromString($freezePane);
      $panes[0] = $row - 1;
      $panes[1] = PHPExcel_Cell::columnIndexFromString($column) - 1;
    }
    else {

      // thaw panes
      return;
    }
    $y = isset($panes[0]) ? $panes[0] : null;
    $x = isset($panes[1]) ? $panes[1] : null;
    $rwTop = isset($panes[2]) ? $panes[2] : null;
    $colLeft = isset($panes[3]) ? $panes[3] : null;
    if (count($panes) > 4) {

      // if Active pane was received
      $pnnAct = $panes[4];
    }
    else {
      $pnnAct = null;
    }
    $record = 0x41;

    // Record identifier
    $length = 0xa;

    // Number of bytes to follow
    // Code specific to frozen or thawed panes.
    if ($this->_phpSheet
      ->getFreezePane()) {

      // Set default values for $rwTop and $colLeft
      if (!isset($rwTop)) {
        $rwTop = $y;
      }
      if (!isset($colLeft)) {
        $colLeft = $x;
      }
    }
    else {

      // Set default values for $rwTop and $colLeft
      if (!isset($rwTop)) {
        $rwTop = 0;
      }
      if (!isset($colLeft)) {
        $colLeft = 0;
      }

      // Convert Excel's row and column units to the internal units.
      // The default row height is 12.75
      // The default column width is 8.43
      // The following slope and intersection values were interpolated.
      //
      $y = 20 * $y + 255;
      $x = 113.879 * $x + 390;
    }

    // Determine which pane should be active. There is also the undocumented
    // option to override this should it be necessary: may be removed later.
    //
    if (!isset($pnnAct)) {
      if ($x != 0 && $y != 0) {
        $pnnAct = 0;

        // Bottom right
      }
      if ($x != 0 && $y == 0) {
        $pnnAct = 1;

        // Top right
      }
      if ($x == 0 && $y != 0) {
        $pnnAct = 2;

        // Bottom left
      }
      if ($x == 0 && $y == 0) {
        $pnnAct = 3;

        // Top left
      }
    }
    $this->_active_pane = $pnnAct;

    // Used in _writeSelection
    $header = pack("vv", $record, $length);
    $data = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct);
    $this
      ->_append($header . $data);
  }

  /**
   * Store the page setup SETUP BIFF record.
   */
  private function _writeSetup() {
    $record = 0xa1;

    // Record identifier
    $length = 0x22;

    // Number of bytes to follow
    $iPaperSize = $this->_phpSheet
      ->getPageSetup()
      ->getPaperSize();

    // Paper size
    $iScale = $this->_phpSheet
      ->getPageSetup()
      ->getScale() ? $this->_phpSheet
      ->getPageSetup()
      ->getScale() : 100;

    // Print scaling factor
    $iPageStart = 0x1;

    // Starting page number
    $iFitWidth = (int) $this->_phpSheet
      ->getPageSetup()
      ->getFitToWidth();

    // Fit to number of pages wide
    $iFitHeight = (int) $this->_phpSheet
      ->getPageSetup()
      ->getFitToHeight();

    // Fit to number of pages high
    $grbit = 0x0;

    // Option flags
    $iRes = 0x258;

    // Print resolution
    $iVRes = 0x258;

    // Vertical print resolution
    $numHdr = $this->_phpSheet
      ->getPageMargins()
      ->getHeader();

    // Header Margin
    $numFtr = $this->_phpSheet
      ->getPageMargins()
      ->getFooter();

    // Footer Margin
    $iCopies = 0x1;

    // Number of copies
    $fLeftToRight = 0x0;

    // Print over then down
    // Page orientation
    $fLandscape = $this->_phpSheet
      ->getPageSetup()
      ->getOrientation() == PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE ? 0x0 : 0x1;
    $fNoPls = 0x0;

    // Setup not read from printer
    $fNoColor = 0x0;

    // Print black and white
    $fDraft = 0x0;

    // Print draft quality
    $fNotes = 0x0;

    // Print notes
    $fNoOrient = 0x0;

    // Orientation not set
    $fUsePage = 0x0;

    // Use custom starting page
    $grbit = $fLeftToRight;
    $grbit |= $fLandscape << 1;
    $grbit |= $fNoPls << 2;
    $grbit |= $fNoColor << 3;
    $grbit |= $fDraft << 4;
    $grbit |= $fNotes << 5;
    $grbit |= $fNoOrient << 6;
    $grbit |= $fUsePage << 7;
    $numHdr = pack("d", $numHdr);
    $numFtr = pack("d", $numFtr);
    if (self::getByteOrder()) {

      // if it's Big Endian
      $numHdr = strrev($numHdr);
      $numFtr = strrev($numFtr);
    }
    $header = pack("vv", $record, $length);
    $data1 = pack("vvvvvvvv", $iPaperSize, $iScale, $iPageStart, $iFitWidth, $iFitHeight, $grbit, $iRes, $iVRes);
    $data2 = $numHdr . $numFtr;
    $data3 = pack("v", $iCopies);
    $this
      ->_append($header . $data1 . $data2 . $data3);
  }

  /**
   * Store the header caption BIFF record.
   */
  private function _writeHeader() {
    $record = 0x14;

    // Record identifier

    /* removing for now
    		// need to fix character count (multibyte!)
    		if (strlen($this->_phpSheet->getHeaderFooter()->getOddHeader()) <= 255) {
    			$str	  = $this->_phpSheet->getHeaderFooter()->getOddHeader();	   // header string
    		} else {
    			$str = '';
    		}
    		*/
    $recordData = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($this->_phpSheet
      ->getHeaderFooter()
      ->getOddHeader());
    $length = strlen($recordData);
    $header = pack("vv", $record, $length);
    $this
      ->_append($header . $recordData);
  }

  /**
   * Store the footer caption BIFF record.
   */
  private function _writeFooter() {
    $record = 0x15;

    // Record identifier

    /* removing for now
    		// need to fix character count (multibyte!)
    		if (strlen($this->_phpSheet->getHeaderFooter()->getOddFooter()) <= 255) {
    			$str = $this->_phpSheet->getHeaderFooter()->getOddFooter();
    		} else {
    			$str = '';
    		}
    		*/
    $recordData = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($this->_phpSheet
      ->getHeaderFooter()
      ->getOddFooter());
    $length = strlen($recordData);
    $header = pack("vv", $record, $length);
    $this
      ->_append($header . $recordData);
  }

  /**
   * Store the horizontal centering HCENTER BIFF record.
   *
   * @access private
   */
  private function _writeHcenter() {
    $record = 0x83;

    // Record identifier
    $length = 0x2;

    // Bytes to follow
    $fHCenter = $this->_phpSheet
      ->getPageSetup()
      ->getHorizontalCentered() ? 1 : 0;

    // Horizontal centering
    $header = pack("vv", $record, $length);
    $data = pack("v", $fHCenter);
    $this
      ->_append($header . $data);
  }

  /**
   * Store the vertical centering VCENTER BIFF record.
   */
  private function _writeVcenter() {
    $record = 0x84;

    // Record identifier
    $length = 0x2;

    // Bytes to follow
    $fVCenter = $this->_phpSheet
      ->getPageSetup()
      ->getVerticalCentered() ? 1 : 0;

    // Horizontal centering
    $header = pack("vv", $record, $length);
    $data = pack("v", $fVCenter);
    $this
      ->_append($header . $data);
  }

  /**
   * Store the LEFTMARGIN BIFF record.
   */
  private function _writeMarginLeft() {
    $record = 0x26;

    // Record identifier
    $length = 0x8;

    // Bytes to follow
    $margin = $this->_phpSheet
      ->getPageMargins()
      ->getLeft();

    // Margin in inches
    $header = pack("vv", $record, $length);
    $data = pack("d", $margin);
    if (self::getByteOrder()) {

      // if it's Big Endian
      $data = strrev($data);
    }
    $this
      ->_append($header . $data);
  }

  /**
   * Store the RIGHTMARGIN BIFF record.
   */
  private function _writeMarginRight() {
    $record = 0x27;

    // Record identifier
    $length = 0x8;

    // Bytes to follow
    $margin = $this->_phpSheet
      ->getPageMargins()
      ->getRight();

    // Margin in inches
    $header = pack("vv", $record, $length);
    $data = pack("d", $margin);
    if (self::getByteOrder()) {

      // if it's Big Endian
      $data = strrev($data);
    }
    $this
      ->_append($header . $data);
  }

  /**
   * Store the TOPMARGIN BIFF record.
   */
  private function _writeMarginTop() {
    $record = 0x28;

    // Record identifier
    $length = 0x8;

    // Bytes to follow
    $margin = $this->_phpSheet
      ->getPageMargins()
      ->getTop();

    // Margin in inches
    $header = pack("vv", $record, $length);
    $data = pack("d", $margin);
    if (self::getByteOrder()) {

      // if it's Big Endian
      $data = strrev($data);
    }
    $this
      ->_append($header . $data);
  }

  /**
   * Store the BOTTOMMARGIN BIFF record.
   */
  private function _writeMarginBottom() {
    $record = 0x29;

    // Record identifier
    $length = 0x8;

    // Bytes to follow
    $margin = $this->_phpSheet
      ->getPageMargins()
      ->getBottom();

    // Margin in inches
    $header = pack("vv", $record, $length);
    $data = pack("d", $margin);
    if (self::getByteOrder()) {

      // if it's Big Endian
      $data = strrev($data);
    }
    $this
      ->_append($header . $data);
  }

  /**
   * Write the PRINTHEADERS BIFF record.
   */
  private function _writePrintHeaders() {
    $record = 0x2a;

    // Record identifier
    $length = 0x2;

    // Bytes to follow
    $fPrintRwCol = $this->_print_headers;

    // Boolean flag
    $header = pack("vv", $record, $length);
    $data = pack("v", $fPrintRwCol);
    $this
      ->_append($header . $data);
  }

  /**
   * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the
   * GRIDSET record.
   */
  private function _writePrintGridlines() {
    $record = 0x2b;

    // Record identifier
    $length = 0x2;

    // Bytes to follow
    $fPrintGrid = $this->_phpSheet
      ->getPrintGridlines() ? 1 : 0;

    // Boolean flag
    $header = pack("vv", $record, $length);
    $data = pack("v", $fPrintGrid);
    $this
      ->_append($header . $data);
  }

  /**
   * Write the GRIDSET BIFF record. Must be used in conjunction with the
   * PRINTGRIDLINES record.
   */
  private function _writeGridset() {
    $record = 0x82;

    // Record identifier
    $length = 0x2;

    // Bytes to follow
    $fGridSet = !$this->_phpSheet
      ->getPrintGridlines();

    // Boolean flag
    $header = pack("vv", $record, $length);
    $data = pack("v", $fGridSet);
    $this
      ->_append($header . $data);
  }

  /**
   * Write the AUTOFILTERINFO BIFF record. This is used to configure the number of autofilter select used in the sheet.
   */
  private function _writeAutoFilterInfo() {
    $record = 0x9d;

    // Record identifier
    $length = 0x2;

    // Bytes to follow
    $rangeBounds = PHPExcel_Cell::rangeBoundaries($this->_phpSheet
      ->getAutoFilter()
      ->getRange());
    $iNumFilters = 1 + $rangeBounds[1][0] - $rangeBounds[0][0];
    $header = pack("vv", $record, $length);
    $data = pack("v", $iNumFilters);
    $this
      ->_append($header . $data);
  }

  /**
   * Write the GUTS BIFF record. This is used to configure the gutter margins
   * where Excel outline symbols are displayed. The visibility of the gutters is
   * controlled by a flag in WSBOOL.
   *
   * @see _writeWsbool()
   */
  private function _writeGuts() {
    $record = 0x80;

    // Record identifier
    $length = 0x8;

    // Bytes to follow
    $dxRwGut = 0x0;

    // Size of row gutter
    $dxColGut = 0x0;

    // Size of col gutter
    // determine maximum row outline level
    $maxRowOutlineLevel = 0;
    foreach ($this->_phpSheet
      ->getRowDimensions() as $rowDimension) {
      $maxRowOutlineLevel = max($maxRowOutlineLevel, $rowDimension
        ->getOutlineLevel());
    }
    $col_level = 0;

    // Calculate the maximum column outline level. The equivalent calculation
    // for the row outline level is carried out in _writeRow().
    $colcount = count($this->_colinfo);
    for ($i = 0; $i < $colcount; ++$i) {
      $col_level = max($this->_colinfo[$i][5], $col_level);
    }

    // Set the limits for the outline levels (0 <= x <= 7).
    $col_level = max(0, min($col_level, 7));

    // The displayed level is one greater than the max outline levels
    if ($maxRowOutlineLevel) {
      ++$maxRowOutlineLevel;
    }
    if ($col_level) {
      ++$col_level;
    }
    $header = pack("vv", $record, $length);
    $data = pack("vvvv", $dxRwGut, $dxColGut, $maxRowOutlineLevel, $col_level);
    $this
      ->_append($header . $data);
  }

  /**
   * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction
   * with the SETUP record.
   */
  private function _writeWsbool() {
    $record = 0x81;

    // Record identifier
    $length = 0x2;

    // Bytes to follow
    $grbit = 0x0;

    // The only option that is of interest is the flag for fit to page. So we
    // set all the options in one go.
    //
    // Set the option flags
    $grbit |= 0x1;

    // Auto page breaks visible
    if ($this->_outline_style) {
      $grbit |= 0x20;

      // Auto outline styles
    }
    if ($this->_phpSheet
      ->getShowSummaryBelow()) {
      $grbit |= 0x40;

      // Outline summary below
    }
    if ($this->_phpSheet
      ->getShowSummaryRight()) {
      $grbit |= 0x80;

      // Outline summary right
    }
    if ($this->_phpSheet
      ->getPageSetup()
      ->getFitToPage()) {
      $grbit |= 0x100;

      // Page setup fit to page
    }
    if ($this->_outline_on) {
      $grbit |= 0x400;

      // Outline symbols displayed
    }
    $header = pack("vv", $record, $length);
    $data = pack("v", $grbit);
    $this
      ->_append($header . $data);
  }

  /**
   * Write the HORIZONTALPAGEBREAKS and VERTICALPAGEBREAKS BIFF records.
   */
  private function _writeBreaks() {

    // initialize
    $vbreaks = array();
    $hbreaks = array();
    foreach ($this->_phpSheet
      ->getBreaks() as $cell => $breakType) {

      // Fetch coordinates
      $coordinates = PHPExcel_Cell::coordinateFromString($cell);

      // Decide what to do by the type of break
      switch ($breakType) {
        case PHPExcel_Worksheet::BREAK_COLUMN:

          // Add to list of vertical breaks
          $vbreaks[] = PHPExcel_Cell::columnIndexFromString($coordinates[0]) - 1;
          break;
        case PHPExcel_Worksheet::BREAK_ROW:

          // Add to list of horizontal breaks
          $hbreaks[] = $coordinates[1];
          break;
        case PHPExcel_Worksheet::BREAK_NONE:
        default:

          // Nothing to do
          break;
      }
    }

    //horizontal page breaks
    if (!empty($hbreaks)) {

      // Sort and filter array of page breaks
      sort($hbreaks, SORT_NUMERIC);
      if ($hbreaks[0] == 0) {

        // don't use first break if it's 0
        array_shift($hbreaks);
      }
      $record = 0x1b;

      // Record identifier
      $cbrk = count($hbreaks);

      // Number of page breaks
      $length = 2 + 6 * $cbrk;

      // Bytes to follow
      $header = pack("vv", $record, $length);
      $data = pack("v", $cbrk);

      // Append each page break
      foreach ($hbreaks as $hbreak) {
        $data .= pack("vvv", $hbreak, 0x0, 0xff);
      }
      $this
        ->_append($header . $data);
    }

    // vertical page breaks
    if (!empty($vbreaks)) {

      // 1000 vertical pagebreaks appears to be an internal Excel 5 limit.
      // It is slightly higher in Excel 97/200, approx. 1026
      $vbreaks = array_slice($vbreaks, 0, 1000);

      // Sort and filter array of page breaks
      sort($vbreaks, SORT_NUMERIC);
      if ($vbreaks[0] == 0) {

        // don't use first break if it's 0
        array_shift($vbreaks);
      }
      $record = 0x1a;

      // Record identifier
      $cbrk = count($vbreaks);

      // Number of page breaks
      $length = 2 + 6 * $cbrk;

      // Bytes to follow
      $header = pack("vv", $record, $length);
      $data = pack("v", $cbrk);

      // Append each page break
      foreach ($vbreaks as $vbreak) {
        $data .= pack("vvv", $vbreak, 0x0, 0xffff);
      }
      $this
        ->_append($header . $data);
    }
  }

  /**
   * Set the Biff PROTECT record to indicate that the worksheet is protected.
   */
  private function _writeProtect() {

    // Exit unless sheet protection has been specified
    if (!$this->_phpSheet
      ->getProtection()
      ->getSheet()) {
      return;
    }
    $record = 0x12;

    // Record identifier
    $length = 0x2;

    // Bytes to follow
    $fLock = 1;

    // Worksheet is protected
    $header = pack("vv", $record, $length);
    $data = pack("v", $fLock);
    $this
      ->_append($header . $data);
  }

  /**
   * Write SCENPROTECT
   */
  private function _writeScenProtect() {

    // Exit if sheet protection is not active
    if (!$this->_phpSheet
      ->getProtection()
      ->getSheet()) {
      return;
    }

    // Exit if scenarios are not protected
    if (!$this->_phpSheet
      ->getProtection()
      ->getScenarios()) {
      return;
    }
    $record = 0xdd;

    // Record identifier
    $length = 0x2;

    // Bytes to follow
    $header = pack('vv', $record, $length);
    $data = pack('v', 1);
    $this
      ->_append($header . $data);
  }

  /**
   * Write OBJECTPROTECT
   */
  private function _writeObjectProtect() {

    // Exit if sheet protection is not active
    if (!$this->_phpSheet
      ->getProtection()
      ->getSheet()) {
      return;
    }

    // Exit if objects are not protected
    if (!$this->_phpSheet
      ->getProtection()
      ->getObjects()) {
      return;
    }
    $record = 0x63;

    // Record identifier
    $length = 0x2;

    // Bytes to follow
    $header = pack('vv', $record, $length);
    $data = pack('v', 1);
    $this
      ->_append($header . $data);
  }

  /**
   * Write the worksheet PASSWORD record.
   */
  private function _writePassword() {

    // Exit unless sheet protection and password have been specified
    if (!$this->_phpSheet
      ->getProtection()
      ->getSheet() || !$this->_phpSheet
      ->getProtection()
      ->getPassword()) {
      return;
    }
    $record = 0x13;

    // Record identifier
    $length = 0x2;

    // Bytes to follow
    $wPassword = hexdec($this->_phpSheet
      ->getProtection()
      ->getPassword());

    // Encoded password
    $header = pack("vv", $record, $length);
    $data = pack("v", $wPassword);
    $this
      ->_append($header . $data);
  }

  /**
   * Insert a 24bit bitmap image in a worksheet.
   *
   * @access public
   * @param integer $row	 The row we are going to insert the bitmap into
   * @param integer $col	 The column we are going to insert the bitmap into
   * @param mixed   $bitmap  The bitmap filename or GD-image resource
   * @param integer $x	   The horizontal position (offset) of the image inside the cell.
   * @param integer $y	   The vertical position (offset) of the image inside the cell.
   * @param float   $scale_x The horizontal scale
   * @param float   $scale_y The vertical scale
   */
  function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1) {
    $bitmap_array = is_resource($bitmap) ? $this
      ->_processBitmapGd($bitmap) : $this
      ->_processBitmap($bitmap);
    list($width, $height, $size, $data) = $bitmap_array;

    //$this->_processBitmap($bitmap);

    // Scale the frame of the image.
    $width *= $scale_x;
    $height *= $scale_y;

    // Calculate the vertices of the image and write the OBJ record
    $this
      ->_positionImage($col, $row, $x, $y, $width, $height);

    // Write the IMDATA record to store the bitmap data
    $record = 0x7f;
    $length = 8 + $size;
    $cf = 0x9;
    $env = 0x1;
    $lcb = $size;
    $header = pack("vvvvV", $record, $length, $cf, $env, $lcb);
    $this
      ->_append($header . $data);
  }

  /**
   * Calculate the vertices that define the position of the image as required by
   * the OBJ record.
   *
   *		 +------------+------------+
   *		 |	 A	  |	  B	 |
   *   +-----+------------+------------+
   *   |	 |(x1,y1)	 |			|
   *   |  1  |(A1)._______|______	  |
   *   |	 |	|			  |	 |
   *   |	 |	|			  |	 |
   *   +-----+----|	BITMAP	|-----+
   *   |	 |	|			  |	 |
   *   |  2  |	|______________.	 |
   *   |	 |			|		(B2)|
   *   |	 |			|	 (x2,y2)|
   *   +---- +------------+------------+
   *
   * Example of a bitmap that covers some of the area from cell A1 to cell B2.
   *
   * Based on the width and height of the bitmap we need to calculate 8 vars:
   *	 $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
   * The width and height of the cells are also variable and have to be taken into
   * account.
   * The values of $col_start and $row_start are passed in from the calling
   * function. The values of $col_end and $row_end are calculated by subtracting
   * the width and height of the bitmap from the width and height of the
   * underlying cells.
   * The vertices are expressed as a percentage of the underlying cell width as
   * follows (rhs values are in pixels):
   *
   *	   x1 = X / W *1024
   *	   y1 = Y / H *256
   *	   x2 = (X-1) / W *1024
   *	   y2 = (Y-1) / H *256
   *
   *	   Where:  X is distance from the left side of the underlying cell
   *			   Y is distance from the top of the underlying cell
   *			   W is the width of the cell
   *			   H is the height of the cell
   * The SDK incorrectly states that the height should be expressed as a
   *		percentage of 1024.
   *
   * @access private
   * @param integer $col_start Col containing upper left corner of object
   * @param integer $row_start Row containing top left corner of object
   * @param integer $x1		Distance to left side of object
   * @param integer $y1		Distance to top of object
   * @param integer $width	 Width of image frame
   * @param integer $height	Height of image frame
   */
  function _positionImage($col_start, $row_start, $x1, $y1, $width, $height) {

    // Initialise end cell to the same as the start cell
    $col_end = $col_start;

    // Col containing lower right corner of object
    $row_end = $row_start;

    // Row containing bottom right corner of object
    // Zero the specified offset if greater than the cell dimensions
    if ($x1 >= PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_start))) {
      $x1 = 0;
    }
    if ($y1 >= PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_start + 1)) {
      $y1 = 0;
    }
    $width = $width + $x1 - 1;
    $height = $height + $y1 - 1;

    // Subtract the underlying cell widths to find the end cell of the image
    while ($width >= PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end))) {
      $width -= PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end));
      ++$col_end;
    }

    // Subtract the underlying cell heights to find the end cell of the image
    while ($height >= PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_end + 1)) {
      $height -= PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_end + 1);
      ++$row_end;
    }

    // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
    // with zero eight or width.
    //
    if (PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_start)) == 0) {
      return;
    }
    if (PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end)) == 0) {
      return;
    }
    if (PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_start + 1) == 0) {
      return;
    }
    if (PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_end + 1) == 0) {
      return;
    }

    // Convert the pixel values to the percentage value expected by Excel
    $x1 = $x1 / PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_start)) * 1024;
    $y1 = $y1 / PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_start + 1) * 256;
    $x2 = $width / PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, PHPExcel_Cell::stringFromColumnIndex($col_end)) * 1024;

    // Distance to right side of object
    $y2 = $height / PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $row_end + 1) * 256;

    // Distance to bottom of object
    $this
      ->_writeObjPicture($col_start, $x1, $row_start, $y1, $col_end, $x2, $row_end, $y2);
  }

  /**
   * Store the OBJ record that precedes an IMDATA record. This could be generalise
   * to support other Excel objects.
   *
   * @param integer $colL Column containing upper left corner of object
   * @param integer $dxL  Distance from left side of cell
   * @param integer $rwT  Row containing top left corner of object
   * @param integer $dyT  Distance from top of cell
   * @param integer $colR Column containing lower right corner of object
   * @param integer $dxR  Distance from right of cell
   * @param integer $rwB  Row containing bottom right corner of object
   * @param integer $dyB  Distance from bottom of cell
   */
  private function _writeObjPicture($colL, $dxL, $rwT, $dyT, $colR, $dxR, $rwB, $dyB) {
    $record = 0x5d;

    // Record identifier
    $length = 0x3c;

    // Bytes to follow
    $cObj = 0x1;

    // Count of objects in file (set to 1)
    $OT = 0x8;

    // Object type. 8 = Picture
    $id = 0x1;

    // Object ID
    $grbit = 0x614;

    // Option flags
    $cbMacro = 0x0;

    // Length of FMLA structure
    $Reserved1 = 0x0;

    // Reserved
    $Reserved2 = 0x0;

    // Reserved
    $icvBack = 0x9;

    // Background colour
    $icvFore = 0x9;

    // Foreground colour
    $fls = 0x0;

    // Fill pattern
    $fAuto = 0x0;

    // Automatic fill
    $icv = 0x8;

    // Line colour
    $lns = 0xff;

    // Line style
    $lnw = 0x1;

    // Line weight
    $fAutoB = 0x0;

    // Automatic border
    $frs = 0x0;

    // Frame style
    $cf = 0x9;

    // Image format, 9 = bitmap
    $Reserved3 = 0x0;

    // Reserved
    $cbPictFmla = 0x0;

    // Length of FMLA structure
    $Reserved4 = 0x0;

    // Reserved
    $grbit2 = 0x1;

    // Option flags
    $Reserved5 = 0x0;

    // Reserved
    $header = pack("vv", $record, $length);
    $data = pack("V", $cObj);
    $data .= pack("v", $OT);
    $data .= pack("v", $id);
    $data .= pack("v", $grbit);
    $data .= pack("v", $colL);
    $data .= pack("v", $dxL);
    $data .= pack("v", $rwT);
    $data .= pack("v", $dyT);
    $data .= pack("v", $colR);
    $data .= pack("v", $dxR);
    $data .= pack("v", $rwB);
    $data .= pack("v", $dyB);
    $data .= pack("v", $cbMacro);
    $data .= pack("V", $Reserved1);
    $data .= pack("v", $Reserved2);
    $data .= pack("C", $icvBack);
    $data .= pack("C", $icvFore);
    $data .= pack("C", $fls);
    $data .= pack("C", $fAuto);
    $data .= pack("C", $icv);
    $data .= pack("C", $lns);
    $data .= pack("C", $lnw);
    $data .= pack("C", $fAutoB);
    $data .= pack("v", $frs);
    $data .= pack("V", $cf);
    $data .= pack("v", $Reserved3);
    $data .= pack("v", $cbPictFmla);
    $data .= pack("v", $Reserved4);
    $data .= pack("v", $grbit2);
    $data .= pack("V", $Reserved5);
    $this
      ->_append($header . $data);
  }

  /**
   * Convert a GD-image into the internal format.
   *
   * @access private
   * @param resource $image The image to process
   * @return array Array with data and properties of the bitmap
   */
  function _processBitmapGd($image) {
    $width = imagesx($image);
    $height = imagesy($image);
    $data = pack("Vvvvv", 0xc, $width, $height, 0x1, 0x18);
    for ($j = $height; $j--;) {
      for ($i = 0; $i < $width; ++$i) {
        $color = imagecolorsforindex($image, imagecolorat($image, $i, $j));
        foreach (array(
          "red",
          "green",
          "blue",
        ) as $key) {
          $color[$key] = $color[$key] + round((255 - $color[$key]) * $color["alpha"] / 127);
        }
        $data .= chr($color["blue"]) . chr($color["green"]) . chr($color["red"]);
      }
      if (3 * $width % 4) {
        $data .= str_repeat("\0", 4 - 3 * $width % 4);
      }
    }
    return array(
      $width,
      $height,
      strlen($data),
      $data,
    );
  }

  /**
   * Convert a 24 bit bitmap into the modified internal format used by Windows.
   * This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the
   * MSDN library.
   *
   * @access private
   * @param string $bitmap The bitmap to process
   * @return array Array with data and properties of the bitmap
   */
  function _processBitmap($bitmap) {

    // Open file.
    $bmp_fd = @fopen($bitmap, "rb");
    if (!$bmp_fd) {
      throw new PHPExcel_Writer_Exception("Couldn't import {$bitmap}");
    }

    // Slurp the file into a string.
    $data = fread($bmp_fd, filesize($bitmap));

    // Check that the file is big enough to be a bitmap.
    if (strlen($data) <= 0x36) {
      throw new PHPExcel_Writer_Exception("{$bitmap} doesn't contain enough data.\n");
    }

    // The first 2 bytes are used to identify the bitmap.
    $identity = unpack("A2ident", $data);
    if ($identity['ident'] != "BM") {
      throw new PHPExcel_Writer_Exception("{$bitmap} doesn't appear to be a valid bitmap image.\n");
    }

    // Remove bitmap data: ID.
    $data = substr($data, 2);

    // Read and remove the bitmap size. This is more reliable than reading
    // the data size at offset 0x22.
    //
    $size_array = unpack("Vsa", substr($data, 0, 4));
    $size = $size_array['sa'];
    $data = substr($data, 4);
    $size -= 0x36;

    // Subtract size of bitmap header.
    $size += 0xc;

    // Add size of BIFF header.
    // Remove bitmap data: reserved, offset, header length.
    $data = substr($data, 12);

    // Read and remove the bitmap width and height. Verify the sizes.
    $width_and_height = unpack("V2", substr($data, 0, 8));
    $width = $width_and_height[1];
    $height = $width_and_height[2];
    $data = substr($data, 8);
    if ($width > 0xffff) {
      throw new PHPExcel_Writer_Exception("{$bitmap}: largest image width supported is 65k.\n");
    }
    if ($height > 0xffff) {
      throw new PHPExcel_Writer_Exception("{$bitmap}: largest image height supported is 65k.\n");
    }

    // Read and remove the bitmap planes and bpp data. Verify them.
    $planes_and_bitcount = unpack("v2", substr($data, 0, 4));
    $data = substr($data, 4);
    if ($planes_and_bitcount[2] != 24) {

      // Bitcount
      throw new PHPExcel_Writer_Exception("{$bitmap} isn't a 24bit true color bitmap.\n");
    }
    if ($planes_and_bitcount[1] != 1) {
      throw new PHPExcel_Writer_Exception("{$bitmap}: only 1 plane supported in bitmap image.\n");
    }

    // Read and remove the bitmap compression. Verify compression.
    $compression = unpack("Vcomp", substr($data, 0, 4));
    $data = substr($data, 4);

    //$compression = 0;
    if ($compression['comp'] != 0) {
      throw new PHPExcel_Writer_Exception("{$bitmap}: compression not supported in bitmap image.\n");
    }

    // Remove bitmap data: data size, hres, vres, colours, imp. colours.
    $data = substr($data, 20);

    // Add the BITMAPCOREHEADER data
    $header = pack("Vvvvv", 0xc, $width, $height, 0x1, 0x18);
    $data = $header . $data;
    return array(
      $width,
      $height,
      $size,
      $data,
    );
  }

  /**
   * Store the window zoom factor. This should be a reduced fraction but for
   * simplicity we will store all fractions with a numerator of 100.
   */
  private function _writeZoom() {

    // If scale is 100 we don't need to write a record
    if ($this->_phpSheet
      ->getSheetView()
      ->getZoomScale() == 100) {
      return;
    }
    $record = 0xa0;

    // Record identifier
    $length = 0x4;

    // Bytes to follow
    $header = pack("vv", $record, $length);
    $data = pack("vv", $this->_phpSheet
      ->getSheetView()
      ->getZoomScale(), 100);
    $this
      ->_append($header . $data);
  }

  /**
   * Get Escher object
   *
   * @return PHPExcel_Shared_Escher
   */
  public function getEscher() {
    return $this->_escher;
  }

  /**
   * Set Escher object
   *
   * @param PHPExcel_Shared_Escher $pValue
   */
  public function setEscher(PHPExcel_Shared_Escher $pValue = null) {
    $this->_escher = $pValue;
  }

  /**
   * Write MSODRAWING record
   */
  private function _writeMsoDrawing() {

    // write the Escher stream if necessary
    if (isset($this->_escher)) {
      $writer = new PHPExcel_Writer_Excel5_Escher($this->_escher);
      $data = $writer
        ->close();
      $spOffsets = $writer
        ->getSpOffsets();
      $spTypes = $writer
        ->getSpTypes();

      // write the neccesary MSODRAWING, OBJ records
      // split the Escher stream
      $spOffsets[0] = 0;
      $nm = count($spOffsets) - 1;

      // number of shapes excluding first shape
      for ($i = 1; $i <= $nm; ++$i) {

        // MSODRAWING record
        $record = 0xec;

        // Record identifier
        // chunk of Escher stream for one shape
        $dataChunk = substr($data, $spOffsets[$i - 1], $spOffsets[$i] - $spOffsets[$i - 1]);
        $length = strlen($dataChunk);
        $header = pack("vv", $record, $length);
        $this
          ->_append($header . $dataChunk);

        // OBJ record
        $record = 0x5d;

        // record identifier
        $objData = '';

        // ftCmo
        if ($spTypes[$i] == 0xc9) {

          // Add ftCmo (common object data) subobject
          $objData .= pack('vvvvvVVV', 0x15, 0x12, 0x14, $i, 0x2101, 0, 0, 0);

          // Add ftSbs Scroll bar subobject
          $objData .= pack('vv', 0xc, 0x14);
          $objData .= pack('H*', '0000000000000000640001000A00000010000100');

          // Add ftLbsData (List box data) subobject
          $objData .= pack('vv', 0x13, 0x1fee);
          $objData .= pack('H*', '00000000010001030000020008005700');
        }
        else {

          // Add ftCmo (common object data) subobject
          $objData .= pack('vvvvvVVV', 0x15, 0x12, 0x8, $i, 0x6011, 0, 0, 0);
        }

        // ftEnd
        $objData .= pack('vv', 0x0, 0x0);
        $length = strlen($objData);
        $header = pack('vv', $record, $length);
        $this
          ->_append($header . $objData);
      }
    }
  }

  /**
   * Store the DATAVALIDATIONS and DATAVALIDATION records.
   */
  private function _writeDataValidity() {

    // Datavalidation collection
    $dataValidationCollection = $this->_phpSheet
      ->getDataValidationCollection();

    // Write data validations?
    if (!empty($dataValidationCollection)) {

      // DATAVALIDATIONS record
      $record = 0x1b2;

      // Record identifier
      $length = 0x12;

      // Bytes to follow
      $grbit = 0x0;

      // Prompt box at cell, no cached validity data at DV records
      $horPos = 0x0;

      // Horizontal position of prompt box, if fixed position
      $verPos = 0x0;

      // Vertical position of prompt box, if fixed position
      $objId = 0xffffffff;

      // Object identifier of drop down arrow object, or -1 if not visible
      $header = pack('vv', $record, $length);
      $data = pack('vVVVV', $grbit, $horPos, $verPos, $objId, count($dataValidationCollection));
      $this
        ->_append($header . $data);

      // DATAVALIDATION records
      $record = 0x1be;

      // Record identifier
      foreach ($dataValidationCollection as $cellCoordinate => $dataValidation) {

        // initialize record data
        $data = '';

        // options
        $options = 0x0;

        // data type
        $type = $dataValidation
          ->getType();
        switch ($type) {
          case PHPExcel_Cell_DataValidation::TYPE_NONE:
            $type = 0x0;
            break;
          case PHPExcel_Cell_DataValidation::TYPE_WHOLE:
            $type = 0x1;
            break;
          case PHPExcel_Cell_DataValidation::TYPE_DECIMAL:
            $type = 0x2;
            break;
          case PHPExcel_Cell_DataValidation::TYPE_LIST:
            $type = 0x3;
            break;
          case PHPExcel_Cell_DataValidation::TYPE_DATE:
            $type = 0x4;
            break;
          case PHPExcel_Cell_DataValidation::TYPE_TIME:
            $type = 0x5;
            break;
          case PHPExcel_Cell_DataValidation::TYPE_TEXTLENGTH:
            $type = 0x6;
            break;
          case PHPExcel_Cell_DataValidation::TYPE_CUSTOM:
            $type = 0x7;
            break;
        }
        $options |= $type << 0;

        // error style
        $errorStyle = $dataValidation
          ->getType();
        switch ($errorStyle) {
          case PHPExcel_Cell_DataValidation::STYLE_STOP:
            $errorStyle = 0x0;
            break;
          case PHPExcel_Cell_DataValidation::STYLE_WARNING:
            $errorStyle = 0x1;
            break;
          case PHPExcel_Cell_DataValidation::STYLE_INFORMATION:
            $errorStyle = 0x2;
            break;
        }
        $options |= $errorStyle << 4;

        // explicit formula?
        if ($type == 0x3 && preg_match('/^\\".*\\"$/', $dataValidation
          ->getFormula1())) {
          $options |= 0x1 << 7;
        }

        // empty cells allowed
        $options |= $dataValidation
          ->getAllowBlank() << 8;

        // show drop down
        $options |= !$dataValidation
          ->getShowDropDown() << 9;

        // show input message
        $options |= $dataValidation
          ->getShowInputMessage() << 18;

        // show error message
        $options |= $dataValidation
          ->getShowErrorMessage() << 19;

        // condition operator
        $operator = $dataValidation
          ->getOperator();
        switch ($operator) {
          case PHPExcel_Cell_DataValidation::OPERATOR_BETWEEN:
            $operator = 0x0;
            break;
          case PHPExcel_Cell_DataValidation::OPERATOR_NOTBETWEEN:
            $operator = 0x1;
            break;
          case PHPExcel_Cell_DataValidation::OPERATOR_EQUAL:
            $operator = 0x2;
            break;
          case PHPExcel_Cell_DataValidation::OPERATOR_NOTEQUAL:
            $operator = 0x3;
            break;
          case PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHAN:
            $operator = 0x4;
            break;
          case PHPExcel_Cell_DataValidation::OPERATOR_LESSTHAN:
            $operator = 0x5;
            break;
          case PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHANOREQUAL:
            $operator = 0x6;
            break;
          case PHPExcel_Cell_DataValidation::OPERATOR_LESSTHANOREQUAL:
            $operator = 0x7;
            break;
        }
        $options |= $operator << 20;
        $data = pack('V', $options);

        // prompt title
        $promptTitle = $dataValidation
          ->getPromptTitle() !== '' ? $dataValidation
          ->getPromptTitle() : chr(0);
        $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($promptTitle);

        // error title
        $errorTitle = $dataValidation
          ->getErrorTitle() !== '' ? $dataValidation
          ->getErrorTitle() : chr(0);
        $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($errorTitle);

        // prompt text
        $prompt = $dataValidation
          ->getPrompt() !== '' ? $dataValidation
          ->getPrompt() : chr(0);
        $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($prompt);

        // error text
        $error = $dataValidation
          ->getError() !== '' ? $dataValidation
          ->getError() : chr(0);
        $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($error);

        // formula 1
        try {
          $formula1 = $dataValidation
            ->getFormula1();
          if ($type == 0x3) {

            // list type
            $formula1 = str_replace(',', chr(0), $formula1);
          }
          $this->_parser
            ->parse($formula1);
          $formula1 = $this->_parser
            ->toReversePolish();
          $sz1 = strlen($formula1);
        } catch (PHPExcel_Exception $e) {
          $sz1 = 0;
          $formula1 = '';
        }
        $data .= pack('vv', $sz1, 0x0);
        $data .= $formula1;

        // formula 2
        try {
          $formula2 = $dataValidation
            ->getFormula2();
          if ($formula2 === '') {
            throw new PHPExcel_Writer_Exception('No formula2');
          }
          $this->_parser
            ->parse($formula2);
          $formula2 = $this->_parser
            ->toReversePolish();
          $sz2 = strlen($formula2);
        } catch (PHPExcel_Exception $e) {
          $sz2 = 0;
          $formula2 = '';
        }
        $data .= pack('vv', $sz2, 0x0);
        $data .= $formula2;

        // cell range address list
        $data .= pack('v', 0x1);
        $data .= $this
          ->_writeBIFF8CellRangeAddressFixed($cellCoordinate);
        $length = strlen($data);
        $header = pack("vv", $record, $length);
        $this
          ->_append($header . $data);
      }
    }
  }

  /**
   * Map Error code
   *
   * @param string $errorCode
   * @return int
   */
  private static function _mapErrorCode($errorCode) {
    switch ($errorCode) {
      case '#NULL!':
        return 0x0;
      case '#DIV/0!':
        return 0x7;
      case '#VALUE!':
        return 0xf;
      case '#REF!':
        return 0x17;
      case '#NAME?':
        return 0x1d;
      case '#NUM!':
        return 0x24;
      case '#N/A':
        return 0x2a;
    }
    return 0;
  }

  /**
   * Write PLV Record
   */
  private function _writePageLayoutView() {
    $record = 0x88b;

    // Record identifier
    $length = 0x10;

    // Bytes to follow
    $rt = 0x88b;

    // 2
    $grbitFrt = 0x0;

    // 2
    $reserved = 0x0;

    // 8
    $wScalvePLV = $this->_phpSheet
      ->getSheetView()
      ->getZoomScale();

    // 2
    // The options flags that comprise $grbit
    if ($this->_phpSheet
      ->getSheetView()
      ->getView() == PHPExcel_Worksheet_SheetView::SHEETVIEW_PAGE_LAYOUT) {
      $fPageLayoutView = 1;
    }
    else {
      $fPageLayoutView = 0;
    }
    $fRulerVisible = 0;
    $fWhitespaceHidden = 0;
    $grbit = $fPageLayoutView;

    // 2
    $grbit |= $fRulerVisible << 1;
    $grbit |= $fWhitespaceHidden << 3;
    $header = pack("vv", $record, $length);
    $data = pack("vvVVvv", $rt, $grbitFrt, 0x0, 0x0, $wScalvePLV, $grbit);
    $this
      ->_append($header . $data);
  }

  /**
   * Write CFRule Record
   * @param PHPExcel_Style_Conditional $conditional
   */
  private function _writeCFRule(PHPExcel_Style_Conditional $conditional) {
    $record = 0x1b1;

    // Record identifier
    // $type : Type of the CF
    // $operatorType : Comparison operator
    if ($conditional
      ->getConditionType() == PHPExcel_Style_Conditional::CONDITION_EXPRESSION) {
      $type = 0x2;
      $operatorType = 0x0;
    }
    else {
      if ($conditional
        ->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CELLIS) {
        $type = 0x1;
        switch ($conditional
          ->getOperatorType()) {
          case PHPExcel_Style_Conditional::OPERATOR_NONE:
            $operatorType = 0x0;
            break;
          case PHPExcel_Style_Conditional::OPERATOR_EQUAL:
            $operatorType = 0x3;
            break;
          case PHPExcel_Style_Conditional::OPERATOR_GREATERTHAN:
            $operatorType = 0x5;
            break;
          case PHPExcel_Style_Conditional::OPERATOR_GREATERTHANOREQUAL:
            $operatorType = 0x7;
            break;
          case PHPExcel_Style_Conditional::OPERATOR_LESSTHAN:
            $operatorType = 0x6;
            break;
          case PHPExcel_Style_Conditional::OPERATOR_LESSTHANOREQUAL:
            $operatorType = 0x8;
            break;
          case PHPExcel_Style_Conditional::OPERATOR_NOTEQUAL:
            $operatorType = 0x4;
            break;
          case PHPExcel_Style_Conditional::OPERATOR_BETWEEN:
            $operatorType = 0x1;
            break;
        }
      }
    }

    // $szValue1 : size of the formula data for first value or formula
    // $szValue2 : size of the formula data for second value or formula
    $arrConditions = $conditional
      ->getConditions();
    $numConditions = sizeof($arrConditions);
    if ($numConditions == 1) {
      $szValue1 = $arrConditions[0] <= 65535 ? 3 : 0x0;
      $szValue2 = 0x0;
      $operand1 = pack('Cv', 0x1e, $arrConditions[0]);
      $operand2 = null;
    }
    else {
      if ($numConditions == 2 && $conditional
        ->getOperatorType() == PHPExcel_Style_Conditional::OPERATOR_BETWEEN) {
        $szValue1 = $arrConditions[0] <= 65535 ? 3 : 0x0;
        $szValue2 = $arrConditions[1] <= 65535 ? 3 : 0x0;
        $operand1 = pack('Cv', 0x1e, $arrConditions[0]);
        $operand2 = pack('Cv', 0x1e, $arrConditions[1]);
      }
      else {
        $szValue1 = 0x0;
        $szValue2 = 0x0;
        $operand1 = null;
        $operand2 = null;
      }
    }

    // $flags : Option flags
    // Alignment
    $bAlignHz = $conditional
      ->getStyle()
      ->getAlignment()
      ->getHorizontal() == null ? 1 : 0;
    $bAlignVt = $conditional
      ->getStyle()
      ->getAlignment()
      ->getVertical() == null ? 1 : 0;
    $bAlignWrapTx = $conditional
      ->getStyle()
      ->getAlignment()
      ->getWrapText() == false ? 1 : 0;
    $bTxRotation = $conditional
      ->getStyle()
      ->getAlignment()
      ->getTextRotation() == null ? 1 : 0;
    $bIndent = $conditional
      ->getStyle()
      ->getAlignment()
      ->getIndent() == 0 ? 1 : 0;
    $bShrinkToFit = $conditional
      ->getStyle()
      ->getAlignment()
      ->getShrinkToFit() == false ? 1 : 0;
    if ($bAlignHz == 0 || $bAlignVt == 0 || $bAlignWrapTx == 0 || $bTxRotation == 0 || $bIndent == 0 || $bShrinkToFit == 0) {
      $bFormatAlign = 1;
    }
    else {
      $bFormatAlign = 0;
    }

    // Protection
    $bProtLocked = $conditional
      ->getStyle()
      ->getProtection()
      ->getLocked() == null ? 1 : 0;
    $bProtHidden = $conditional
      ->getStyle()
      ->getProtection()
      ->getHidden() == null ? 1 : 0;
    if ($bProtLocked == 0 || $bProtHidden == 0) {
      $bFormatProt = 1;
    }
    else {
      $bFormatProt = 0;
    }

    // Border
    $bBorderLeft = $conditional
      ->getStyle()
      ->getBorders()
      ->getLeft()
      ->getColor()
      ->getARGB() == PHPExcel_Style_Color::COLOR_BLACK && $conditional
      ->getStyle()
      ->getBorders()
      ->getLeft()
      ->getBorderStyle() == PHPExcel_Style_Border::BORDER_NONE ? 1 : 0;
    $bBorderRight = $conditional
      ->getStyle()
      ->getBorders()
      ->getRight()
      ->getColor()
      ->getARGB() == PHPExcel_Style_Color::COLOR_BLACK && $conditional
      ->getStyle()
      ->getBorders()
      ->getRight()
      ->getBorderStyle() == PHPExcel_Style_Border::BORDER_NONE ? 1 : 0;
    $bBorderTop = $conditional
      ->getStyle()
      ->getBorders()
      ->getTop()
      ->getColor()
      ->getARGB() == PHPExcel_Style_Color::COLOR_BLACK && $conditional
      ->getStyle()
      ->getBorders()
      ->getTop()
      ->getBorderStyle() == PHPExcel_Style_Border::BORDER_NONE ? 1 : 0;
    $bBorderBottom = $conditional
      ->getStyle()
      ->getBorders()
      ->getBottom()
      ->getColor()
      ->getARGB() == PHPExcel_Style_Color::COLOR_BLACK && $conditional
      ->getStyle()
      ->getBorders()
      ->getBottom()
      ->getBorderStyle() == PHPExcel_Style_Border::BORDER_NONE ? 1 : 0;
    if ($bBorderLeft == 0 || $bBorderRight == 0 || $bBorderTop == 0 || $bBorderBottom == 0) {
      $bFormatBorder = 1;
    }
    else {
      $bFormatBorder = 0;
    }

    // Pattern
    $bFillStyle = $conditional
      ->getStyle()
      ->getFill()
      ->getFillType() == null ? 0 : 1;
    $bFillColor = $conditional
      ->getStyle()
      ->getFill()
      ->getStartColor()
      ->getARGB() == null ? 0 : 1;
    $bFillColorBg = $conditional
      ->getStyle()
      ->getFill()
      ->getEndColor()
      ->getARGB() == null ? 0 : 1;
    if ($bFillStyle == 0 || $bFillColor == 0 || $bFillColorBg == 0) {
      $bFormatFill = 1;
    }
    else {
      $bFormatFill = 0;
    }

    // Font
    if ($conditional
      ->getStyle()
      ->getFont()
      ->getName() != null || $conditional
      ->getStyle()
      ->getFont()
      ->getSize() != null || $conditional
      ->getStyle()
      ->getFont()
      ->getBold() != null || $conditional
      ->getStyle()
      ->getFont()
      ->getItalic() != null || $conditional
      ->getStyle()
      ->getFont()
      ->getSuperScript() != null || $conditional
      ->getStyle()
      ->getFont()
      ->getSubScript() != null || $conditional
      ->getStyle()
      ->getFont()
      ->getUnderline() != null || $conditional
      ->getStyle()
      ->getFont()
      ->getStrikethrough() != null || $conditional
      ->getStyle()
      ->getFont()
      ->getColor()
      ->getARGB() != null) {
      $bFormatFont = 1;
    }
    else {
      $bFormatFont = 0;
    }

    // Alignment
    $flags = 0;
    $flags |= 1 == $bAlignHz ? 0x1 : 0;
    $flags |= 1 == $bAlignVt ? 0x2 : 0;
    $flags |= 1 == $bAlignWrapTx ? 0x4 : 0;
    $flags |= 1 == $bTxRotation ? 0x8 : 0;

    // Justify last line flag
    $flags |= 1 == 1 ? 0x10 : 0;
    $flags |= 1 == $bIndent ? 0x20 : 0;
    $flags |= 1 == $bShrinkToFit ? 0x40 : 0;

    // Default
    $flags |= 1 == 1 ? 0x80 : 0;

    // Protection
    $flags |= 1 == $bProtLocked ? 0x100 : 0;
    $flags |= 1 == $bProtHidden ? 0x200 : 0;

    // Border
    $flags |= 1 == $bBorderLeft ? 0x400 : 0;
    $flags |= 1 == $bBorderRight ? 0x800 : 0;
    $flags |= 1 == $bBorderTop ? 0x1000 : 0;
    $flags |= 1 == $bBorderBottom ? 0x2000 : 0;
    $flags |= 1 == 1 ? 0x4000 : 0;

    // Top left to Bottom right border
    $flags |= 1 == 1 ? 0x8000 : 0;

    // Bottom left to Top right border
    // Pattern
    $flags |= 1 == $bFillStyle ? 0x10000 : 0;
    $flags |= 1 == $bFillColor ? 0x20000 : 0;
    $flags |= 1 == $bFillColorBg ? 0x40000 : 0;
    $flags |= 1 == 1 ? 0x380000 : 0;

    // Font
    $flags |= 1 == $bFormatFont ? 0x4000000 : 0;

    // Alignment :
    $flags |= 1 == $bFormatAlign ? 0x8000000 : 0;

    // Border
    $flags |= 1 == $bFormatBorder ? 0x10000000 : 0;

    // Pattern
    $flags |= 1 == $bFormatFill ? 0x20000000 : 0;

    // Protection
    $flags |= 1 == $bFormatProt ? 0x40000000 : 0;

    // Text direction
    $flags |= 1 == 0 ? 0x80000000 : 0;

    // Data Blocks
    if ($bFormatFont == 1) {

      // Font Name
      if ($conditional
        ->getStyle()
        ->getFont()
        ->getName() == null) {
        $dataBlockFont = pack('VVVVVVVV', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0);
        $dataBlockFont .= pack('VVVVVVVV', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0);
      }
      else {
        $dataBlockFont = PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($conditional
          ->getStyle()
          ->getFont()
          ->getName());
      }

      // Font Size
      if ($conditional
        ->getStyle()
        ->getFont()
        ->getSize() == null) {
        $dataBlockFont .= pack('V', 20 * 11);
      }
      else {
        $dataBlockFont .= pack('V', 20 * $conditional
          ->getStyle()
          ->getFont()
          ->getSize());
      }

      // Font Options
      $dataBlockFont .= pack('V', 0);

      // Font weight
      if ($conditional
        ->getStyle()
        ->getFont()
        ->getBold() == true) {
        $dataBlockFont .= pack('v', 0x2bc);
      }
      else {
        $dataBlockFont .= pack('v', 0x190);
      }

      // Escapement type
      if ($conditional
        ->getStyle()
        ->getFont()
        ->getSubScript() == true) {
        $dataBlockFont .= pack('v', 0x2);
        $fontEscapement = 0;
      }
      else {
        if ($conditional
          ->getStyle()
          ->getFont()
          ->getSuperScript() == true) {
          $dataBlockFont .= pack('v', 0x1);
          $fontEscapement = 0;
        }
        else {
          $dataBlockFont .= pack('v', 0x0);
          $fontEscapement = 1;
        }
      }

      // Underline type
      switch ($conditional
        ->getStyle()
        ->getFont()
        ->getUnderline()) {
        case PHPExcel_Style_Font::UNDERLINE_NONE:
          $dataBlockFont .= pack('C', 0x0);
          $fontUnderline = 0;
          break;
        case PHPExcel_Style_Font::UNDERLINE_DOUBLE:
          $dataBlockFont .= pack('C', 0x2);
          $fontUnderline = 0;
          break;
        case PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING:
          $dataBlockFont .= pack('C', 0x22);
          $fontUnderline = 0;
          break;
        case PHPExcel_Style_Font::UNDERLINE_SINGLE:
          $dataBlockFont .= pack('C', 0x1);
          $fontUnderline = 0;
          break;
        case PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING:
          $dataBlockFont .= pack('C', 0x21);
          $fontUnderline = 0;
          break;
        default:
          $dataBlockFont .= pack('C', 0x0);
          $fontUnderline = 1;
          break;
      }

      // Not used (3)
      $dataBlockFont .= pack('vC', 0x0, 0x0);

      // Font color index
      switch ($conditional
        ->getStyle()
        ->getFont()
        ->getColor()
        ->getRGB()) {
        case '000000':
          $colorIdx = 0x8;
          break;
        case 'FFFFFF':
          $colorIdx = 0x9;
          break;
        case 'FF0000':
          $colorIdx = 0xa;
          break;
        case '00FF00':
          $colorIdx = 0xb;
          break;
        case '0000FF':
          $colorIdx = 0xc;
          break;
        case 'FFFF00':
          $colorIdx = 0xd;
          break;
        case 'FF00FF':
          $colorIdx = 0xe;
          break;
        case '00FFFF':
          $colorIdx = 0xf;
          break;
        case '800000':
          $colorIdx = 0x10;
          break;
        case '008000':
          $colorIdx = 0x11;
          break;
        case '000080':
          $colorIdx = 0x12;
          break;
        case '808000':
          $colorIdx = 0x13;
          break;
        case '800080':
          $colorIdx = 0x14;
          break;
        case '008080':
          $colorIdx = 0x15;
          break;
        case 'C0C0C0':
          $colorIdx = 0x16;
          break;
        case '808080':
          $colorIdx = 0x17;
          break;
        case '9999FF':
          $colorIdx = 0x18;
          break;
        case '993366':
          $colorIdx = 0x19;
          break;
        case 'FFFFCC':
          $colorIdx = 0x1a;
          break;
        case 'CCFFFF':
          $colorIdx = 0x1b;
          break;
        case '660066':
          $colorIdx = 0x1c;
          break;
        case 'FF8080':
          $colorIdx = 0x1d;
          break;
        case '0066CC':
          $colorIdx = 0x1e;
          break;
        case 'CCCCFF':
          $colorIdx = 0x1f;
          break;
        case '000080':
          $colorIdx = 0x20;
          break;
        case 'FF00FF':
          $colorIdx = 0x21;
          break;
        case 'FFFF00':
          $colorIdx = 0x22;
          break;
        case '00FFFF':
          $colorIdx = 0x23;
          break;
        case '800080':
          $colorIdx = 0x24;
          break;
        case '800000':
          $colorIdx = 0x25;
          break;
        case '008080':
          $colorIdx = 0x26;
          break;
        case '0000FF':
          $colorIdx = 0x27;
          break;
        case '00CCFF':
          $colorIdx = 0x28;
          break;
        case 'CCFFFF':
          $colorIdx = 0x29;
          break;
        case 'CCFFCC':
          $colorIdx = 0x2a;
          break;
        case 'FFFF99':
          $colorIdx = 0x2b;
          break;
        case '99CCFF':
          $colorIdx = 0x2c;
          break;
        case 'FF99CC':
          $colorIdx = 0x2d;
          break;
        case 'CC99FF':
          $colorIdx = 0x2e;
          break;
        case 'FFCC99':
          $colorIdx = 0x2f;
          break;
        case '3366FF':
          $colorIdx = 0x30;
          break;
        case '33CCCC':
          $colorIdx = 0x31;
          break;
        case '99CC00':
          $colorIdx = 0x32;
          break;
        case 'FFCC00':
          $colorIdx = 0x33;
          break;
        case 'FF9900':
          $colorIdx = 0x34;
          break;
        case 'FF6600':
          $colorIdx = 0x35;
          break;
        case '666699':
          $colorIdx = 0x36;
          break;
        case '969696':
          $colorIdx = 0x37;
          break;
        case '003366':
          $colorIdx = 0x38;
          break;
        case '339966':
          $colorIdx = 0x39;
          break;
        case '003300':
          $colorIdx = 0x3a;
          break;
        case '333300':
          $colorIdx = 0x3b;
          break;
        case '993300':
          $colorIdx = 0x3c;
          break;
        case '993366':
          $colorIdx = 0x3d;
          break;
        case '333399':
          $colorIdx = 0x3e;
          break;
        case '333333':
          $colorIdx = 0x3f;
          break;
        default:
          $colorIdx = 0x0;
          break;
      }
      $dataBlockFont .= pack('V', $colorIdx);

      // Not used (4)
      $dataBlockFont .= pack('V', 0x0);

      // Options flags for modified font attributes
      $optionsFlags = 0;
      $optionsFlagsBold = $conditional
        ->getStyle()
        ->getFont()
        ->getBold() == null ? 1 : 0;
      $optionsFlags |= 1 == $optionsFlagsBold ? 0x2 : 0;
      $optionsFlags |= 1 == 1 ? 0x8 : 0;
      $optionsFlags |= 1 == 1 ? 0x10 : 0;
      $optionsFlags |= 1 == 0 ? 0x20 : 0;
      $optionsFlags |= 1 == 1 ? 0x80 : 0;
      $dataBlockFont .= pack('V', $optionsFlags);

      // Escapement type
      $dataBlockFont .= pack('V', $fontEscapement);

      // Underline type
      $dataBlockFont .= pack('V', $fontUnderline);

      // Always
      $dataBlockFont .= pack('V', 0x0);

      // Always
      $dataBlockFont .= pack('V', 0x0);

      // Not used (8)
      $dataBlockFont .= pack('VV', 0x0, 0x0);

      // Always
      $dataBlockFont .= pack('v', 0x1);
    }
    if ($bFormatAlign == 1) {
      $blockAlign = 0;

      // Alignment and text break
      switch ($conditional
        ->getStyle()
        ->getAlignment()
        ->getHorizontal()) {
        case PHPExcel_Style_Alignment::HORIZONTAL_GENERAL:
          $blockAlign = 0;
          break;
        case PHPExcel_Style_Alignment::HORIZONTAL_LEFT:
          $blockAlign = 1;
          break;
        case PHPExcel_Style_Alignment::HORIZONTAL_RIGHT:
          $blockAlign = 3;
          break;
        case PHPExcel_Style_Alignment::HORIZONTAL_CENTER:
          $blockAlign = 2;
          break;
        case PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS:
          $blockAlign = 6;
          break;
        case PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY:
          $blockAlign = 5;
          break;
      }
      if ($conditional
        ->getStyle()
        ->getAlignment()
        ->getWrapText() == true) {
        $blockAlign |= 1 << 3;
      }
      else {
        $blockAlign |= 0 << 3;
      }
      switch ($conditional
        ->getStyle()
        ->getAlignment()
        ->getVertical()) {
        case PHPExcel_Style_Alignment::VERTICAL_BOTTOM:
          $blockAlign = 2 << 4;
          break;
        case PHPExcel_Style_Alignment::VERTICAL_TOP:
          $blockAlign = 0 << 4;
          break;
        case PHPExcel_Style_Alignment::VERTICAL_CENTER:
          $blockAlign = 1 << 4;
          break;
        case PHPExcel_Style_Alignment::VERTICAL_JUSTIFY:
          $blockAlign = 3 << 4;
          break;
      }
      $blockAlign |= 0 << 7;

      // Text rotation angle
      $blockRotation = $conditional
        ->getStyle()
        ->getAlignment()
        ->getTextRotation();

      // Indentation
      $blockIndent = $conditional
        ->getStyle()
        ->getAlignment()
        ->getIndent();
      if ($conditional
        ->getStyle()
        ->getAlignment()
        ->getShrinkToFit() == true) {
        $blockIndent |= 1 << 4;
      }
      else {
        $blockIndent |= 0 << 4;
      }
      $blockIndent |= 0 << 6;

      // Relative indentation
      $blockIndentRelative = 255;
      $dataBlockAlign = pack('CCvvv', $blockAlign, $blockRotation, $blockIndent, $blockIndentRelative, 0x0);
    }
    if ($bFormatBorder == 1) {
      $blockLineStyle = 0;
      switch ($conditional
        ->getStyle()
        ->getBorders()
        ->getLeft()
        ->getBorderStyle()) {
        case PHPExcel_Style_Border::BORDER_NONE:
          $blockLineStyle |= 0x0;
          break;
        case PHPExcel_Style_Border::BORDER_THIN:
          $blockLineStyle |= 0x1;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUM:
          $blockLineStyle |= 0x2;
          break;
        case PHPExcel_Style_Border::BORDER_DASHED:
          $blockLineStyle |= 0x3;
          break;
        case PHPExcel_Style_Border::BORDER_DOTTED:
          $blockLineStyle |= 0x4;
          break;
        case PHPExcel_Style_Border::BORDER_THICK:
          $blockLineStyle |= 0x5;
          break;
        case PHPExcel_Style_Border::BORDER_DOUBLE:
          $blockLineStyle |= 0x6;
          break;
        case PHPExcel_Style_Border::BORDER_HAIR:
          $blockLineStyle |= 0x7;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHED:
          $blockLineStyle |= 0x8;
          break;
        case PHPExcel_Style_Border::BORDER_DASHDOT:
          $blockLineStyle |= 0x9;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT:
          $blockLineStyle |= 0xa;
          break;
        case PHPExcel_Style_Border::BORDER_DASHDOTDOT:
          $blockLineStyle |= 0xb;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT:
          $blockLineStyle |= 0xc;
          break;
        case PHPExcel_Style_Border::BORDER_SLANTDASHDOT:
          $blockLineStyle |= 0xd;
          break;
      }
      switch ($conditional
        ->getStyle()
        ->getBorders()
        ->getRight()
        ->getBorderStyle()) {
        case PHPExcel_Style_Border::BORDER_NONE:
          $blockLineStyle |= 0x0 << 4;
          break;
        case PHPExcel_Style_Border::BORDER_THIN:
          $blockLineStyle |= 0x1 << 4;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUM:
          $blockLineStyle |= 0x2 << 4;
          break;
        case PHPExcel_Style_Border::BORDER_DASHED:
          $blockLineStyle |= 0x3 << 4;
          break;
        case PHPExcel_Style_Border::BORDER_DOTTED:
          $blockLineStyle |= 0x4 << 4;
          break;
        case PHPExcel_Style_Border::BORDER_THICK:
          $blockLineStyle |= 0x5 << 4;
          break;
        case PHPExcel_Style_Border::BORDER_DOUBLE:
          $blockLineStyle |= 0x6 << 4;
          break;
        case PHPExcel_Style_Border::BORDER_HAIR:
          $blockLineStyle |= 0x7 << 4;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHED:
          $blockLineStyle |= 0x8 << 4;
          break;
        case PHPExcel_Style_Border::BORDER_DASHDOT:
          $blockLineStyle |= 0x9 << 4;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT:
          $blockLineStyle |= 0xa << 4;
          break;
        case PHPExcel_Style_Border::BORDER_DASHDOTDOT:
          $blockLineStyle |= 0xb << 4;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT:
          $blockLineStyle |= 0xc << 4;
          break;
        case PHPExcel_Style_Border::BORDER_SLANTDASHDOT:
          $blockLineStyle |= 0xd << 4;
          break;
      }
      switch ($conditional
        ->getStyle()
        ->getBorders()
        ->getTop()
        ->getBorderStyle()) {
        case PHPExcel_Style_Border::BORDER_NONE:
          $blockLineStyle |= 0x0 << 8;
          break;
        case PHPExcel_Style_Border::BORDER_THIN:
          $blockLineStyle |= 0x1 << 8;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUM:
          $blockLineStyle |= 0x2 << 8;
          break;
        case PHPExcel_Style_Border::BORDER_DASHED:
          $blockLineStyle |= 0x3 << 8;
          break;
        case PHPExcel_Style_Border::BORDER_DOTTED:
          $blockLineStyle |= 0x4 << 8;
          break;
        case PHPExcel_Style_Border::BORDER_THICK:
          $blockLineStyle |= 0x5 << 8;
          break;
        case PHPExcel_Style_Border::BORDER_DOUBLE:
          $blockLineStyle |= 0x6 << 8;
          break;
        case PHPExcel_Style_Border::BORDER_HAIR:
          $blockLineStyle |= 0x7 << 8;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHED:
          $blockLineStyle |= 0x8 << 8;
          break;
        case PHPExcel_Style_Border::BORDER_DASHDOT:
          $blockLineStyle |= 0x9 << 8;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT:
          $blockLineStyle |= 0xa << 8;
          break;
        case PHPExcel_Style_Border::BORDER_DASHDOTDOT:
          $blockLineStyle |= 0xb << 8;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT:
          $blockLineStyle |= 0xc << 8;
          break;
        case PHPExcel_Style_Border::BORDER_SLANTDASHDOT:
          $blockLineStyle |= 0xd << 8;
          break;
      }
      switch ($conditional
        ->getStyle()
        ->getBorders()
        ->getBottom()
        ->getBorderStyle()) {
        case PHPExcel_Style_Border::BORDER_NONE:
          $blockLineStyle |= 0x0 << 12;
          break;
        case PHPExcel_Style_Border::BORDER_THIN:
          $blockLineStyle |= 0x1 << 12;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUM:
          $blockLineStyle |= 0x2 << 12;
          break;
        case PHPExcel_Style_Border::BORDER_DASHED:
          $blockLineStyle |= 0x3 << 12;
          break;
        case PHPExcel_Style_Border::BORDER_DOTTED:
          $blockLineStyle |= 0x4 << 12;
          break;
        case PHPExcel_Style_Border::BORDER_THICK:
          $blockLineStyle |= 0x5 << 12;
          break;
        case PHPExcel_Style_Border::BORDER_DOUBLE:
          $blockLineStyle |= 0x6 << 12;
          break;
        case PHPExcel_Style_Border::BORDER_HAIR:
          $blockLineStyle |= 0x7 << 12;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHED:
          $blockLineStyle |= 0x8 << 12;
          break;
        case PHPExcel_Style_Border::BORDER_DASHDOT:
          $blockLineStyle |= 0x9 << 12;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT:
          $blockLineStyle |= 0xa << 12;
          break;
        case PHPExcel_Style_Border::BORDER_DASHDOTDOT:
          $blockLineStyle |= 0xb << 12;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT:
          $blockLineStyle |= 0xc << 12;
          break;
        case PHPExcel_Style_Border::BORDER_SLANTDASHDOT:
          $blockLineStyle |= 0xd << 12;
          break;
      }

      //@todo _writeCFRule() => $blockLineStyle => Index Color for left line

      //@todo _writeCFRule() => $blockLineStyle => Index Color for right line

      //@todo _writeCFRule() => $blockLineStyle => Top-left to bottom-right on/off

      //@todo _writeCFRule() => $blockLineStyle => Bottom-left to top-right on/off
      $blockColor = 0;

      //@todo _writeCFRule() => $blockColor => Index Color for top line

      //@todo _writeCFRule() => $blockColor => Index Color for bottom line

      //@todo _writeCFRule() => $blockColor => Index Color for diagonal line
      switch ($conditional
        ->getStyle()
        ->getBorders()
        ->getDiagonal()
        ->getBorderStyle()) {
        case PHPExcel_Style_Border::BORDER_NONE:
          $blockColor |= 0x0 << 21;
          break;
        case PHPExcel_Style_Border::BORDER_THIN:
          $blockColor |= 0x1 << 21;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUM:
          $blockColor |= 0x2 << 21;
          break;
        case PHPExcel_Style_Border::BORDER_DASHED:
          $blockColor |= 0x3 << 21;
          break;
        case PHPExcel_Style_Border::BORDER_DOTTED:
          $blockColor |= 0x4 << 21;
          break;
        case PHPExcel_Style_Border::BORDER_THICK:
          $blockColor |= 0x5 << 21;
          break;
        case PHPExcel_Style_Border::BORDER_DOUBLE:
          $blockColor |= 0x6 << 21;
          break;
        case PHPExcel_Style_Border::BORDER_HAIR:
          $blockColor |= 0x7 << 21;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHED:
          $blockColor |= 0x8 << 21;
          break;
        case PHPExcel_Style_Border::BORDER_DASHDOT:
          $blockColor |= 0x9 << 21;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT:
          $blockColor |= 0xa << 21;
          break;
        case PHPExcel_Style_Border::BORDER_DASHDOTDOT:
          $blockColor |= 0xb << 21;
          break;
        case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT:
          $blockColor |= 0xc << 21;
          break;
        case PHPExcel_Style_Border::BORDER_SLANTDASHDOT:
          $blockColor |= 0xd << 21;
          break;
      }
      $dataBlockBorder = pack('vv', $blockLineStyle, $blockColor);
    }
    if ($bFormatFill == 1) {

      // Fill Patern Style
      $blockFillPatternStyle = 0;
      switch ($conditional
        ->getStyle()
        ->getFill()
        ->getFillType()) {
        case PHPExcel_Style_Fill::FILL_NONE:
          $blockFillPatternStyle = 0x0;
          break;
        case PHPExcel_Style_Fill::FILL_SOLID:
          $blockFillPatternStyle = 0x1;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_MEDIUMGRAY:
          $blockFillPatternStyle = 0x2;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_DARKGRAY:
          $blockFillPatternStyle = 0x3;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRAY:
          $blockFillPatternStyle = 0x4;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_DARKHORIZONTAL:
          $blockFillPatternStyle = 0x5;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_DARKVERTICAL:
          $blockFillPatternStyle = 0x6;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_DARKDOWN:
          $blockFillPatternStyle = 0x7;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_DARKUP:
          $blockFillPatternStyle = 0x8;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_DARKGRID:
          $blockFillPatternStyle = 0x9;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_DARKTRELLIS:
          $blockFillPatternStyle = 0xa;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTHORIZONTAL:
          $blockFillPatternStyle = 0xb;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTVERTICAL:
          $blockFillPatternStyle = 0xc;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTDOWN:
          $blockFillPatternStyle = 0xd;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTUP:
          $blockFillPatternStyle = 0xe;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRID:
          $blockFillPatternStyle = 0xf;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_LIGHTTRELLIS:
          $blockFillPatternStyle = 0x10;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_GRAY125:
          $blockFillPatternStyle = 0x11;
          break;
        case PHPExcel_Style_Fill::FILL_PATTERN_GRAY0625:
          $blockFillPatternStyle = 0x12;
          break;
        case PHPExcel_Style_Fill::FILL_GRADIENT_LINEAR:
          $blockFillPatternStyle = 0x0;
          break;

        // does not exist in BIFF8
        case PHPExcel_Style_Fill::FILL_GRADIENT_PATH:
          $blockFillPatternStyle = 0x0;
          break;

        // does not exist in BIFF8
        default:
          $blockFillPatternStyle = 0x0;
          break;
      }

      // Color
      switch ($conditional
        ->getStyle()
        ->getFill()
        ->getStartColor()
        ->getRGB()) {
        case '000000':
          $colorIdxBg = 0x8;
          break;
        case 'FFFFFF':
          $colorIdxBg = 0x9;
          break;
        case 'FF0000':
          $colorIdxBg = 0xa;
          break;
        case '00FF00':
          $colorIdxBg = 0xb;
          break;
        case '0000FF':
          $colorIdxBg = 0xc;
          break;
        case 'FFFF00':
          $colorIdxBg = 0xd;
          break;
        case 'FF00FF':
          $colorIdxBg = 0xe;
          break;
        case '00FFFF':
          $colorIdxBg = 0xf;
          break;
        case '800000':
          $colorIdxBg = 0x10;
          break;
        case '008000':
          $colorIdxBg = 0x11;
          break;
        case '000080':
          $colorIdxBg = 0x12;
          break;
        case '808000':
          $colorIdxBg = 0x13;
          break;
        case '800080':
          $colorIdxBg = 0x14;
          break;
        case '008080':
          $colorIdxBg = 0x15;
          break;
        case 'C0C0C0':
          $colorIdxBg = 0x16;
          break;
        case '808080':
          $colorIdxBg = 0x17;
          break;
        case '9999FF':
          $colorIdxBg = 0x18;
          break;
        case '993366':
          $colorIdxBg = 0x19;
          break;
        case 'FFFFCC':
          $colorIdxBg = 0x1a;
          break;
        case 'CCFFFF':
          $colorIdxBg = 0x1b;
          break;
        case '660066':
          $colorIdxBg = 0x1c;
          break;
        case 'FF8080':
          $colorIdxBg = 0x1d;
          break;
        case '0066CC':
          $colorIdxBg = 0x1e;
          break;
        case 'CCCCFF':
          $colorIdxBg = 0x1f;
          break;
        case '000080':
          $colorIdxBg = 0x20;
          break;
        case 'FF00FF':
          $colorIdxBg = 0x21;
          break;
        case 'FFFF00':
          $colorIdxBg = 0x22;
          break;
        case '00FFFF':
          $colorIdxBg = 0x23;
          break;
        case '800080':
          $colorIdxBg = 0x24;
          break;
        case '800000':
          $colorIdxBg = 0x25;
          break;
        case '008080':
          $colorIdxBg = 0x26;
          break;
        case '0000FF':
          $colorIdxBg = 0x27;
          break;
        case '00CCFF':
          $colorIdxBg = 0x28;
          break;
        case 'CCFFFF':
          $colorIdxBg = 0x29;
          break;
        case 'CCFFCC':
          $colorIdxBg = 0x2a;
          break;
        case 'FFFF99':
          $colorIdxBg = 0x2b;
          break;
        case '99CCFF':
          $colorIdxBg = 0x2c;
          break;
        case 'FF99CC':
          $colorIdxBg = 0x2d;
          break;
        case 'CC99FF':
          $colorIdxBg = 0x2e;
          break;
        case 'FFCC99':
          $colorIdxBg = 0x2f;
          break;
        case '3366FF':
          $colorIdxBg = 0x30;
          break;
        case '33CCCC':
          $colorIdxBg = 0x31;
          break;
        case '99CC00':
          $colorIdxBg = 0x32;
          break;
        case 'FFCC00':
          $colorIdxBg = 0x33;
          break;
        case 'FF9900':
          $colorIdxBg = 0x34;
          break;
        case 'FF6600':
          $colorIdxBg = 0x35;
          break;
        case '666699':
          $colorIdxBg = 0x36;
          break;
        case '969696':
          $colorIdxBg = 0x37;
          break;
        case '003366':
          $colorIdxBg = 0x38;
          break;
        case '339966':
          $colorIdxBg = 0x39;
          break;
        case '003300':
          $colorIdxBg = 0x3a;
          break;
        case '333300':
          $colorIdxBg = 0x3b;
          break;
        case '993300':
          $colorIdxBg = 0x3c;
          break;
        case '993366':
          $colorIdxBg = 0x3d;
          break;
        case '333399':
          $colorIdxBg = 0x3e;
          break;
        case '333333':
          $colorIdxBg = 0x3f;
          break;
        default:
          $colorIdxBg = 0x41;
          break;
      }

      // Fg Color
      switch ($conditional
        ->getStyle()
        ->getFill()
        ->getEndColor()
        ->getRGB()) {
        case '000000':
          $colorIdxFg = 0x8;
          break;
        case 'FFFFFF':
          $colorIdxFg = 0x9;
          break;
        case 'FF0000':
          $colorIdxFg = 0xa;
          break;
        case '00FF00':
          $colorIdxFg = 0xb;
          break;
        case '0000FF':
          $colorIdxFg = 0xc;
          break;
        case 'FFFF00':
          $colorIdxFg = 0xd;
          break;
        case 'FF00FF':
          $colorIdxFg = 0xe;
          break;
        case '00FFFF':
          $colorIdxFg = 0xf;
          break;
        case '800000':
          $colorIdxFg = 0x10;
          break;
        case '008000':
          $colorIdxFg = 0x11;
          break;
        case '000080':
          $colorIdxFg = 0x12;
          break;
        case '808000':
          $colorIdxFg = 0x13;
          break;
        case '800080':
          $colorIdxFg = 0x14;
          break;
        case '008080':
          $colorIdxFg = 0x15;
          break;
        case 'C0C0C0':
          $colorIdxFg = 0x16;
          break;
        case '808080':
          $colorIdxFg = 0x17;
          break;
        case '9999FF':
          $colorIdxFg = 0x18;
          break;
        case '993366':
          $colorIdxFg = 0x19;
          break;
        case 'FFFFCC':
          $colorIdxFg = 0x1a;
          break;
        case 'CCFFFF':
          $colorIdxFg = 0x1b;
          break;
        case '660066':
          $colorIdxFg = 0x1c;
          break;
        case 'FF8080':
          $colorIdxFg = 0x1d;
          break;
        case '0066CC':
          $colorIdxFg = 0x1e;
          break;
        case 'CCCCFF':
          $colorIdxFg = 0x1f;
          break;
        case '000080':
          $colorIdxFg = 0x20;
          break;
        case 'FF00FF':
          $colorIdxFg = 0x21;
          break;
        case 'FFFF00':
          $colorIdxFg = 0x22;
          break;
        case '00FFFF':
          $colorIdxFg = 0x23;
          break;
        case '800080':
          $colorIdxFg = 0x24;
          break;
        case '800000':
          $colorIdxFg = 0x25;
          break;
        case '008080':
          $colorIdxFg = 0x26;
          break;
        case '0000FF':
          $colorIdxFg = 0x27;
          break;
        case '00CCFF':
          $colorIdxFg = 0x28;
          break;
        case 'CCFFFF':
          $colorIdxFg = 0x29;
          break;
        case 'CCFFCC':
          $colorIdxFg = 0x2a;
          break;
        case 'FFFF99':
          $colorIdxFg = 0x2b;
          break;
        case '99CCFF':
          $colorIdxFg = 0x2c;
          break;
        case 'FF99CC':
          $colorIdxFg = 0x2d;
          break;
        case 'CC99FF':
          $colorIdxFg = 0x2e;
          break;
        case 'FFCC99':
          $colorIdxFg = 0x2f;
          break;
        case '3366FF':
          $colorIdxFg = 0x30;
          break;
        case '33CCCC':
          $colorIdxFg = 0x31;
          break;
        case '99CC00':
          $colorIdxFg = 0x32;
          break;
        case 'FFCC00':
          $colorIdxFg = 0x33;
          break;
        case 'FF9900':
          $colorIdxFg = 0x34;
          break;
        case 'FF6600':
          $colorIdxFg = 0x35;
          break;
        case '666699':
          $colorIdxFg = 0x36;
          break;
        case '969696':
          $colorIdxFg = 0x37;
          break;
        case '003366':
          $colorIdxFg = 0x38;
          break;
        case '339966':
          $colorIdxFg = 0x39;
          break;
        case '003300':
          $colorIdxFg = 0x3a;
          break;
        case '333300':
          $colorIdxFg = 0x3b;
          break;
        case '993300':
          $colorIdxFg = 0x3c;
          break;
        case '993366':
          $colorIdxFg = 0x3d;
          break;
        case '333399':
          $colorIdxFg = 0x3e;
          break;
        case '333333':
          $colorIdxFg = 0x3f;
          break;
        default:
          $colorIdxFg = 0x40;
          break;
      }
      $dataBlockFill = pack('v', $blockFillPatternStyle);
      $dataBlockFill .= pack('v', $colorIdxFg | $colorIdxBg << 7);
    }
    if ($bFormatProt == 1) {
      $dataBlockProtection = 0;
      if ($conditional
        ->getStyle()
        ->getProtection()
        ->getLocked() == PHPExcel_Style_Protection::PROTECTION_PROTECTED) {
        $dataBlockProtection = 1;
      }
      if ($conditional
        ->getStyle()
        ->getProtection()
        ->getHidden() == PHPExcel_Style_Protection::PROTECTION_PROTECTED) {
        $dataBlockProtection = 1 << 1;
      }
    }
    $data = pack('CCvvVv', $type, $operatorType, $szValue1, $szValue2, $flags, 0x0);
    if ($bFormatFont == 1) {

      // Block Formatting : OK
      $data .= $dataBlockFont;
    }
    if ($bFormatAlign == 1) {
      $data .= $dataBlockAlign;
    }
    if ($bFormatBorder == 1) {
      $data .= $dataBlockBorder;
    }
    if ($bFormatFill == 1) {

      // Block Formatting : OK
      $data .= $dataBlockFill;
    }
    if ($bFormatProt == 1) {
      $data .= $dataBlockProtection;
    }
    if (!is_null($operand1)) {
      $data .= $operand1;
    }
    if (!is_null($operand2)) {
      $data .= $operand2;
    }
    $header = pack('vv', $record, strlen($data));
    $this
      ->_append($header . $data);
  }

  /**
   * Write CFHeader record
   */
  private function _writeCFHeader() {
    $record = 0x1b0;

    // Record identifier
    $length = 0x16;

    // Bytes to follow
    $numColumnMin = null;
    $numColumnMax = null;
    $numRowMin = null;
    $numRowMax = null;
    $arrConditional = array();
    foreach ($this->_phpSheet
      ->getConditionalStylesCollection() as $cellCoordinate => $conditionalStyles) {
      foreach ($conditionalStyles as $conditional) {
        if ($conditional
          ->getConditionType() == PHPExcel_Style_Conditional::CONDITION_EXPRESSION || $conditional
          ->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CELLIS) {
          if (!in_array($conditional
            ->getHashCode(), $arrConditional)) {
            $arrConditional[] = $conditional
              ->getHashCode();
          }

          // Cells
          $arrCoord = PHPExcel_Cell::coordinateFromString($cellCoordinate);
          if (!is_numeric($arrCoord[0])) {
            $arrCoord[0] = PHPExcel_Cell::columnIndexFromString($arrCoord[0]);
          }
          if (is_null($numColumnMin) || $numColumnMin > $arrCoord[0]) {
            $numColumnMin = $arrCoord[0];
          }
          if (is_null($numColumnMax) || $numColumnMax < $arrCoord[0]) {
            $numColumnMax = $arrCoord[0];
          }
          if (is_null($numRowMin) || $numRowMin > $arrCoord[1]) {
            $numRowMin = $arrCoord[1];
          }
          if (is_null($numRowMax) || $numRowMax < $arrCoord[1]) {
            $numRowMax = $arrCoord[1];
          }
        }
      }
    }
    $needRedraw = 1;
    $cellRange = pack('vvvv', $numRowMin - 1, $numRowMax - 1, $numColumnMin - 1, $numColumnMax - 1);
    $header = pack('vv', $record, $length);
    $data = pack('vv', count($arrConditional), $needRedraw);
    $data .= $cellRange;
    $data .= pack('v', 0x1);
    $data .= $cellRange;
    $this
      ->_append($header . $data);
  }

}

Classes

Namesort descending Description
PHPExcel_Writer_Excel5_Worksheet PHPExcel_Writer_Excel5_Worksheet