PHPExcel_Writer_Excel5
[ class tree: PHPExcel_Writer_Excel5 ] [ index: PHPExcel_Writer_Excel5 ] [ all elements ]

Source for file Worksheet.php

Documentation is available at Worksheet.php

  1. <?php
  2. /**
  3.  * PHPExcel
  4.  *
  5.  * Copyright (c) 2006 - 2010 PHPExcel
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  *
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  20.  *
  21.  * @category   PHPExcel
  22.  * @package    PHPExcel_Writer_Excel5
  23.  * @copyright  Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel)
  24.  * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
  25.  * @version    1.7.4, 2010-08-26
  26.  */
  27.  
  28. // Original file header of PEAR::Spreadsheet_Excel_Writer_Worksheet (used as the base for this class):
  29. // -----------------------------------------------------------------------------------------
  30. // /*
  31. // *  Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
  32. // *
  33. // *  The majority of this is _NOT_ my code.  I simply ported it from the
  34. // *  PERL Spreadsheet::WriteExcel module.
  35. // *
  36. // *  The author of the Spreadsheet::WriteExcel module is John McNamara
  37. // *  <jmcnamara@cpan.org>
  38. // *
  39. // *  I _DO_ maintain this code, and John McNamara has nothing to do with the
  40. // *  porting of this code to PHP.  Any questions directly related to this
  41. // *  class library should be directed to me.
  42. // *
  43. // *  License Information:
  44. // *
  45. // *    Spreadsheet_Excel_Writer:  A library for generating Excel Spreadsheets
  46. // *    Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
  47. // *
  48. // *    This library is free software; you can redistribute it and/or
  49. // *    modify it under the terms of the GNU Lesser General Public
  50. // *    License as published by the Free Software Foundation; either
  51. // *    version 2.1 of the License, or (at your option) any later version.
  52. // *
  53. // *    This library is distributed in the hope that it will be useful,
  54. // *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  55. // *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  56. // *    Lesser General Public License for more details.
  57. // *
  58. // *    You should have received a copy of the GNU Lesser General Public
  59. // *    License along with this library; if not, write to the Free Software
  60. // *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  61. // */
  62.  
  63.  
  64. /**
  65.  * PHPExcel_Writer_Excel5_Worksheet
  66.  *
  67.  * @category   PHPExcel
  68.  * @package    PHPExcel_Writer_Excel5
  69.  * @copyright  Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel)
  70.  */
  71. {
  72.     /**
  73.      * Formula parser
  74.      *
  75.      * @var PHPExcel_Writer_Excel5_Parser 
  76.      */
  77.     private $_parser;
  78.  
  79.     /**
  80.      * Maximum number of characters for a string (LABEL record in BIFF5)
  81.      * @var integer 
  82.      */
  83.     public $_xls_strmax;
  84.  
  85.     /**
  86.      * Array containing format information for columns
  87.      * @var array 
  88.      */
  89.     public $_colinfo;
  90.  
  91.     /**
  92.      * Array containing the selected area for the worksheet
  93.      * @var array 
  94.      */
  95.     public $_selection;
  96.  
  97.     /**
  98.      * The active pane for the worksheet
  99.      * @var integer 
  100.      */
  101.     public $_active_pane;
  102.  
  103.     /**
  104.      * Whether to use outline.
  105.      * @var integer 
  106.      */
  107.     public $_outline_on;
  108.  
  109.     /**
  110.      * Auto outline styles.
  111.      * @var bool 
  112.      */
  113.     public $_outline_style;
  114.  
  115.     /**
  116.      * Whether to have outline summary below.
  117.      * @var bool 
  118.      */
  119.     public $_outline_below;
  120.  
  121.     /**
  122.      * Whether to have outline summary at the right.
  123.      * @var bool 
  124.      */
  125.     public $_outline_right;
  126.  
  127.     /**
  128.      * Reference to the total number of strings in the workbook
  129.      * @var integer 
  130.      */
  131.     public $_str_total;
  132.  
  133.     /**
  134.      * Reference to the number of unique strings in the workbook
  135.      * @var integer 
  136.      */
  137.     public $_str_unique;
  138.  
  139.     /**
  140.      * Reference to the array containing all the unique strings in the workbook
  141.      * @var array 
  142.      */
  143.     public $_str_table;
  144.  
  145.     /**
  146.      * Color cache
  147.      */
  148.     private $_colors;
  149.  
  150.     /**
  151.      * Index of first used row (at least 0)
  152.      * @var int 
  153.      */
  154.     private $_firstRowIndex;
  155.  
  156.     /**
  157.      * Index of last used row. (no used rows means -1)
  158.      * @var int 
  159.      */
  160.     private $_lastRowIndex;
  161.  
  162.     /**
  163.      * Index of first used column (at least 0)
  164.      * @var int 
  165.      */
  166.     private $_firstColumnIndex;
  167.  
  168.     /**
  169.      * Index of last used column (no used columns means -1)
  170.      * @var int 
  171.      */
  172.     private $_lastColumnIndex;
  173.  
  174.     /**
  175.      * Sheet object
  176.      * @var PHPExcel_Worksheet 
  177.      */
  178.     private $_phpSheet;
  179.  
  180.     /**
  181.      * Count cell style Xfs
  182.      *
  183.      * @var int 
  184.      */
  185.     private $_countCellStyleXfs;
  186.  
  187.     /**
  188.      * Escher object corresponding to MSODRAWING
  189.      *
  190.      * @var PHPExcel_Shared_Escher 
  191.      */
  192.     private $_escher;
  193.  
  194.     /**
  195.      * Constructor
  196.      *
  197.      * @param int  $BIFF_version         BIFF version
  198.      * @param int  $str_total        Total number of strings
  199.      * @param int  $str_unique        Total number of unique strings
  200.      * @param array  $str_table 
  201.      * @param mixed   $parser      The formula parser created for the Workbook
  202.      * @param string   $tempDir      The temporary directory to be used
  203.      * @param PHPExcel_Worksheet $phpSheet 
  204.      */
  205.     public function __construct($BIFF_version,
  206.                                                 &$str_total,
  207.                                                 &$str_unique&$str_table&$colors,
  208.                                                 $parser$preCalculateFormulas$phpSheet)
  209.     {
  210.         // It needs to call its parent's constructor explicitly
  211.         parent::__construct();
  212.  
  213.         $this->_BIFF_version    = $BIFF_version;
  214.         if ($BIFF_version == 0x0600{
  215.             // change BIFFwriter limit for CONTINUE records
  216.             $this->_limit = 8224;
  217.         }
  218.  
  219.  
  220.         $this->_preCalculateFormulas $preCalculateFormulas;
  221.         $this->_str_total        = &$str_total;
  222.         $this->_str_unique        = &$str_unique;
  223.         $this->_str_table        = &$str_table;
  224.         $this->_colors            &$colors;
  225.         $this->_parser            $parser;
  226.  
  227.         $this->_phpSheet $phpSheet;
  228.  
  229.         //$this->ext_sheets        = array();
  230.         //$this->offset            = 0;
  231.         $this->_xls_strmax        = 255;
  232.         $this->_colinfo            = array();
  233.         $this->_selection        = array(0,0,0,0);
  234.         $this->_active_pane        = 3;
  235.  
  236.         $this->_print_headers        0;
  237.  
  238.         $this->_outline_style        = 0;
  239.         $this->_outline_below        = 1;
  240.         $this->_outline_right        = 1;
  241.         $this->_outline_on            = 1;
  242.  
  243.         // calculate values for DIMENSIONS record
  244.         $this->_firstRowIndex    =  0;
  245.         $this->_lastRowIndex     = -1;
  246.         $this->_firstColumnIndex =  0;
  247.         $this->_lastColumnIndex  = -1;
  248.  
  249.         foreach ($this->_phpSheet->getCellCollection(falseas $cellID{
  250.             preg_match('/^(\w+)(\d+)$/U',$cellID,$matches);
  251.             list(,$col,$row$matches;
  252.             $column PHPExcel_Cell::columnIndexFromString($col1;
  253.  
  254.             // Don't break Excel!
  255.             if ($row 65536 or $column 256{
  256.                 break;
  257.             }
  258.  
  259.             $this->_firstRowIndex    min($this->_firstRowIndex$row);
  260.             $this->_lastRowIndex     max($this->_lastRowIndex$row);
  261.             $this->_firstColumnIndex min($this->_firstColumnIndex$column);
  262.             $this->_lastColumnIndex  max($this->_lastColumnIndex$column);
  263.         }
  264.  
  265.         $this->_countCellStyleXfs count($phpSheet->getParent()->getCellStyleXfCollection());
  266.     }
  267.  
  268.     /**
  269.      * Add data to the beginning of the workbook (note the reverse order)
  270.      * and to the end of the workbook.
  271.      *
  272.      * @access public
  273.      * @see PHPExcel_Writer_Excel5_Workbook::storeWorkbook()
  274.      */
  275.     function close()
  276.     {
  277.         $num_sheets $this->_phpSheet->getParent()->getSheetCount();
  278.  
  279.         // Write BOF record
  280.         $this->_storeBof(0x0010);
  281.  
  282.         // Write PRINTHEADERS
  283.         $this->_writePrintHeaders();
  284.  
  285.         // Write PRINTGRIDLINES
  286.         $this->_writePrintGridlines();
  287.  
  288.         // Write GRIDSET
  289.         $this->_writeGridset();
  290.  
  291.         // Calculate column widths
  292.         $this->_phpSheet->calculateColumnWidths();
  293.  
  294.         // Column dimensions
  295.         $columnDimensions $this->_phpSheet->getColumnDimensions();
  296.         for ($i 0$i 256++$i{
  297.             $hidden 0;
  298.             $level 0;
  299.             $xfIndex 15// there are 15 cell style Xfs
  300.  
  301.             if ($this->_phpSheet->getDefaultColumnDimension()->getWidth(>= 0{
  302.                 $width $this->_phpSheet->getDefaultColumnDimension()->getWidth();
  303.             else {
  304.                 $width PHPExcel_Shared_Font::getDefaultColumnWidthByFont($this->_phpSheet->getParent()->getDefaultStyle()->getFont());
  305.             }
  306.  
  307.             $columnLetter PHPExcel_Cell::stringFromColumnIndex($i);
  308.             if (isset($columnDimensions[$columnLetter])) {
  309.                 $columnDimension $columnDimensions[$columnLetter];
  310.                 if ($columnDimension->getWidth(>= 0{
  311.                     $width $columnDimension->getWidth();
  312.                 }
  313.                 $hidden $columnDimension->getVisible(1;
  314.                 $level $columnDimension->getOutlineLevel();
  315.                 $xfIndex $columnDimension->getXfIndex(15// there are 15 cell style Xfs
  316.             }
  317.  
  318.             // Components of _colinfo:
  319.             // $firstcol first column on the range
  320.             // $lastcol  last column on the range
  321.             // $width    width to set
  322.             // $xfIndex  The optional cell style Xf index to apply to the columns
  323.             // $hidden   The optional hidden atribute
  324.             // $level    The optional outline level
  325.             $this->_colinfo[array($i$i$width$xfIndex$hidden$level);
  326.         }
  327.  
  328.         // Write GUTS
  329.         $this->_writeGuts();
  330.  
  331.         // Write DEFAULTROWHEIGHT
  332.         if ($this->_BIFF_version == 0x0600{
  333.             $this->_writeDefaultRowHeight();
  334.         }
  335.  
  336.         // Write WSBOOL
  337.         $this->_writeWsbool();
  338.  
  339.         // Write horizontal and vertical page breaks
  340.         $this->_writeBreaks();
  341.  
  342.         // Write page header
  343.         $this->_writeHeader();
  344.  
  345.         // Write page footer
  346.         $this->_writeFooter();
  347.  
  348.         // Write page horizontal centering
  349.         $this->_writeHcenter();
  350.  
  351.         // Write page vertical centering
  352.         $this->_writeVcenter();
  353.  
  354.         // Write left margin
  355.         $this->_writeMarginLeft();
  356.  
  357.         // Write right margin
  358.         $this->_writeMarginRight();
  359.  
  360.         // Write top margin
  361.         $this->_writeMarginTop();
  362.  
  363.         // Write bottom margin
  364.         $this->_writeMarginBottom();
  365.  
  366.         // Write page setup
  367.         $this->_writeSetup();
  368.  
  369.         // Write sheet protection
  370.         $this->_writeProtect();
  371.  
  372.         // Write SCENPROTECT
  373.         $this->_writeScenProtect();
  374.  
  375.         // Write OBJECTPROTECT
  376.         $this->_writeObjectProtect();
  377.  
  378.         // Write sheet password
  379.         $this->_writePassword();
  380.  
  381.         // Write DEFCOLWIDTH record
  382.         $this->_writeDefcol();
  383.  
  384.         // Write the COLINFO records if they exist
  385.         if (!empty($this->_colinfo)) {
  386.             $colcount count($this->_colinfo);
  387.             for ($i 0$i $colcount++$i{
  388.                 $this->_writeColinfo($this->_colinfo[$i]);
  389.             }
  390.         }
  391.  
  392.         // Write EXTERNCOUNT of external references
  393.         if ($this->_BIFF_version == 0x0500{
  394.             $this->_writeExterncount($num_sheets);
  395.         }
  396.  
  397.         // Write EXTERNSHEET references
  398.         if ($this->_BIFF_version == 0x0500{
  399.             for ($i 0$i $num_sheets++$i{
  400.                 $this->_writeExternsheet($this->_phpSheet->getParent()->getSheet($i)->getTitle());
  401.             }
  402.         }
  403.  
  404.         // Write sheet dimensions
  405.         $this->_writeDimensions();
  406.  
  407.         // Row dimensions
  408.         foreach ($this->_phpSheet->getRowDimensions(as $rowDimension{
  409.             $xfIndex $rowDimension->getXfIndex(15// there are 15 cellXfs
  410.             $this->_writeRow$rowDimension->getRowIndex(1$rowDimension->getRowHeight()$xfIndex($rowDimension->getVisible('0' '1')$rowDimension->getOutlineLevel() );
  411.         }
  412.  
  413.         // Write Cells
  414.         foreach ($this->_phpSheet->getCellCollection(as $cellID{
  415.             $cell $this->_phpSheet->getCell($cellID);
  416.             $row $cell->getRow(1;
  417.             $column PHPExcel_Cell::columnIndexFromString($cell->getColumn()) 1;
  418.  
  419.             // Don't break Excel!
  420.             if ($row 65536 or $column 256{
  421.                 break;
  422.             }
  423.  
  424.             // Write cell value
  425.             $xfIndex $cell->getXfIndex(15// there are 15 cell style Xfs
  426.  
  427.             if ($cell->getValue(instanceof PHPExcel_RichText{
  428.                 $this->_writeString($row$column$cell->getValue()->getPlainText()$xfIndex);
  429.             else {
  430.                 switch ($cell->getDatatype()) {
  431.  
  432.                 case PHPExcel_Cell_DataType::TYPE_STRING:
  433.                     if ($cell->getValue(=== '' or $cell->getValue(=== null{
  434.                         $this->_writeBlank($row$column$xfIndex);
  435.                     else {
  436.                         $this->_writeString($row$column$cell->getValue()$xfIndex);
  437.                     }
  438.                     break;
  439.  
  440.                 case PHPExcel_Cell_DataType::TYPE_FORMULA:
  441.                     $calculatedValue $this->_preCalculateFormulas ?
  442.                         $cell->getCalculatedValue(null;
  443.                     $this->_writeFormula($row$column$cell->getValue()$xfIndex$calculatedValue);
  444.                     break;
  445.  
  446.                 case PHPExcel_Cell_DataType::TYPE_BOOL:
  447.                     $this->_writeBoolErr($row$column$cell->getValue()0$xfIndex);
  448.                     break;
  449.  
  450.                 case PHPExcel_Cell_DataType::TYPE_ERROR:
  451.                     $this->_writeBoolErr($row$column$this->_mapErrorCode($cell->getValue())1$xfIndex);
  452.                     break;
  453.  
  454.                 case PHPExcel_Cell_DataType::TYPE_NUMERIC:
  455.                     $this->_writeNumber($row$column$cell->getValue()$xfIndex);
  456.                     break;
  457.                 }
  458.             }
  459.         }
  460.  
  461.         // Append
  462.         if ($this->_BIFF_version == 0x0600{
  463.             $this->_writeMsoDrawing();
  464.         }
  465.         $this->_writeWindow2();
  466.         $this->_writeZoom();
  467.         if ($this->_phpSheet->getFreezePane()) {
  468.             $this->_writePanes();
  469.         }
  470.         $this->_writeSelection();
  471.         $this->_writeMergedCells();
  472.  
  473.         // Hyperlinks
  474.         if ($this->_BIFF_version == 0x0600{
  475.             foreach ($this->_phpSheet->getHyperLinkCollection(as $coordinate => $hyperlink{
  476.                 list($column$rowPHPExcel_Cell::coordinateFromString($coordinate);
  477.  
  478.                 $url $hyperlink->getUrl();
  479.  
  480.                 if strpos($url'sheet://'!== false {
  481.                     // internal to current workbook
  482.                     $url str_replace('sheet://''internal:'$url);
  483.  
  484.                 else if preg_match('/^(http:|https:|ftp:|mailto:)/'$url) ) {
  485.                     // URL
  486.                     // $url = $url;
  487.  
  488.                 else {
  489.                     // external (local file)
  490.                     $url 'external:' $url;
  491.                 }
  492.  
  493.                 $this->_writeUrl($row 1PHPExcel_Cell::columnIndexFromString($column1$url);
  494.             }
  495.         }
  496.  
  497.         if ($this->_BIFF_version == 0x0600{
  498.             $this->_writeDataValidity();
  499.             $this->_writeSheetLayout();
  500.             $this->_writeSheetProtection();
  501.             $this->_writeRangeProtection();
  502.         }
  503.  
  504.         $this->_storeEof();
  505.     }
  506.  
  507.     /**
  508.      * Write a cell range address in BIFF8
  509.      * always fixed range
  510.      * See section 2.5.14 in OpenOffice.org's Documentation of the Microsoft Excel File Format
  511.      *
  512.      * @param string $range E.g. 'A1' or 'A1:B6'
  513.      * @return string Binary data
  514.      */
  515.     private function _writeBIFF8CellRangeAddressFixed($range 'A1')
  516.     {
  517.         $explodes explode(':'$range);
  518.  
  519.         // extract first cell, e.g. 'A1'
  520.         $firstCell $explodes[0];
  521.  
  522.         // extract last cell, e.g. 'B6'
  523.         if (count($explodes== 1{
  524.             $lastCell $firstCell;
  525.         else {
  526.             $lastCell $explodes[1];
  527.         }
  528.  
  529.         $firstCellCoordinates PHPExcel_Cell::coordinateFromString($firstCell)// e.g. array(0, 1)
  530.         $lastCellCoordinates  PHPExcel_Cell::coordinateFromString($lastCell);  // e.g. array(1, 6)
  531.  
  532.         $data pack('vvvv',
  533.             $firstCellCoordinates[11,
  534.             $lastCellCoordinates[11,
  535.             PHPExcel_Cell::columnIndexFromString($firstCellCoordinates[0]1,
  536.             PHPExcel_Cell::columnIndexFromString($lastCellCoordinates[0]1
  537.         );
  538.  
  539.         return $data;
  540.     }
  541.  
  542.     /**
  543.      * Retrieves data from memory in one chunk, or from disk in $buffer
  544.      * sized chunks.
  545.      *
  546.      * @return string The data
  547.      */
  548.     function getData()
  549.     {
  550.         $buffer 4096;
  551.  
  552.         // Return data stored in memory
  553.         if (isset($this->_data)) {
  554.             $tmp   $this->_data;
  555.             unset($this->_data);
  556.             return $tmp;
  557.         }
  558.         // No data to return
  559.         return false;
  560.     }
  561.  
  562.     /**
  563.      * Set the option to print the row and column headers on the printed page.
  564.      *
  565.      * @access public
  566.      * @param integer $print Whether to print the headers or not. Defaults to 1 (print).
  567.      */
  568.     function printRowColHeaders($print 1)
  569.     {
  570.         $this->_print_headers $print;
  571.     }
  572.  
  573.     /**
  574.      * This method sets the properties for outlining and grouping. The defaults
  575.      * correspond to Excel's defaults.
  576.      *
  577.      * @param bool $visible 
  578.      * @param bool $symbols_below 
  579.      * @param bool $symbols_right 
  580.      * @param bool $auto_style 
  581.      */
  582.     function setOutline($visible true$symbols_below true$symbols_right true$auto_style false)
  583.     {
  584.         $this->_outline_on    = $visible;
  585.         $this->_outline_below = $symbols_below;
  586.         $this->_outline_right = $symbols_right;
  587.         $this->_outline_style = $auto_style;
  588.  
  589.         // Ensure this is a boolean vale for Window2
  590.         if ($this->_outline_on{
  591.             $this->_outline_on = 1;
  592.         }
  593.      }
  594.  
  595.     /**
  596.      * Write a double to the specified row and column (zero indexed).
  597.      * An integer can be written as a double. Excel will display an
  598.      * integer. $format is optional.
  599.      *
  600.      * Returns  0 : normal termination
  601.      *         -2 : row or column out of range
  602.      *
  603.      * @param integer $row    Zero indexed row
  604.      * @param integer $col    Zero indexed column
  605.      * @param float   $num    The number to write
  606.      * @param mixed   $format The optional XF format
  607.      * @return integer 
  608.      */
  609.     private function _writeNumber($row$col$num$xfIndex)
  610.     {
  611.         $record    0x0203;                 // Record identifier
  612.         $length    0x000E;                 // Number of bytes to follow
  613.  
  614.         $header    pack("vv",  $record$length);
  615.         $data      pack("vvv"$row$col$xfIndex);
  616.         $xl_double pack("d",   $num);
  617.         if (PHPExcel_Writer_Excel5_BIFFwriter::getByteOrder()) // if it's Big Endian
  618.             $xl_double strrev($xl_double);
  619.         }
  620.  
  621.         $this->_append($header.$data.$xl_double);
  622.         return(0);
  623.     }
  624.  
  625.     /**
  626.      * Write a LABELSST record or a LABEL record. Which one depends on BIFF version
  627.      *
  628.      * @param int $row Row index (0-based)
  629.      * @param int $col Column index (0-based)
  630.      * @param string $str The string
  631.      * @param int $xfIndex Index to XF record
  632.      */
  633.     private function _writeString($row$col$str$xfIndex)
  634.     {
  635.         if ($this->_BIFF_version == 0x0600{
  636.             $this->_writeLabelSst($row$col$str$xfIndex);
  637.         else {
  638.             $this->_writeLabel($row$col$str$xfIndex);
  639.         }
  640.     }
  641.     /**
  642.      * Write a string to the specified row and column (zero indexed).
  643.      * NOTE: there is an Excel 5 defined limit of 255 characters.
  644.      * $format is optional.
  645.      * Returns  0 : normal termination
  646.      *         -2 : row or column out of range
  647.      *         -3 : long string truncated to 255 chars
  648.      *
  649.      * @access public
  650.      * @param integer $row    Zero indexed row
  651.      * @param integer $col    Zero indexed column
  652.      * @param string  $str    The string to write
  653.      * @param mixed   $format The XF format for the cell
  654.      * @return integer 
  655.      */
  656.     private function _writeLabel($row$col$str$xfIndex)
  657.     {
  658.         $strlen    strlen($str);
  659.         $record    0x0204;                   // Record identifier
  660.         $length    0x0008 $strlen;         // Bytes to follow
  661.  
  662.         $str_error 0;
  663.  
  664.         if ($strlen $this->_xls_strmax// LABEL must be < 255 chars
  665.             $str       substr($str0$this->_xls_strmax);
  666.             $length    0x0008 $this->_xls_strmax;
  667.             $strlen    $this->_xls_strmax;
  668.             $str_error = -3;
  669.         }
  670.  
  671.         $header    pack("vv",   $record$length);
  672.         $data      pack("vvvv"$row$col$xfIndex$strlen);
  673.         $this->_append($header $data $str);
  674.         return($str_error);
  675.     }
  676.  
  677.     /**
  678.      * Write a string to the specified row and column (zero indexed).
  679.      * This is the BIFF8 version (no 255 chars limit).
  680.      * $format is optional.
  681.      * Returns  0 : normal termination
  682.      *         -2 : row or column out of range
  683.      *         -3 : long string truncated to 255 chars
  684.      *
  685.      * @access public
  686.      * @param integer $row    Zero indexed row
  687.      * @param integer $col    Zero indexed column
  688.      * @param string  $str    The string to write
  689.      * @param mixed   $format The XF format for the cell
  690.      * @return integer 
  691.      */
  692.     private function _writeLabelSst($row$col$str$xfIndex)
  693.     {
  694.         $record    0x00FD;                   // Record identifier
  695.         $length    0x000A;                   // Bytes to follow
  696.  
  697.         $str PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($str);
  698.  
  699.         /* check if string is already present */
  700.         if (!isset($this->_str_table[$str])) {
  701.             $this->_str_table[$str$this->_str_unique++;
  702.         }
  703.         $this->_str_total++;
  704.  
  705.         $header    pack('vv',   $record$length);
  706.         $data      pack('vvvV'$row$col$xfIndex$this->_str_table[$str]);
  707.         $this->_append($header.$data);
  708.     }
  709.  
  710.     /**
  711.      * Writes a note associated with the cell given by the row and column.
  712.      * NOTE records don't have a length limit.
  713.      *
  714.      * @param integer $row    Zero indexed row
  715.      * @param integer $col    Zero indexed column
  716.      * @param string  $note   The note to write
  717.      */
  718.     private function _writeNote($row$col$note)
  719.     {
  720.         $note_length    strlen($note);
  721.         $record         0x001C;                // Record identifier
  722.         $max_length     2048;                  // Maximun length for a NOTE record
  723.         //$length      = 0x0006 + $note_length;    // Bytes to follow
  724.  
  725.         // Length for this record is no more than 2048 + 6
  726.         $length    0x0006 min($note_length2048);
  727.         $header    pack("vv",   $record$length);
  728.         $data      pack("vvv"$row$col$note_length);
  729.         $this->_append($header $data substr($note02048));
  730.  
  731.         for ($i $max_length$i $note_length$i += $max_length{
  732.             $chunk  substr($note$i$max_length);
  733.             $length 0x0006 strlen($chunk);
  734.             $header pack("vv",   $record$length);
  735.             $data   pack("vvv"-10strlen($chunk));
  736.             $this->_append($header.$data.$chunk);
  737.         }
  738.         return(0);
  739.     }
  740.  
  741.     /**
  742.      * Write a blank cell to the specified row and column (zero indexed).
  743.      * A blank cell is used to specify formatting without adding a string
  744.      * or a number.
  745.      *
  746.      * A blank cell without a format serves no purpose. Therefore, we don't write
  747.      * a BLANK record unless a format is specified.
  748.      *
  749.      * Returns  0 : normal termination (including no format)
  750.      *         -1 : insufficient number of arguments
  751.      *         -2 : row or column out of range
  752.      *
  753.      * @param integer $row    Zero indexed row
  754.      * @param integer $col    Zero indexed column
  755.      * @param mixed   $format The XF format
  756.      */
  757.     function _writeBlank($row$col$xfIndex)
  758.     {
  759.         $record    0x0201;                 // Record identifier
  760.         $length    0x0006;                 // Number of bytes to follow
  761.  
  762.         $header    pack("vv",  $record$length);
  763.         $data      pack("vvv"$row$col$xfIndex);
  764.         $this->_append($header $data);
  765.         return 0;
  766.     }
  767.  
  768.     /**
  769.      * Write a boolean or an error type to the specified row and column (zero indexed)
  770.      *
  771.      * @param int $row Row index (0-based)
  772.      * @param int $col Column index (0-based)
  773.      * @param int $value 
  774.      * @param boolean $isError Error or Boolean?
  775.      * @param int $xfIndex 
  776.      */
  777.     private function _writeBoolErr($row$col$value$isError$xfIndex)
  778.     {
  779.         $record 0x0205;
  780.         $length 8;
  781.  
  782.         $header    pack("vv",  $record$length);
  783.         $data      pack("vvvCC"$row$col$xfIndex$value$isError);
  784.         $this->_append($header $data);
  785.         return 0;
  786.     }
  787.  
  788.     /**
  789.      * Write a formula to the specified row and column (zero indexed).
  790.      * The textual representation of the formula is passed to the parser in
  791.      * Parser.php which returns a packed binary string.
  792.      *
  793.      * Returns  0 : normal termination
  794.      *         -1 : formula errors (bad formula)
  795.      *         -2 : row or column out of range
  796.      *
  797.      * @param integer $row     Zero indexed row
  798.      * @param integer $col     Zero indexed column
  799.      * @param string  $formula The formula text string
  800.      * @param mixed   $format  The optional XF format
  801.      * @param mixed   $calculatedValue  Calculated value
  802.      * @return integer 
  803.      */
  804.     private function _writeFormula($row$col$formula$xfIndex$calculatedValue)
  805.     {
  806.         $record    0x0006;     // Record identifier
  807.  
  808.         // Initialize possible additional value for STRING record that should be written after the FORMULA record?
  809.         $stringValue null;
  810.  
  811.         // calculated value
  812.         if (isset($calculatedValue)) {
  813.  
  814.             // Since we can't yet get the data type of the calculated value,
  815.             // we use best effort to determine data type
  816.  
  817.             if (is_bool($calculatedValue)) {
  818.                 // Boolean value
  819.                 $num pack('CCCvCv'0x010x00(int)$calculatedValue0x000x000xFFFF);
  820.  
  821.             elseif (is_int($calculatedValue|| is_float($calculatedValue)) {
  822.                 // Numeric value
  823.                 $num pack('d'$calculatedValue);
  824.  
  825.             elseif (is_string($calculatedValue)) {
  826.                 if (array_key_exists($calculatedValuePHPExcel_Cell_DataType::getErrorCodes())) {
  827.                     // Error value
  828.                     $num pack('CCCvCv'0x020x00$this->_mapErrorCode($calculatedValue)0x000x000xFFFF);
  829.  
  830.                 elseif ($calculatedValue === '' && $this->_BIFF_version == 0x0600{
  831.                     // Empty string (and BIFF8)
  832.                     $num pack('CCCvCv'0x030x000x000x000x000xFFFF);
  833.  
  834.                 else {
  835.                     // Non-empty string value (or empty string BIFF5)
  836.                     $stringValue $calculatedValue;
  837.                     $num pack('CCCvCv'0x000x000x000x000x000xFFFF);
  838.  
  839.                 }
  840.  
  841.             else {
  842.                 // We are really not supposed to reach here
  843.                 $num pack('d'0x00);
  844.  
  845.             }
  846.  
  847.         else {
  848.             $num pack('d'0x00);
  849.         }
  850.  
  851.         $grbit     0x03;                // Option flags
  852.         $unknown   0x0000;              // Must be zero
  853.  
  854.         // Strip the '=' or '@' sign at the beginning of the formula string
  855.         if (preg_match("/^=/"$formula)) {
  856.             $formula preg_replace("/(^=)/"""$formula);
  857.         else {
  858.             // Error handling
  859.             $this->_writeString($row$col'Unrecognised character for formula');
  860.             return -1;
  861.         }
  862.  
  863.         // Parse the formula using the parser in Parser.php
  864.         try {
  865.             $error $this->_parser->parse($formula);
  866.             $formula $this->_parser->toReversePolish();
  867.  
  868.             $formlen    strlen($formula);    // Length of the binary string
  869.             $length     0x16 $formlen;     // Length of the record data
  870.  
  871.             $header    pack("vv",      $record$length);
  872.  
  873.             $data      pack("vvv"$row$col$xfIndex)
  874.                         . $num
  875.                         . pack("vVv"$grbit$unknown$formlen);
  876.             $this->_append($header $data $formula);
  877.  
  878.             // Append also a STRING record if necessary
  879.             if ($stringValue !== null{
  880.                 $this->_writeStringRecord($stringValue);
  881.             }
  882.  
  883.             return 0;
  884.  
  885.         catch (Exception $e{
  886.             // do nothing
  887.         }
  888.  
  889.     }
  890.  
  891.     /**
  892.      * Write a STRING record. This
  893.      *
  894.      * @param string $stringValue 
  895.      */
  896.     private function _writeStringRecord($stringValue)
  897.     {
  898.         $record 0x0207;     // Record identifier
  899.         $data PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($stringValue);
  900.  
  901.         $length strlen($data);
  902.         $header pack('vv'$record$length);
  903.  
  904.         $this->_append($header $data);
  905.     }
  906.  
  907.     /**
  908.      * Write a hyperlink.
  909.      * This is comprised of two elements: the visible label and
  910.      * the invisible link. The visible label is the same as the link unless an
  911.      * alternative string is specified. The label is written using the
  912.      * _writeString() method. Therefore the 255 characters string limit applies.
  913.      * $string and $format are optional.
  914.      *
  915.      * The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external
  916.      * directory url.
  917.      *
  918.      * Returns  0 : normal termination
  919.      *         -2 : row or column out of range
  920.      *         -3 : long string truncated to 255 chars
  921.      *
  922.      * @param integer $row    Row
  923.      * @param integer $col    Column
  924.      * @param string  $url    URL string
  925.      * @return integer 
  926.      */
  927.     private function _writeUrl($row$col$url)
  928.     {
  929.         // Add start row and col to arg list
  930.         return($this->_writeUrlRange($row$col$row$col$url));
  931.     }
  932.  
  933.     /**
  934.      * This is the more general form of _writeUrl(). It allows a hyperlink to be
  935.      * written to a range of cells. This function also decides the type of hyperlink
  936.      * to be written. These are either, Web (http, ftp, mailto), Internal
  937.      * (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1').
  938.      *
  939.      * @access private
  940.      * @see _writeUrl()
  941.      * @param integer $row1   Start row
  942.      * @param integer $col1   Start column
  943.      * @param integer $row2   End row
  944.      * @param integer $col2   End column
  945.      * @param string  $url    URL string
  946.      * @return integer 
  947.      */
  948.  
  949.     function _writeUrlRange($row1$col1$row2$col2$url)
  950.     {
  951.  
  952.         // Check for internal/external sheet links or default to web link
  953.         if (preg_match('[^internal:]'$url)) {
  954.             return($this->_writeUrlInternal($row1$col1$row2$col2$url));
  955.         }
  956.         if (preg_match('[^external:]'$url)) {
  957.             return($this->_writeUrlExternal($row1$col1$row2$col2$url));
  958.         }
  959.         return($this->_writeUrlWeb($row1$col1$row2$col2$url));
  960.     }
  961.  
  962.  
  963.     /**
  964.      * Used to write http, ftp and mailto hyperlinks.
  965.      * The link type ($options) is 0x03 is the same as absolute dir ref without
  966.      * sheet. However it is differentiated by the $unknown2 data stream.
  967.      *
  968.      * @access private
  969.      * @see _writeUrl()
  970.      * @param integer $row1   Start row
  971.      * @param integer $col1   Start column
  972.      * @param integer $row2   End row
  973.      * @param integer $col2   End column
  974.      * @param string  $url    URL string
  975.      * @return integer 
  976.      */
  977.     function _writeUrlWeb($row1$col1$row2$col2$url)
  978.     {
  979.         $record      0x01B8;                       // Record identifier
  980.         $length      0x00000;                      // Bytes to follow
  981.  
  982.         // Pack the undocumented parts of the hyperlink stream
  983.         $unknown1    pack("H*""D0C9EA79F9BACE118C8200AA004BA90B02000000");
  984.         $unknown2    pack("H*""E0C9EA79F9BACE118C8200AA004BA90B");
  985.  
  986.         // Pack the option flags
  987.         $options     pack("V"0x03);
  988.  
  989.         // Convert URL to a null terminated wchar string
  990.         $url         join("\0"preg_split("''"$url-1PREG_SPLIT_NO_EMPTY));
  991.         $url         $url "\0\0\0";
  992.  
  993.         // Pack the length of the URL
  994.         $url_len     pack("V"strlen($url));
  995.  
  996.         // Calculate the data length
  997.         $length      0x34 strlen($url);
  998.  
  999.         // Pack the header data
  1000.         $header      pack("vv",   $record$length);
  1001.         $data        pack("vvvv"$row1$row2$col1$col2);
  1002.  
  1003.         // Write the packed data
  1004.         $this->_append($header $data .
  1005.                        $unknown1 $options .
  1006.                        $unknown2 $url_len $url);
  1007.         return 0;
  1008.     }
  1009.  
  1010.     /**
  1011.      * Used to write internal reference hyperlinks such as "Sheet1!A1".
  1012.      *
  1013.      * @access private
  1014.      * @see _writeUrl()
  1015.      * @param integer $row1   Start row
  1016.      * @param integer $col1   Start column
  1017.      * @param integer $row2   End row
  1018.      * @param integer $col2   End column
  1019.      * @param string  $url    URL string
  1020.      * @return integer 
  1021.      */
  1022.     function _writeUrlInternal($row1$col1$row2$col2$url)
  1023.     {
  1024.         $record      0x01B8;                       // Record identifier
  1025.         $length      0x00000;                      // Bytes to follow
  1026.  
  1027.         // Strip URL type
  1028.         $url preg_replace('/^internal:/'''$url);
  1029.  
  1030.         // Pack the undocumented parts of the hyperlink stream
  1031.         $unknown1    pack("H*""D0C9EA79F9BACE118C8200AA004BA90B02000000");
  1032.  
  1033.         // Pack the option flags
  1034.         $options     pack("V"0x08);
  1035.  
  1036.         // Convert the URL type and to a null terminated wchar string
  1037.         $url .= "\0";
  1038.  
  1039.         // character count
  1040.         $url_len PHPExcel_Shared_String::CountCharacters($url);
  1041.         $url_len pack('V'$url_len);
  1042.  
  1043.         $url PHPExcel_Shared_String::ConvertEncoding($url'UTF-16LE''UTF-8');
  1044.  
  1045.         // Calculate the data length
  1046.         $length      0x24 strlen($url);
  1047.  
  1048.         // Pack the header data
  1049.         $header      pack("vv",   $record$length);
  1050.         $data        pack("vvvv"$row1$row2$col1$col2);
  1051.  
  1052.         // Write the packed data
  1053.         $this->_append($header $data .
  1054.                        $unknown1 $options .
  1055.                        $url_len $url);
  1056.         return 0;
  1057.     }
  1058.  
  1059.     /**
  1060.      * Write links to external directory names such as 'c:\foo.xls',
  1061.      * c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'.
  1062.      *
  1063.      * Note: Excel writes some relative links with the $dir_long string. We ignore
  1064.      * these cases for the sake of simpler code.
  1065.      *
  1066.      * @access private
  1067.      * @see _writeUrl()
  1068.      * @param integer $row1   Start row
  1069.      * @param integer $col1   Start column
  1070.      * @param integer $row2   End row
  1071.      * @param integer $col2   End column
  1072.      * @param string  $url    URL string
  1073.      * @return integer 
  1074.      */
  1075.     function _writeUrlExternal($row1$col1$row2$col2$url)
  1076.     {
  1077.         // Network drives are different. We will handle them separately
  1078.         // MS/Novell network drives and shares start with \\
  1079.         if (preg_match('[^external:\\\\]'$url)) {
  1080.             return//($this->_writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format));
  1081.         }
  1082.  
  1083.         $record      0x01B8;                       // Record identifier
  1084.         $length      0x00000;                      // Bytes to follow
  1085.  
  1086.         // Strip URL type and change Unix dir separator to Dos style (if needed)
  1087.         //
  1088.         $url preg_replace('/^external:/'''$url);
  1089.         $url preg_replace('/\//'"\\"$url);
  1090.  
  1091.         // Determine if the link is relative or absolute:
  1092.         //   relative if link contains no dir separator, "somefile.xls"
  1093.         //   relative if link starts with up-dir, "..\..\somefile.xls"
  1094.         //   otherwise, absolute
  1095.  
  1096.         $absolute 0x00// relative path
  1097.         if preg_match('/^[A-Z]:/'$url) ) {
  1098.             $absolute 0x02// absolute path on Windows, e.g. C:\...
  1099.         }
  1100.         $link_type               0x01 $absolute;
  1101.  
  1102.         // Determine if the link contains a sheet reference and change some of the
  1103.         // parameters accordingly.
  1104.         // Split the dir name and sheet name (if it exists)
  1105.         $dir_long $url;
  1106.         if (preg_match("/\#/"$url)) {
  1107.             $link_type |= 0x08;
  1108.         }
  1109.  
  1110.  
  1111.         // Pack the link type
  1112.         $link_type   pack("V"$link_type);
  1113.  
  1114.         // Calculate the up-level dir count e.g.. (..\..\..\ == 3)
  1115.         $up_count    preg_match_all("/\.\.\\\/"$dir_long$useless);
  1116.         $up_count    pack("v"$up_count);
  1117.  
  1118.         // Store the short dos dir name (null terminated)
  1119.         $dir_short   preg_replace("/\.\.\\\/"''$dir_long"\0";
  1120.  
  1121.         // Store the long dir name as a wchar string (non-null terminated)
  1122.         $dir_long       $dir_long "\0";
  1123.  
  1124.         // Pack the lengths of the dir strings
  1125.         $dir_short_len pack("V"strlen($dir_short)      );
  1126.         $dir_long_len  pack("V"strlen($dir_long)       );
  1127.         $stream_len    pack("V"0);//strlen($dir_long) + 0x06);
  1128.  
  1129.         // Pack the undocumented parts of the hyperlink stream
  1130.         $unknown1 pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000'       );
  1131.         $unknown2 pack("H*",'0303000000000000C000000000000046'               );
  1132.         $unknown3 pack("H*",'FFFFADDE000000000000000000000000000000000000000');
  1133.         $unknown4 pack("v",  0x03                                            );
  1134.  
  1135.         // Pack the main data stream
  1136.         $data        pack("vvvv"$row1$row2$col1$col2.
  1137.                           $unknown1     .
  1138.                           $link_type    .
  1139.                           $unknown2     .
  1140.                           $up_count     .
  1141.                           $dir_short_len.
  1142.                           $dir_short    .
  1143.                           $unknown3     .
  1144.                           $stream_len   ;/*.
  1145.                           $dir_long_len .
  1146.                           $unknown4     .
  1147.                           $dir_long     .
  1148.                           $sheet_len    .
  1149.                           $sheet        ;*/
  1150.  
  1151.         // Pack the header data
  1152.         $length   strlen($data);
  1153.         $header   pack("vv"$record$length);
  1154.  
  1155.         // Write the packed data
  1156.         $this->_append($header$data);
  1157.         return 0;
  1158.     }
  1159.  
  1160.     /**
  1161.      * This method is used to set the height and format for a row.
  1162.      *
  1163.      * @param integer $row    The row to set
  1164.      * @param integer $height Height we are giving to the row.
  1165.      *                         Use null to set XF without setting height
  1166.      * @param integer $xfIndex  The optional cell style Xf index to apply to the columns
  1167.      * @param bool    $hidden The optional hidden attribute
  1168.      * @param integer $level  The optional outline level for row, in range [0,7]
  1169.      */
  1170.     private function _writeRow($row$height$xfIndex$hidden false$level 0)
  1171.     {
  1172.         $record      0x0208;               // Record identifier
  1173.         $length      0x0010;               // Number of bytes to follow
  1174.  
  1175.         $colMic      0x0000;               // First defined column
  1176.         $colMac      0x0000;               // Last defined column
  1177.         $irwMac      0x0000;               // Used by Excel to optimise loading
  1178.         $reserved    0x0000;               // Reserved
  1179.         $grbit       0x0000;               // Option flags
  1180.         $ixfe        $xfIndex;
  1181.  
  1182.         if $height ){
  1183.             $height null;
  1184.         }
  1185.  
  1186.         // Use _writeRow($row, null, $XF) to set XF format without setting height
  1187.         if ($height != null{
  1188.             $miyRw $height 20;  // row height
  1189.         else {
  1190.             $miyRw 0xff;          // default row height is 256
  1191.         }
  1192.  
  1193.         // Set the options flags. fUnsynced is used to show that the font and row
  1194.         // heights are not compatible. This is usually the case for WriteExcel.
  1195.         // The collapsed flag 0x10 doesn't seem to be used to indicate that a row
  1196.         // is collapsed. Instead it is used to indicate that the previous row is
  1197.         // collapsed. The zero height flag, 0x20, is used to collapse a row.
  1198.  
  1199.         $grbit |= $level;
  1200.         if ($hidden{
  1201.             $grbit |= 0x0020;
  1202.         }
  1203.         if ($height !== null{
  1204.             $grbit |= 0x0040// fUnsynced
  1205.         }
  1206.         if ($xfIndex !== 0xF{
  1207.             $grbit |= 0x0080;
  1208.         }
  1209.         $grbit |= 0x0100;
  1210.  
  1211.         $header   pack("vv",       $record$length);
  1212.         $data     pack("vvvvvvvv"$row$colMic$colMac$miyRw,
  1213.                                      $irwMac,$reserved$grbit$ixfe);
  1214.         $this->_append($header.$data);
  1215.     }
  1216.  
  1217.     /**
  1218.      * Writes Excel DIMENSIONS to define the area in which there is data.
  1219.      */
  1220.     private function _writeDimensions()
  1221.     {
  1222.         $record 0x0200// Record identifier
  1223.  
  1224.         if ($this->_BIFF_version == 0x0500{
  1225.             $length 0x000A;               // Number of bytes to follow
  1226.             $data pack("vvvvv"
  1227.                     $this->_firstRowIndex
  1228.                     $this->_lastRowIndex 1
  1229.                     $this->_firstColumnIndex
  1230.                     $this->_lastColumnIndex 1
  1231.                     0x0000 // reserved
  1232.                 );
  1233.  
  1234.         elseif ($this->_BIFF_version == 0x0600{
  1235.             $length 0x000E;
  1236.             $data pack('VVvvv'
  1237.                     $this->_firstRowIndex
  1238.                     $this->_lastRowIndex 1
  1239.                     $this->_firstColumnIndex
  1240.                     $this->_lastColumnIndex 1
  1241.                     0x0000 // reserved
  1242.                 );
  1243.         }
  1244.  
  1245.         $header pack("vv"$record$length);
  1246.         $this->_append($header.$data);
  1247.     }
  1248.  
  1249.     /**
  1250.      * Write BIFF record Window2.
  1251.      */
  1252.     private function _writeWindow2()
  1253.     {
  1254.         $record         0x023E;     // Record identifier
  1255.         if ($this->_BIFF_version == 0x0500{
  1256.             $length         0x000A;     // Number of bytes to follow
  1257.         elseif ($this->_BIFF_version == 0x0600{
  1258.             $length         0x0012;
  1259.         }
  1260.  
  1261.         $grbit          0x00B6;     // Option flags
  1262.         $rwTop          0x0000;     // Top row visible in window
  1263.         $colLeft        0x0000;     // Leftmost column visible in window
  1264.  
  1265.  
  1266.         // The options flags that comprise $grbit
  1267.         $fDspFmla       0;                     // 0 - bit
  1268.         $fDspGrid       $this->_phpSheet->getShowGridlines(0// 1
  1269.         $fDspRwCol      $this->_phpSheet->getShowRowColHeaders(0// 2
  1270.         $fFrozen        $this->_phpSheet->getFreezePane(0;        // 3
  1271.         $fDspZeros      1;                     // 4
  1272.         $fDefaultHdr    1;                     // 5
  1273.         $fArabic        $this->_phpSheet->getRightToLeft(0// 6
  1274.         $fDspGuts       $this->_outline_on;    // 7
  1275.         $fFrozenNoSplit 0;                     // 0 - bit
  1276.         // no support in PHPExcel for selected sheet, therefore sheet is only selected if it is the active sheet
  1277.         $fSelected      ($this->_phpSheet === $this->_phpSheet->getParent()->getActiveSheet()) 0;
  1278.         $fPaged         1;                     // 2
  1279.  
  1280.         $grbit             $fDspFmla;
  1281.         $grbit            |= $fDspGrid       << 1;
  1282.         $grbit            |= $fDspRwCol      << 2;
  1283.         $grbit            |= $fFrozen        << 3;
  1284.         $grbit            |= $fDspZeros      << 4;
  1285.         $grbit            |= $fDefaultHdr    << 5;
  1286.         $grbit            |= $fArabic        << 6;
  1287.         $grbit            |= $fDspGuts       << 7;
  1288.         $grbit            |= $fFrozenNoSplit << 8;
  1289.         $grbit            |= $fSelected      << 9;
  1290.         $grbit            |= $fPaged         << 10;
  1291.  
  1292.         $header  pack("vv",   $record$length);
  1293.         $data    pack("vvv"$grbit$rwTop$colLeft);
  1294.         // FIXME !!!
  1295.         if ($this->_BIFF_version == 0x0500{
  1296.             $rgbHdr         0x00000000// Row/column heading and gridline color
  1297.             $data .= pack("V"$rgbHdr);
  1298.         elseif ($this->_BIFF_version == 0x0600{
  1299.             $rgbHdr       0x0040// Row/column heading and gridline color index
  1300.             $zoom_factor_page_break 0x0000;
  1301.             $zoom_factor_normal     0x0000;
  1302.             $data .= pack("vvvvV"$rgbHdr0x0000$zoom_factor_page_break$zoom_factor_normal0x00000000);
  1303.         }
  1304.         $this->_append($header.$data);
  1305.     }
  1306.  
  1307.     /**
  1308.      * Write BIFF record DEFAULTROWHEIGHT.
  1309.      */
  1310.     private function _writeDefaultRowHeight()
  1311.     {
  1312.         $defaultRowHeight $this->_phpSheet->getDefaultRowDimension()->getRowHeight();
  1313.  
  1314.         if ($defaultRowHeight 0{
  1315.             return;
  1316.         }
  1317.  
  1318.         // convert to twips
  1319.         $defaultRowHeight = (int) 20 $defaultRowHeight;
  1320.  
  1321.         $record   0x0225;      // Record identifier
  1322.         $length   0x0004;      // Number of bytes to follow
  1323.  
  1324.         $header   pack("vv"$record$length);
  1325.         $data     pack("vv",  1$defaultRowHeight);
  1326.         $this->_append($header $data);
  1327.     }
  1328.  
  1329.     /**
  1330.      * Write BIFF record DEFCOLWIDTH if COLINFO records are in use.
  1331.      */
  1332.     private function _writeDefcol()
  1333.     {
  1334.         $defaultColWidth 8;
  1335.  
  1336.         $record   0x0055;      // Record identifier
  1337.         $length   0x0002;      // Number of bytes to follow
  1338.  
  1339.         $header pack("vv"$record$length);
  1340.         $data pack("v"$defaultColWidth);
  1341.         $this->_append($header $data);
  1342.     }
  1343.  
  1344.     /**
  1345.      * Write BIFF record COLINFO to define column widths
  1346.      *
  1347.      * Note: The SDK says the record length is 0x0B but Excel writes a 0x0C
  1348.      * length record.
  1349.      *
  1350.      * @param array $col_array This is the only parameter received and is composed of the following:
  1351.      *                 0 => First formatted column,
  1352.      *                 1 => Last formatted column,
  1353.      *                 2 => Col width (8.43 is Excel default),
  1354.      *                 3 => The optional XF format of the column,
  1355.      *                 4 => Option flags.
  1356.      *                 5 => Optional outline level
  1357.      */
  1358.     private function _writeColinfo($col_array)
  1359.     {
  1360.         if (isset($col_array[0])) {
  1361.             $colFirst $col_array[0];
  1362.         }
  1363.         if (isset($col_array[1])) {
  1364.             $colLast $col_array[1];
  1365.         }
  1366.         if (isset($col_array[2])) {
  1367.             $coldx $col_array[2];
  1368.         else {
  1369.             $coldx 8.43;
  1370.         }
  1371.         if (isset($col_array[3])) {
  1372.             $xfIndex $col_array[3];
  1373.         else {
  1374.             $xfIndex 15;
  1375.         }
  1376.         if (isset($col_array[4])) {
  1377.             $grbit $col_array[4];
  1378.         else {
  1379.             $grbit 0;
  1380.         }
  1381.         if (isset($col_array[5])) {
  1382.             $level $col_array[5];
  1383.         else {
  1384.             $level 0;
  1385.         }
  1386.         $record   0x007D;          // Record identifier
  1387.         $length   0x000C;          // Number of bytes to follow
  1388.  
  1389.         $coldx   *= 256;             // Convert to units of 1/256 of a char
  1390.  
  1391.         $ixfe     $xfIndex;
  1392.         $reserved 0x0000;            // Reserved
  1393.  
  1394.         $level max(0min($level7));
  1395.         $grbit |= $level << 8;
  1396.  
  1397.         $header   pack("vv",     $record$length);
  1398.         $data     pack("vvvvvv"$colFirst$colLast$coldx,
  1399.                                    $ixfe$grbit$reserved);
  1400.         $this->_append($header.$data);
  1401.     }
  1402.  
  1403.     /**
  1404.      * Write BIFF record SELECTION.
  1405.      */
  1406.     private function _writeSelection()
  1407.     {
  1408.         // look up the selected cell range
  1409.         $selectedCells $this->_phpSheet->getSelectedCells();
  1410.         $selectedCells PHPExcel_Cell::splitRange($this->_phpSheet->getSelectedCells());
  1411.         $selectedCells $selectedCells[0];
  1412.         if (count($selectedCells== 2{
  1413.             list($first$last$selectedCells;
  1414.         else {
  1415.             $first $selectedCells[0];
  1416.             $last  $selectedCells[0];
  1417.         }
  1418.  
  1419.         list($colFirst$rwFirstPHPExcel_Cell::coordinateFromString($first);
  1420.         $colFirst PHPExcel_Cell::columnIndexFromString($colFirst1// base 0 column index
  1421.         --$rwFirst// base 0 row index
  1422.  
  1423.         list($colLast$rwLastPHPExcel_Cell::coordinateFromString($last);
  1424.         $colLast PHPExcel_Cell::columnIndexFromString($colLast1// base 0 column index
  1425.         --$rwLast// base 0 row index
  1426.  
  1427.         // make sure we are not out of bounds
  1428.         $colFirst min($colFirst255);
  1429.         $colLast  min($colLast,  255);
  1430.         if ($this->_BIFF_version == 0x0600{
  1431.             $rwFirst min($rwFirst65535);
  1432.             $rwLast  min($rwLast,  65535);
  1433.         else {
  1434.             $rwFirst min($rwFirst16383);
  1435.             $rwLast  min($rwLast,  16383);
  1436.         }
  1437.  
  1438.         $record   0x001D;                  // Record identifier
  1439.         $length   0x000F;                  // Number of bytes to follow
  1440.  
  1441.         $pnn      $this->_active_pane;     // Pane position
  1442.         $rwAct    $rwFirst;                // Active row
  1443.         $colAct   $colFirst;               // Active column
  1444.         $irefAct  0;                       // Active cell ref
  1445.         $cref     1;                       // Number of refs
  1446.  
  1447.         if (!isset($rwLast)) {
  1448.             $rwLast   $rwFirst;       // Last  row in reference
  1449.         }
  1450.         if (!isset($colLast)) {
  1451.             $colLast  $colFirst;      // Last  col in reference
  1452.         }
  1453.  
  1454.         // Swap last row/col for first row/col as necessary
  1455.         if ($rwFirst $rwLast{
  1456.             list($rwFirst$rwLastarray($rwLast$rwFirst);
  1457.         }
  1458.  
  1459.         if ($colFirst $colLast{
  1460.             list($colFirst$colLastarray($colLast$colFirst);
  1461.         }
  1462.  
  1463.         $header   pack("vv",         $record$length);
  1464.         $data     pack("CvvvvvvCC",  $pnn$rwAct$colAct,
  1465.                                        $irefAct$cref,
  1466.                                        $rwFirst$rwLast,
  1467.                                        $colFirst$colLast);
  1468.         $this->_append($header $data);
  1469.     }
  1470.  
  1471.     /**
  1472.      * Store the MERGEDCELLS records for all ranges of merged cells
  1473.      */
  1474.     private function _writeMergedCells()
  1475.     {
  1476.         $mergeCells $this->_phpSheet->getMergeCells();
  1477.         $countMergeCells count($mergeCells);
  1478.  
  1479.         if ($countMergeCells == 0{
  1480.             return;
  1481.         }
  1482.  
  1483.         // maximum allowed number of merged cells per record
  1484.         if ($this->_BIFF_version == 0x0600{
  1485.             $maxCountMergeCellsPerRecord 1027;
  1486.         else {
  1487.             $maxCountMergeCellsPerRecord 259;
  1488.         }
  1489.  
  1490.         // record identifier
  1491.         $record 0x00E5;
  1492.  
  1493.         // counter for total number of merged cells treated so far by the writer
  1494.         $i 0;
  1495.  
  1496.         // counter for number of merged cells written in record currently being written
  1497.         $j 0;
  1498.  
  1499.         // initialize record data
  1500.         $recordData '';
  1501.  
  1502.         // loop through the merged cells
  1503.         foreach ($mergeCells as $mergeCell{
  1504.             ++$i;
  1505.             ++$j;
  1506.  
  1507.             // extract the row and column indexes
  1508.             $range PHPExcel_Cell::splitRange($mergeCell);
  1509.             list($first$last$range[0];
  1510.             list($firstColumn$firstRowPHPExcel_Cell::coordinateFromString($first);
  1511.             list($lastColumn$lastRowPHPExcel_Cell::coordinateFromString($last);
  1512.  
  1513.             $recordData .= pack('vvvv'$firstRow 1$lastRow 1PHPExcel_Cell::columnIndexFromString($firstColumn1PHPExcel_Cell::columnIndexFromString($lastColumn1);
  1514.  
  1515.             // flush record if we have reached limit for number of merged cells, or reached final merged cell
  1516.             if ($j == $maxCountMergeCellsPerRecord or $i == $countMergeCells{
  1517.                 $recordData pack('v'$j$recordData;
  1518.                 $length strlen($recordData);
  1519.                 $header pack('vv'$record$length);
  1520.                 $this->_append($header $recordData);
  1521.  
  1522.                 // initialize for next record, if any
  1523.                 $recordData '';
  1524.                 $j 0;
  1525.             }
  1526.         }
  1527.     }
  1528.  
  1529.     /**
  1530.      * Write SHEETLAYOUT record
  1531.      */
  1532.     private function _writeSheetLayout()
  1533.     {
  1534.         if (!$this->_phpSheet->isTabColorSet()) {
  1535.             return;
  1536.         }
  1537.  
  1538.         $recordData pack(
  1539.             'vvVVVvv'
  1540.             0x0862
  1541.             0x0000        // unused
  1542.             0x00000000    // unused
  1543.             0x00000000    // unused
  1544.             0x00000014    // size of record data
  1545.             $this->_colors[$this->_phpSheet->getTabColor()->getRGB()]    // color index
  1546.             0x0000        // unused
  1547.         );
  1548.  
  1549.         $length strlen($recordData);
  1550.  
  1551.         $record 0x0862// Record identifier
  1552.         $header pack('vv'$record$length);
  1553.         $this->_append($header $recordData);
  1554.     }
  1555.  
  1556.     /**
  1557.      * Write SHEETPROTECTION
  1558.      */
  1559.     private function _writeSheetProtection()
  1560.     {
  1561.         // record identifier
  1562.         $record 0x0867;
  1563.  
  1564.         // prepare options
  1565.         $options  =   (int) !$this->_phpSheet->getProtection()->getObjects()
  1566.                     | (int) !$this->_phpSheet->getProtection()->getScenarios()           << 1
  1567.                     | (int) !$this->_phpSheet->getProtection()->getFormatCells()         << 2
  1568.                     | (int) !$this->_phpSheet->getProtection()->getFormatColumns()       << 3
  1569.                     | (int) !$this->_phpSheet->getProtection()->getFormatRows()          << 4
  1570.                     | (int) !$this->_phpSheet->getProtection()->getInsertColumns()       << 5
  1571.                     | (int) !$this->_phpSheet->getProtection()->getInsertRows()          << 6
  1572.                     | (int) !$this->_phpSheet->getProtection()->getInsertHyperlinks()    << 7
  1573.                     | (int) !$this->_phpSheet->getProtection()->getDeleteColumns()       << 8
  1574.                     | (int) !$this->_phpSheet->getProtection()->getDeleteRows()          << 9
  1575.                     | (int) !$this->_phpSheet->getProtection()->getSelectLockedCells()   << 10
  1576.                     | (int) !$this->_phpSheet->getProtection()->getSort()                << 11
  1577.                     | (int) !$this->_phpSheet->getProtection()->getAutoFilter()          << 12
  1578.                     | (int) !$this->_phpSheet->getProtection()->getPivotTables()         << 13
  1579.                     | (int) !$this->_phpSheet->getProtection()->getSelectUnlockedCells(<< 14 ;
  1580.  
  1581.         // record data
  1582.         $recordData pack(
  1583.             'vVVCVVvv'
  1584.             0x0867        // repeated record identifier
  1585.             0x0000        // not used
  1586.             0x0000        // not used
  1587.             0x00            // not used
  1588.             0x01000200    // unknown data
  1589.             0xFFFFFFFF    // unknown data
  1590.             $options        // options
  1591.             0x0000        // not used
  1592.         );
  1593.  
  1594.         $length strlen($recordData);
  1595.         $header pack('vv'$record$length);
  1596.  
  1597.         $this->_append($header $recordData);
  1598.     }
  1599.  
  1600.     /**
  1601.      * Write BIFF record RANGEPROTECTION
  1602.      *
  1603.      * Openoffice.org's Documentaion of the Microsoft Excel File Format uses term RANGEPROTECTION for these records
  1604.      * Microsoft Office Excel 97-2007 Binary File Format Specification uses term FEAT for these records
  1605.      */
  1606.     private function _writeRangeProtection()
  1607.     {
  1608.         foreach ($this->_phpSheet->getProtectedCells(as $range => $password{
  1609.             // number of ranges, e.g. 'A1:B3 C20:D25'
  1610.             $cellRanges explode(' '$range);
  1611.             $cref count($cellRanges);
  1612.  
  1613.             $recordData pack(
  1614.                 'vvVVvCVvVv',
  1615.                 0x0868,
  1616.                 0x00,
  1617.                 0x0000,
  1618.                 0x0000,
  1619.                 0x02,
  1620.                 0x0,
  1621.                 0x0000,
  1622.                 $cref,
  1623.                 0x0000,
  1624.                 0x00
  1625.             );
  1626.  
  1627.             foreach ($cellRanges as $cellRange{
  1628.                 $recordData .= $this->_writeBIFF8CellRangeAddressFixed($cellRange);
  1629.             }
  1630.  
  1631.             // the rgbFeat structure
  1632.             $recordData .= pack(
  1633.                 'VV',
  1634.                 0x0000,
  1635.                 hexdec($password)
  1636.             );
  1637.  
  1638.             $recordData .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong('p' md5($recordData));
  1639.  
  1640.             $length strlen($recordData);
  1641.  
  1642.             $record 0x0868;        // Record identifier
  1643.             $header pack("vv"$record$length);
  1644.             $this->_append($header $recordData);
  1645.         }
  1646.     }
  1647.  
  1648.     /**
  1649.      * Write BIFF record EXTERNCOUNT to indicate the number of external sheet
  1650.      * references in a worksheet.
  1651.      *
  1652.      * Excel only stores references to external sheets that are used in formulas.
  1653.      * For simplicity we store references to all the sheets in the workbook
  1654.      * regardless of whether they are used or not. This reduces the overall
  1655.      * complexity and eliminates the need for a two way dialogue between the formula
  1656.      * parser the worksheet objects.
  1657.      *
  1658.      * @param integer $count The number of external sheet references in this worksheet
  1659.      */
  1660.     private function _writeExterncount($count)
  1661.     {
  1662.         $record 0x0016;          // Record identifier
  1663.         $length 0x0002;          // Number of bytes to follow
  1664.  
  1665.         $header pack("vv"$record$length);
  1666.         $data   pack("v",  $count);
  1667.         $this->_append($header $data);
  1668.     }
  1669.  
  1670.     /**
  1671.      * Writes the Excel BIFF EXTERNSHEET record. These references are used by
  1672.      * formulas. A formula references a sheet name via an index. Since we store a
  1673.      * reference to all of the external worksheets the EXTERNSHEET index is the same
  1674.      * as the worksheet index.
  1675.      *
  1676.      * @param string $sheetname The name of a external worksheet
  1677.      */
  1678.     private function _writeExternsheet($sheetname)
  1679.     {
  1680.         $record    0x0017;         // Record identifier
  1681.  
  1682.         // References to the current sheet are encoded differently to references to
  1683.         // external sheets.
  1684.         //
  1685.         if ($this->_phpSheet->getTitle(== $sheetname{
  1686.             $sheetname '';
  1687.             $length    0x02;  // The following 2 bytes
  1688.             $cch       1;     // The following byte
  1689.             $rgch      0x02;  // Self reference
  1690.         else {
  1691.             $length    0x02 strlen($sheetname);
  1692.             $cch       strlen($sheetname);
  1693.             $rgch      0x03;  // Reference to a sheet in the current workbook
  1694.         }
  1695.  
  1696.         $header pack("vv",  $record$length);
  1697.         $data   pack("CC"$cch$rgch);
  1698.         $this->_append($header $data $sheetname);
  1699.     }
  1700.  
  1701.     /**
  1702.      * Writes the Excel BIFF PANE record.
  1703.      * The panes can either be frozen or thawed (unfrozen).
  1704.      * Frozen panes are specified in terms of an integer number of rows and columns.
  1705.      * Thawed panes are specified in terms of Excel's units for rows and columns.
  1706.      */
  1707.     private function _writePanes()
  1708.     {
  1709.         $panes array();
  1710.         if ($freezePane $this->_phpSheet->getFreezePane()) {
  1711.             list($column$rowPHPExcel_Cell::coordinateFromString($freezePane);
  1712.             $panes[0$row 1;
  1713.             $panes[1PHPExcel_Cell::columnIndexFromString($column1;
  1714.         else {
  1715.             // thaw panes
  1716.             return;
  1717.         }
  1718.  
  1719.         $y       = isset($panes[0]$panes[0null;
  1720.         $x       = isset($panes[1]$panes[1null;
  1721.         $rwTop   = isset($panes[2]$panes[2null;
  1722.         $colLeft = isset($panes[3]$panes[3null;
  1723.         if (count($panes4// if Active pane was received
  1724.             $pnnAct $panes[4];
  1725.         else {
  1726.             $pnnAct null;
  1727.         }
  1728.         $record  0x0041;       // Record identifier
  1729.         $length  0x000A;       // Number of bytes to follow
  1730.  
  1731.         // Code specific to frozen or thawed panes.
  1732.         if ($this->_phpSheet->getFreezePane()) {
  1733.             // Set default values for $rwTop and $colLeft
  1734.             if (!isset($rwTop)) {
  1735.                 $rwTop   $y;
  1736.             }
  1737.             if (!isset($colLeft)) {
  1738.                 $colLeft $x;
  1739.             }
  1740.         else {
  1741.             // Set default values for $rwTop and $colLeft
  1742.             if (!isset($rwTop)) {
  1743.                 $rwTop   0;
  1744.             }
  1745.             if (!isset($colLeft)) {
  1746.                 $colLeft 0;
  1747.             }
  1748.  
  1749.             // Convert Excel's row and column units to the internal units.
  1750.             // The default row height is 12.75
  1751.             // The default column width is 8.43
  1752.             // The following slope and intersection values were interpolated.
  1753.             //
  1754.             $y 20*$y      255;
  1755.             $x 113.879*$x 390;
  1756.         }
  1757.  
  1758.  
  1759.         // Determine which pane should be active. There is also the undocumented
  1760.         // option to override this should it be necessary: may be removed later.
  1761.         //
  1762.         if (!isset($pnnAct)) {
  1763.             if ($x != && $y != 0{
  1764.                 $pnnAct 0// Bottom right
  1765.             }
  1766.             if ($x != && $y == 0{
  1767.                 $pnnAct 1// Top right
  1768.             }
  1769.             if ($x == && $y != 0{
  1770.                 $pnnAct 2// Bottom left
  1771.             }
  1772.             if ($x == && $y == 0{
  1773.                 $pnnAct 3// Top left
  1774.             }
  1775.         }
  1776.  
  1777.         $this->_active_pane = $pnnAct// Used in _writeSelection
  1778.  
  1779.         $header     pack("vv",    $record$length);
  1780.         $data       pack("vvvvv"$x$y$rwTop$colLeft$pnnAct);
  1781.         $this->_append($header $data);
  1782.     }
  1783.  
  1784.     /**
  1785.      * Store the page setup SETUP BIFF record.
  1786.      */
  1787.     private function _writeSetup()
  1788.     {
  1789.         $record       0x00A1;                  // Record identifier
  1790.         $length       0x0022;                  // Number of bytes to follow
  1791.  
  1792.         $iPaperSize   $this->_phpSheet->getPageSetup()->getPaperSize();    // Paper size
  1793.  
  1794.         $iScale $this->_phpSheet->getPageSetup()->getScale(?
  1795.             $this->_phpSheet->getPageSetup()->getScale(100;   // Print scaling factor
  1796.  
  1797.         $iPageStart   0x01;                 // Starting page number
  1798.         $iFitWidth    = (int) $this->_phpSheet->getPageSetup()->getFitToWidth();    // Fit to number of pages wide
  1799.         $iFitHeight    = (int) $this->_phpSheet->getPageSetup()->getFitToHeight();    // Fit to number of pages high
  1800.         $grbit        0x00;                 // Option flags
  1801.         $iRes         0x0258;               // Print resolution
  1802.         $iVRes        0x0258;               // Vertical print resolution
  1803.  
  1804.         $numHdr       $this->_phpSheet->getPageMargins()->getHeader();  // Header Margin
  1805.  
  1806.         $numFtr       $this->_phpSheet->getPageMargins()->getFooter();   // Footer Margin
  1807.         $iCopies      0x01;                 // Number of copies
  1808.  
  1809.         $fLeftToRight 0x0;                     // Print over then down
  1810.  
  1811.         // Page orientation
  1812.         $fLandscape ($this->_phpSheet->getPageSetup()->getOrientation(== PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE?
  1813.             0x0 0x1;
  1814.  
  1815.         $fNoPls       0x0;                     // Setup not read from printer
  1816.         $fNoColor     0x0;                     // Print black and white
  1817.         $fDraft       0x0;                     // Print draft quality
  1818.         $fNotes       0x0;                     // Print notes
  1819.         $fNoOrient    0x0;                     // Orientation not set
  1820.         $fUsePage     0x0;                     // Use custom starting page
  1821.  
  1822.         $grbit           $fLeftToRight;
  1823.         $grbit          |= $fLandscape    << 1;
  1824.         $grbit          |= $fNoPls        << 2;
  1825.         $grbit          |= $fNoColor      << 3;
  1826.         $grbit          |= $fDraft        << 4;
  1827.         $grbit          |= $fNotes        << 5;
  1828.         $grbit          |= $fNoOrient     << 6;
  1829.         $grbit          |= $fUsePage      << 7;
  1830.  
  1831.         $numHdr pack("d"$numHdr);
  1832.         $numFtr pack("d"$numFtr);
  1833.         if (PHPExcel_Writer_Excel5_BIFFwriter::getByteOrder()) // if it's Big Endian
  1834.             $numHdr strrev($numHdr);
  1835.             $numFtr strrev($numFtr);
  1836.         }
  1837.  
  1838.         $header pack("vv"$record$length);
  1839.         $data1  pack("vvvvvvvv"$iPaperSize,
  1840.                                    $iScale,
  1841.                                    $iPageStart,
  1842.                                    $iFitWidth,
  1843.                                    $iFitHeight,
  1844.                                    $grbit,
  1845.                                    $iRes,
  1846.                                    $iVRes);
  1847.         $data2  $numHdr.$numFtr;
  1848.         $data3  pack("v"$iCopies);
  1849.         $this->_append($header $data1 $data2 $data3);
  1850.     }
  1851.  
  1852.     /**
  1853.      * Store the header caption BIFF record.
  1854.      */
  1855.     private function _writeHeader()
  1856.     {
  1857.         $record  0x0014;               // Record identifier
  1858.  
  1859.         /* removing for now
  1860.         // need to fix character count (multibyte!)
  1861.         if (strlen($this->_phpSheet->getHeaderFooter()->getOddHeader()) <= 255) {
  1862.             $str      = $this->_phpSheet->getHeaderFooter()->getOddHeader();       // header string
  1863.         } else {
  1864.             $str = '';
  1865.         }
  1866.         */
  1867.  
  1868.         if ($this->_BIFF_version == 0x0600{
  1869.             $recordData PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($this->_phpSheet->getHeaderFooter()->getOddHeader());
  1870.             $length strlen($recordData);
  1871.         else {
  1872.             $cch      strlen($this->_phpSheet->getHeaderFooter()->getOddHeader());         // Length of header string
  1873.             $length  $cch;             // Bytes to follow
  1874.             $data      pack("C",  $cch);
  1875.             $recordData $data $this->_phpSheet->getHeaderFooter()->getOddHeader();
  1876.         }
  1877.  
  1878.         $header   pack("vv"$record$length);
  1879.  
  1880.         $this->_append($header $recordData);
  1881.     }
  1882.  
  1883.     /**
  1884.      * Store the footer caption BIFF record.
  1885.      */
  1886.     private function _writeFooter()
  1887.     {
  1888.         $record  0x0015;               // Record identifier
  1889.  
  1890.         /* removing for now
  1891.         // need to fix character count (multibyte!)
  1892.         if (strlen($this->_phpSheet->getHeaderFooter()->getOddFooter()) <= 255) {
  1893.             $str = $this->_phpSheet->getHeaderFooter()->getOddFooter();
  1894.         } else {
  1895.             $str = '';
  1896.         }
  1897.         */
  1898.  
  1899.         if ($this->_BIFF_version == 0x0600{
  1900.             $recordData PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($this->_phpSheet->getHeaderFooter()->getOddFooter());
  1901.             $length strlen($recordData);
  1902.         else {
  1903.             $cch      strlen($this->_phpSheet->getHeaderFooter()->getOddFooter());         // Length of footer string
  1904.             $length  $cch;
  1905.             $data      pack("C",  $cch);
  1906.             $recordData $data $this->_phpSheet->getHeaderFooter()->getOddFooter();
  1907.         }
  1908.  
  1909.         $header    pack("vv"$record$length);
  1910.  
  1911.         $this->_append($header $recordData);
  1912.     }
  1913.  
  1914.     /**
  1915.      * Store the horizontal centering HCENTER BIFF record.
  1916.      *
  1917.      * @access private
  1918.      */
  1919.     private function _writeHcenter()
  1920.     {
  1921.         $record   0x0083;              // Record identifier
  1922.         $length   0x0002;              // Bytes to follow
  1923.  
  1924.         $fHCenter $this->_phpSheet->getPageSetup()->getHorizontalCentered(0;     // Horizontal centering
  1925.  
  1926.         $header    pack("vv"$record$length);
  1927.         $data      pack("v",  $fHCenter);
  1928.  
  1929.         $this->_append($header.$data);
  1930.     }
  1931.  
  1932.     /**
  1933.      * Store the vertical centering VCENTER BIFF record.
  1934.      */
  1935.     private function _writeVcenter()
  1936.     {
  1937.         $record   0x0084;              // Record identifier
  1938.         $length   0x0002;              // Bytes to follow
  1939.  
  1940.         $fVCenter $this->_phpSheet->getPageSetup()->getVerticalCentered(0;     // Horizontal centering
  1941.  
  1942.         $header    pack("vv"$record$length);
  1943.         $data      pack("v",  $fVCenter);
  1944.         $this->_append($header $data);
  1945.     }
  1946.  
  1947.     /**
  1948.      * Store the LEFTMARGIN BIFF record.
  1949.      */
  1950.     private function _writeMarginLeft()
  1951.     {
  1952.         $record  0x0026;                   // Record identifier
  1953.         $length  0x0008;                   // Bytes to follow
  1954.  
  1955.         $margin  $this->_phpSheet->getPageMargins()->getLeft();     // Margin in inches
  1956.  
  1957.         $header    pack("vv",  $record$length);
  1958.         $data      pack("d",   $margin);
  1959.         if (PHPExcel_Writer_Excel5_BIFFwriter::getByteOrder()) // if it's Big Endian
  1960.             $data strrev($data);
  1961.         }
  1962.  
  1963.         $this->_append($header $data);
  1964.     }
  1965.  
  1966.     /**
  1967.      * Store the RIGHTMARGIN BIFF record.
  1968.      */
  1969.     private function _writeMarginRight()
  1970.     {
  1971.         $record  0x0027;                   // Record identifier
  1972.         $length  0x0008;                   // Bytes to follow
  1973.  
  1974.         $margin  $this->_phpSheet->getPageMargins()->getRight();     // Margin in inches
  1975.  
  1976.         $header    pack("vv",  $record$length);
  1977.         $data      pack("d",   $margin);
  1978.         if (PHPExcel_Writer_Excel5_BIFFwriter::getByteOrder()) // if it's Big Endian
  1979.             $data strrev($data);
  1980.         }
  1981.  
  1982.         $this->_append($header $data);
  1983.     }
  1984.  
  1985.     /**
  1986.      * Store the TOPMARGIN BIFF record.
  1987.      */
  1988.     private function _writeMarginTop()
  1989.     {
  1990.         $record  0x0028;                   // Record identifier
  1991.         $length  0x0008;                   // Bytes to follow
  1992.  
  1993.         $margin  $this->_phpSheet->getPageMargins()->getTop();     // Margin in inches
  1994.  
  1995.         $header    pack("vv",  $record$length);
  1996.         $data      pack("d",   $margin);
  1997.         if (PHPExcel_Writer_Excel5_BIFFwriter::getByteOrder()) // if it's Big Endian
  1998.             $data strrev($data);
  1999.         }
  2000.  
  2001.         $this->_append($header $data);
  2002.     }
  2003.  
  2004.     /**
  2005.      * Store the BOTTOMMARGIN BIFF record.
  2006.      */
  2007.     private function _writeMarginBottom()
  2008.     {
  2009.         $record  0x0029;                   // Record identifier
  2010.         $length  0x0008;                   // Bytes to follow
  2011.  
  2012.         $margin  $this->_phpSheet->getPageMargins()->getBottom();     // Margin in inches
  2013.  
  2014.         $header    pack("vv",  $record$length);
  2015.         $data      pack("d",   $margin);
  2016.         if (PHPExcel_Writer_Excel5_BIFFwriter::getByteOrder()) // if it's Big Endian
  2017.             $data strrev($data);
  2018.         }
  2019.  
  2020.         $this->_append($header $data);
  2021.     }
  2022.  
  2023.     /**
  2024.      * Write the PRINTHEADERS BIFF record.
  2025.      */
  2026.     private function _writePrintHeaders()
  2027.     {
  2028.         $record      0x002a;                   // Record identifier
  2029.         $length      0x0002;                   // Bytes to follow
  2030.  
  2031.         $fPrintRwCol $this->_print_headers;     // Boolean flag
  2032.  
  2033.         $header      pack("vv"$record$length);
  2034.         $data        pack("v"$fPrintRwCol);
  2035.         $this->_append($header $data);
  2036.     }
  2037.  
  2038.     /**
  2039.      * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the
  2040.      * GRIDSET record.
  2041.      */
  2042.     private function _writePrintGridlines()
  2043.     {
  2044.         $record      0x002b;                    // Record identifier
  2045.         $length      0x0002;                    // Bytes to follow
  2046.  
  2047.         $fPrintGrid  $this->_phpSheet->getPrintGridlines(0;    // Boolean flag
  2048.  
  2049.         $header      pack("vv"$record$length);
  2050.         $data        pack("v"$fPrintGrid);
  2051.         $this->_append($header $data);
  2052.     }
  2053.  
  2054.     /**
  2055.      * Write the GRIDSET BIFF record. Must be used in conjunction with the
  2056.      * PRINTGRIDLINES record.
  2057.      */
  2058.     private function _writeGridset()
  2059.     {
  2060.         $record      0x0082;                        // Record identifier
  2061.         $length      0x0002;                        // Bytes to follow
  2062.  
  2063.         $fGridSet    !$this->_phpSheet->getPrintGridlines();     // Boolean flag
  2064.  
  2065.         $header      pack("vv",  $record$length);
  2066.         $data        pack("v",   $fGridSet);
  2067.         $this->_append($header $data);
  2068.     }
  2069.  
  2070.     /**
  2071.      * Write the GUTS BIFF record. This is used to configure the gutter margins
  2072.      * where Excel outline symbols are displayed. The visibility of the gutters is
  2073.      * controlled by a flag in WSBOOL.
  2074.      *
  2075.      * @see _writeWsbool()
  2076.      */
  2077.     private  function _writeGuts()
  2078.     {
  2079.         $record      0x0080;   // Record identifier
  2080.         $length      0x0008;   // Bytes to follow
  2081.  
  2082.         $dxRwGut     0x0000;   // Size of row gutter
  2083.         $dxColGut    0x0000;   // Size of col gutter
  2084.  
  2085.         // determine maximum row outline level
  2086.         $maxRowOutlineLevel 0;
  2087.         foreach ($this->_phpSheet->getRowDimensions(as $rowDimension{
  2088.             $maxRowOutlineLevel max($maxRowOutlineLevel$rowDimension->getOutlineLevel());
  2089.         }
  2090.  
  2091.         $col_level   0;
  2092.  
  2093.         // Calculate the maximum column outline level. The equivalent calculation
  2094.         // for the row outline level is carried out in _writeRow().
  2095.         $colcount count($this->_colinfo);
  2096.         for ($i 0$i $colcount++$i{
  2097.             $col_level max($this->_colinfo[$i][5]$col_level);
  2098.         }
  2099.  
  2100.         // Set the limits for the outline levels (0 <= x <= 7).
  2101.         $col_level max(0min($col_level7));
  2102.  
  2103.         // The displayed level is one greater than the max outline levels
  2104.         if ($maxRowOutlineLevel{
  2105.             ++$maxRowOutlineLevel;
  2106.         }
  2107.         if ($col_level{
  2108.             ++$col_level;
  2109.         }
  2110.  
  2111.         $header      pack("vv",   $record$length);
  2112.         $data        pack("vvvv"$dxRwGut$dxColGut$maxRowOutlineLevel$col_level);
  2113.  
  2114.         $this->_append($header.$data);
  2115.     }
  2116.  
  2117.  
  2118.     /**
  2119.      * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction
  2120.      * with the SETUP record.
  2121.      */
  2122.     private function _writeWsbool()
  2123.     {
  2124.         $record      0x0081;   // Record identifier
  2125.         $length      0x0002;   // Bytes to follow
  2126.         $grbit       0x0000;
  2127.  
  2128.         // The only option that is of interest is the flag for fit to page. So we
  2129.         // set all the options in one go.
  2130.         //
  2131.         // Set the option flags
  2132.         $grbit |= 0x0001;                           // Auto page breaks visible
  2133.         if ($this->_outline_style{
  2134.             $grbit |= 0x0020// Auto outline styles
  2135.         }
  2136.         if ($this->_phpSheet->getShowSummaryBelow()) {
  2137.             $grbit |= 0x0040// Outline summary below
  2138.         }
  2139.         if ($this->_phpSheet->getShowSummaryRight()) {
  2140.             $grbit |= 0x0080// Outline summary right
  2141.         }
  2142.         if ($this->_phpSheet->getPageSetup()->getFitToPage()) {
  2143.             $grbit |= 0x0100// Page setup fit to page
  2144.         }
  2145.         if ($this->_outline_on{
  2146.             $grbit |= 0x0400// Outline symbols displayed
  2147.         }
  2148.  
  2149.         $header      pack("vv"$record$length);
  2150.         $data        pack("v",  $grbit);
  2151.         $this->_append($header $data);
  2152.     }
  2153.  
  2154.     /**
  2155.      * Write the HORIZONTALPAGEBREAKS and VERTICALPAGEBREAKS BIFF records.
  2156.      */
  2157.     private function _writeBreaks()
  2158.     {
  2159.         // initialize
  2160.         $vbreaks array();
  2161.         $hbreaks array();
  2162.  
  2163.         foreach ($this->_phpSheet->getBreaks(as $cell => $breakType{
  2164.             // Fetch coordinates
  2165.             $coordinates PHPExcel_Cell::coordinateFromString($cell);
  2166.  
  2167.             // Decide what to do by the type of break
  2168.             switch ($breakType{
  2169.                 case PHPExcel_Worksheet::BREAK_COLUMN:
  2170.                     // Add to list of vertical breaks
  2171.                     $vbreaks[PHPExcel_Cell::columnIndexFromString($coordinates[0]1;
  2172.                     break;
  2173.  
  2174.                 case PHPExcel_Worksheet::BREAK_ROW:
  2175.                     // Add to list of horizontal breaks
  2176.                     $hbreaks[$coordinates[1];
  2177.                     break;
  2178.  
  2179.                 case PHPExcel_Worksheet::BREAK_NONE:
  2180.                 default:
  2181.                     // Nothing to do
  2182.                     break;
  2183.             }
  2184.         }
  2185.  
  2186.         //horizontal page breaks
  2187.         if (count($hbreaks0{
  2188.  
  2189.             // Sort and filter array of page breaks
  2190.             sort($hbreaksSORT_NUMERIC);
  2191.             if ($hbreaks[0== 0// don't use first break if it's 0
  2192.                 array_shift($hbreaks);
  2193.             }
  2194.  
  2195.             $record  0x001b;               // Record identifier
  2196.             $cbrk    count($hbreaks);       // Number of page breaks
  2197.             if ($this->_BIFF_version == 0x0600{
  2198.                 $length  $cbrk;      // Bytes to follow
  2199.             else {
  2200.                 $length  $cbrk;      // Bytes to follow
  2201.             }
  2202.  
  2203.             $header  pack("vv"$record$length);
  2204.             $data    pack("v",  $cbrk);
  2205.  
  2206.             // Append each page break
  2207.             foreach ($hbreaks as $hbreak{
  2208.                 if ($this->_BIFF_version == 0x0600{
  2209.                     $data .= pack("vvv"$hbreak0x00000x00ff);
  2210.                 else {
  2211.                     $data .= pack("v"$hbreak);
  2212.                 }
  2213.             }
  2214.  
  2215.             $this->_append($header $data);
  2216.         }
  2217.  
  2218.         // vertical page breaks
  2219.         if (count($vbreaks0{
  2220.  
  2221.             // 1000 vertical pagebreaks appears to be an internal Excel 5 limit.
  2222.             // It is slightly higher in Excel 97/200, approx. 1026
  2223.             $vbreaks array_slice($vbreaks01000);
  2224.  
  2225.             // Sort and filter array of page breaks
  2226.             sort($vbreaksSORT_NUMERIC);
  2227.             if ($vbreaks[0== 0// don't use first break if it's 0
  2228.                 array_shift($vbreaks);
  2229.             }
  2230.  
  2231.             $record  0x001a;               // Record identifier
  2232.             $cbrk    count($vbreaks);       // Number of page breaks
  2233.             if ($this->_BIFF_version == 0x0600{
  2234.                 $length  $cbrk;      // Bytes to follow
  2235.             else {
  2236.                 $length  $cbrk;      // Bytes to follow
  2237.             }
  2238.  
  2239.             $header  pack("vv",  $record$length);
  2240.             $data    pack("v",   $cbrk);
  2241.  
  2242.             // Append each page break
  2243.             foreach ($vbreaks as $vbreak{
  2244.                 if ($this->_BIFF_version == 0x0600{
  2245.                     $data .= pack("vvv"$vbreak0x00000xffff);
  2246.                 else {
  2247.                     $data .= pack("v"$vbreak);
  2248.                 }
  2249.             }
  2250.  
  2251.             $this->_append($header $data);
  2252.         }
  2253.     }
  2254.  
  2255.     /**
  2256.      * Set the Biff PROTECT record to indicate that the worksheet is protected.
  2257.      */
  2258.     private function _writeProtect()
  2259.     {
  2260.         // Exit unless sheet protection has been specified
  2261.         if (!$this->_phpSheet->getProtection()->getSheet()) {
  2262.             return;
  2263.         }
  2264.  
  2265.         $record      0x0012;             // Record identifier
  2266.         $length      0x0002;             // Bytes to follow
  2267.  
  2268.         $fLock       1;    // Worksheet is protected
  2269.  
  2270.         $header      pack("vv"$record$length);
  2271.         $data        pack("v",  $fLock);
  2272.  
  2273.         $this->_append($header.$data);
  2274.     }
  2275.  
  2276.     /**
  2277.      * Write SCENPROTECT
  2278.      */
  2279.     private function _writeScenProtect()
  2280.     {
  2281.         // Exit if sheet protection is not active
  2282.         if (!$this->_phpSheet->getProtection()->getSheet()) {
  2283.             return;
  2284.         }
  2285.  
  2286.         // Exit if scenarios are not protected
  2287.         if (!$this->_phpSheet->getProtection()->getScenarios()) {
  2288.             return;
  2289.         }
  2290.  
  2291.         $record 0x00DD// Record identifier
  2292.         $length 0x0002// Bytes to follow
  2293.  
  2294.         $header pack('vv'$record$length);
  2295.         $data pack('v'1);
  2296.  
  2297.         $this->_append($header $data);
  2298.     }
  2299.  
  2300.     /**
  2301.      * Write OBJECTPROTECT
  2302.      */
  2303.     private function _writeObjectProtect()
  2304.     {
  2305.         // Exit if sheet protection is not active
  2306.         if (!$this->_phpSheet->getProtection()->getSheet()) {
  2307.             return;
  2308.         }
  2309.  
  2310.         // Exit if objects are not protected
  2311.         if (!$this->_phpSheet->getProtection()->getObjects()) {
  2312.             return;
  2313.         }
  2314.  
  2315.         $record 0x0063// Record identifier
  2316.         $length 0x0002// Bytes to follow
  2317.  
  2318.         $header pack('vv'$record$length);
  2319.         $data pack('v'1);
  2320.  
  2321.         $this->_append($header $data);
  2322.     }
  2323.  
  2324.     /**
  2325.      * Write the worksheet PASSWORD record.
  2326.      */
  2327.     private function _writePassword()
  2328.     {
  2329.         // Exit unless sheet protection and password have been specified
  2330.         if (!$this->_phpSheet->getProtection()->getSheet(|| !$this->_phpSheet->getProtection()->getPassword()) {
  2331.             return;
  2332.         }
  2333.  
  2334.         $record      0x0013;               // Record identifier
  2335.         $length      0x0002;               // Bytes to follow
  2336.  
  2337.         $wPassword   hexdec($this->_phpSheet->getProtection()->getPassword());     // Encoded password
  2338.  
  2339.         $header      pack("vv"$record$length);
  2340.         $data        pack("v",  $wPassword);
  2341.  
  2342.         $this->_append($header $data);
  2343.     }
  2344.  
  2345.  
  2346.     /**
  2347.      * Insert a 24bit bitmap image in a worksheet.
  2348.      *
  2349.      * @access public
  2350.      * @param integer $row     The row we are going to insert the bitmap into
  2351.      * @param integer $col     The column we are going to insert the bitmap into
  2352.      * @param mixed   $bitmap  The bitmap filename or GD-image resource
  2353.      * @param integer $x       The horizontal position (offset) of the image inside the cell.
  2354.      * @param integer $y       The vertical position (offset) of the image inside the cell.
  2355.      * @param float   $scale_x The horizontal scale
  2356.      * @param float   $scale_y The vertical scale
  2357.      */
  2358.     function insertBitmap($row$col$bitmap$x 0$y 0$scale_x 1$scale_y 1)
  2359.     {
  2360.         $bitmap_array (is_resource($bitmap$this->_processBitmapGd($bitmap$this->_processBitmap($bitmap));
  2361.         list($width$height$size$data$bitmap_array//$this->_processBitmap($bitmap);
  2362.  
  2363.         // Scale the frame of the image.
  2364.         $width  *= $scale_x;
  2365.         $height *= $scale_y;
  2366.  
  2367.         // Calculate the vertices of the image and write the OBJ record
  2368.         $this->_positionImage($col$row$x$y$width$height);
  2369.  
  2370.         // Write the IMDATA record to store the bitmap data
  2371.         $record      0x007f;
  2372.         $length      $size;
  2373.         $cf          0x09;
  2374.         $env         0x01;
  2375.         $lcb         $size;
  2376.  
  2377.         $header      pack("vvvvV"$record$length$cf$env$lcb);
  2378.         $this->_append($header.$data);
  2379.     }
  2380.  
  2381.     /**
  2382.      * Calculate the vertices that define the position of the image as required by
  2383.      * the OBJ record.
  2384.      *
  2385.      *         +------------+------------+
  2386.      *         |     A      |      B     |
  2387.      *   +-----+------------+------------+
  2388.      *   |     |(x1,y1)     |            |
  2389.      *   |  1  |(A1)._______|______      |
  2390.      *   |     |    |              |     |
  2391.      *   |     |    |              |     |
  2392.      *   +-----+----|    BITMAP    |-----+
  2393.      *   |     |    |              |     |
  2394.      *   |  2  |    |______________.     |
  2395.      *   |     |            |        (B2)|
  2396.      *   |     |            |     (x2,y2)|
  2397.      *   +---- +------------+------------+
  2398.      *
  2399.      * Example of a bitmap that covers some of the area from cell A1 to cell B2.
  2400.      *
  2401.      * Based on the width and height of the bitmap we need to calculate 8 vars:
  2402.      *     $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
  2403.      * The width and height of the cells are also variable and have to be taken into
  2404.      * account.
  2405.      * The values of $col_start and $row_start are passed in from the calling
  2406.      * function. The values of $col_end and $row_end are calculated by subtracting
  2407.      * the width and height of the bitmap from the width and height of the
  2408.      * underlying cells.
  2409.      * The vertices are expressed as a percentage of the underlying cell width as
  2410.      * follows (rhs values are in pixels):
  2411.      *
  2412.      *       x1 = X / W *1024
  2413.      *       y1 = Y / H *256
  2414.      *       x2 = (X-1) / W *1024
  2415.      *       y2 = (Y-1) / H *256
  2416.      *
  2417.      *       Where:  X is distance from the left side of the underlying cell
  2418.      *               Y is distance from the top of the underlying cell
  2419.      *               W is the width of the cell
  2420.      *               H is the height of the cell
  2421.      * The SDK incorrectly states that the height should be expressed as a
  2422.      *        percentage of 1024.
  2423.      *
  2424.      * @access private
  2425.      * @param integer $col_start Col containing upper left corner of object
  2426.      * @param integer $row_start Row containing top left corner of object
  2427.      * @param integer $x1        Distance to left side of object
  2428.      * @param integer $y1        Distance to top of object
  2429.      * @param integer $width     Width of image frame
  2430.      * @param integer $height    Height of image frame
  2431.      */
  2432.     function _positionImage($col_start$row_start$x1$y1$width$height)
  2433.     {
  2434.         // Initialise end cell to the same as the start cell
  2435.         $col_end    $col_start;  // Col containing lower right corner of object
  2436.         $row_end    $row_start;  // Row containing bottom right corner of object
  2437.  
  2438.         // Zero the specified offset if greater than the cell dimensions
  2439.         if ($x1 >= PHPExcel_Shared_Excel5::sizeCol($this->_phpSheetPHPExcel_Cell::stringFromColumnIndex($col_start))) {
  2440.             $x1 0;
  2441.         }
  2442.         if ($y1 >= PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet$row_start 1)) {
  2443.             $y1 0;
  2444.         }
  2445.  
  2446.         $width      $width  $x1 -1;
  2447.         $height     $height $y1 -1;
  2448.  
  2449.         // Subtract the underlying cell widths to find the end cell of the image
  2450.         while ($width >= PHPExcel_Shared_Excel5::sizeCol($this->_phpSheetPHPExcel_Cell::stringFromColumnIndex($col_end))) {
  2451.             $width -= PHPExcel_Shared_Excel5::sizeCol($this->_phpSheetPHPExcel_Cell::stringFromColumnIndex($col_end));
  2452.             ++$col_end;
  2453.         }
  2454.  
  2455.         // Subtract the underlying cell heights to find the end cell of the image
  2456.         while ($height >= PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet$row_end 1)) {
  2457.             $height -= PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet$row_end 1);
  2458.             ++$row_end;
  2459.         }
  2460.  
  2461.         // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
  2462.         // with zero eight or width.
  2463.         //
  2464.         if (PHPExcel_Shared_Excel5::sizeCol($this->_phpSheetPHPExcel_Cell::stringFromColumnIndex($col_start)) == 0{
  2465.             return;
  2466.         }
  2467.         if (PHPExcel_Shared_Excel5::sizeCol($this->_phpSheetPHPExcel_Cell::stringFromColumnIndex($col_end))   == 0{
  2468.             return;
  2469.         }
  2470.         if (PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet$row_start 1== 0{
  2471.             return;
  2472.         }
  2473.         if (PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet$row_end 1)   == 0{
  2474.             return;
  2475.         }
  2476.  
  2477.         // Convert the pixel values to the percentage value expected by Excel
  2478.         $x1 $x1     PHPExcel_Shared_Excel5::sizeCol($this->_phpSheetPHPExcel_Cell::stringFromColumnIndex($col_start))   1024;
  2479.         $y1 $y1     PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet$row_start 1)   *  256;
  2480.         $x2 $width  PHPExcel_Shared_Excel5::sizeCol($this->_phpSheetPHPExcel_Cell::stringFromColumnIndex($col_end))     1024// Distance to right side of object
  2481.         $y2 $height PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet$row_end 1)     *  256// Distance to bottom of object
  2482.  
  2483.         $this->_writeObjPicture($col_start$x1,
  2484.                                  $row_start$y1,
  2485.                                  $col_end$x2,
  2486.                                  $row_end$y2);
  2487.     }
  2488.  
  2489.     /**
  2490.      * Store the OBJ record that precedes an IMDATA record. This could be generalise
  2491.      * to support other Excel objects.
  2492.      *
  2493.      * @param integer $colL Column containing upper left corner of object
  2494.      * @param integer $dxL  Distance from left side of cell
  2495.      * @param integer $rwT  Row containing top left corner of object
  2496.      * @param integer $dyT  Distance from top of cell
  2497.      * @param integer $colR Column containing lower right corner of object
  2498.      * @param integer $dxR  Distance from right of cell
  2499.      * @param integer $rwB  Row containing bottom right corner of object
  2500.      * @param integer $dyB  Distance from bottom of cell
  2501.      */
  2502.     private function _writeObjPicture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB)
  2503.     {
  2504.         $record      0x005d;   // Record identifier
  2505.         $length      0x003c;   // Bytes to follow
  2506.  
  2507.         $cObj        0x0001;   // Count of objects in file (set to 1)
  2508.         $OT          0x0008;   // Object type. 8 = Picture
  2509.         $id          0x0001;   // Object ID
  2510.         $grbit       0x0614;   // Option flags
  2511.  
  2512.         $cbMacro     0x0000;   // Length of FMLA structure
  2513.         $Reserved1   0x0000;   // Reserved
  2514.         $Reserved2   0x0000;   // Reserved
  2515.  
  2516.         $icvBack     0x09;     // Background colour
  2517.         $icvFore     0x09;     // Foreground colour
  2518.         $fls         0x00;     // Fill pattern
  2519.         $fAuto       0x00;     // Automatic fill
  2520.         $icv         0x08;     // Line colour
  2521.         $lns         0xff;     // Line style
  2522.         $lnw         0x01;     // Line weight
  2523.         $fAutoB      0x00;     // Automatic border
  2524.         $frs         0x0000;   // Frame style
  2525.         $cf          0x0009;   // Image format, 9 = bitmap
  2526.         $Reserved3   0x0000;   // Reserved
  2527.         $cbPictFmla  0x0000;   // Length of FMLA structure
  2528.         $Reserved4   0x0000;   // Reserved
  2529.         $grbit2      0x0001;   // Option flags
  2530.         $Reserved5   0x0000;   // Reserved
  2531.  
  2532.  
  2533.         $header      pack("vv"$record$length);
  2534.         $data        pack("V"$cObj);
  2535.         $data       .= pack("v"$OT);
  2536.         $data       .= pack("v"$id);
  2537.         $data       .= pack("v"$grbit);
  2538.         $data       .= pack("v"$colL);
  2539.         $data       .= pack("v"$dxL);
  2540.         $data       .= pack("v"$rwT);
  2541.         $data       .= pack("v"$dyT);
  2542.         $data       .= pack("v"$colR);
  2543.         $data       .= pack("v"$dxR);
  2544.         $data       .= pack("v"$rwB);
  2545.         $data       .= pack("v"$dyB);
  2546.         $data       .= pack("v"$cbMacro);
  2547.         $data       .= pack("V"$Reserved1);
  2548.         $data       .= pack("v"$Reserved2);
  2549.         $data       .= pack("C"$icvBack);
  2550.         $data       .= pack("C"$icvFore);
  2551.         $data       .= pack("C"$fls);
  2552.         $data       .= pack("C"$fAuto);
  2553.         $data       .= pack("C"$icv);
  2554.         $data       .= pack("C"$lns);
  2555.         $data       .= pack("C"$lnw);
  2556.         $data       .= pack("C"$fAutoB);
  2557.         $data       .= pack("v"$frs);
  2558.         $data       .= pack("V"$cf);
  2559.         $data       .= pack("v"$Reserved3);
  2560.         $data       .= pack("v"$cbPictFmla);
  2561.         $data       .= pack("v"$Reserved4);
  2562.         $data       .= pack("v"$grbit2);
  2563.         $data       .= pack("V"$Reserved5);
  2564.  
  2565.         $this->_append($header $data);
  2566.     }
  2567.  
  2568.     /**
  2569.      * Convert a GD-image into the internal format.
  2570.      *
  2571.      * @access private
  2572.      * @param resource $image The image to process
  2573.      * @return array Array with data and properties of the bitmap
  2574.      */
  2575.     function _processBitmapGd($image{
  2576.         $width imagesx($image);
  2577.         $height imagesy($image);
  2578.  
  2579.         $data pack("Vvvvv"0x000c$width$height0x010x18);
  2580.         for ($j=$height$j--{
  2581.             for ($i=0$i $width++$i{
  2582.                 $color imagecolorsforindex($imageimagecolorat($image$i$j));
  2583.                 foreach (array("red""green""blue"as $key{
  2584.                     $color[$key$color[$keyround((255 $color[$key]$color["alpha"127);
  2585.                 }
  2586.                 $data .= chr($color["blue"]chr($color["green"]chr($color["red"]);
  2587.             }
  2588.             if (3*$width 4{
  2589.                 $data .= str_repeat("\x00"3*$width 4);
  2590.             }
  2591.         }
  2592.  
  2593.         return array($width$heightstrlen($data)$data);
  2594.     }
  2595.  
  2596.     /**
  2597.      * Convert a 24 bit bitmap into the modified internal format used by Windows.
  2598.      * This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the
  2599.      * MSDN library.
  2600.      *
  2601.      * @access private
  2602.      * @param string $bitmap The bitmap to process
  2603.      * @return array Array with data and properties of the bitmap
  2604.      */
  2605.     function _processBitmap($bitmap)
  2606.     {
  2607.         // Open file.
  2608.         $bmp_fd @fopen($bitmap,"rb");
  2609.         if (!$bmp_fd{
  2610.             throw new Exception("Couldn't import $bitmap");
  2611.         }
  2612.  
  2613.         // Slurp the file into a string.
  2614.         $data fread($bmp_fdfilesize($bitmap));
  2615.  
  2616.         // Check that the file is big enough to be a bitmap.
  2617.         if (strlen($data<= 0x36{
  2618.             throw new Exception("$bitmap doesn't contain enough data.\n");
  2619.         }
  2620.  
  2621.         // The first 2 bytes are used to identify the bitmap.
  2622.         $identity unpack("A2ident"$data);
  2623.         if ($identity['ident'!= "BM"{
  2624.             throw new Exception("$bitmap doesn't appear to be a valid bitmap image.\n");
  2625.         }
  2626.  
  2627.         // Remove bitmap data: ID.
  2628.         $data substr($data2);
  2629.  
  2630.         // Read and remove the bitmap size. This is more reliable than reading
  2631.         // the data size at offset 0x22.
  2632.         //
  2633.         $size_array   unpack("Vsa"substr($data04));
  2634.         $size   $size_array['sa'];
  2635.         $data   substr($data4);
  2636.         $size  -= 0x36// Subtract size of bitmap header.
  2637.         $size  += 0x0C// Add size of BIFF header.
  2638.  
  2639.         // Remove bitmap data: reserved, offset, header length.
  2640.         $data substr($data12);
  2641.  
  2642.         // Read and remove the bitmap width and height. Verify the sizes.
  2643.         $width_and_height unpack("V2"substr($data08));
  2644.         $width  $width_and_height[1];
  2645.         $height $width_and_height[2];
  2646.         $data   substr($data8);
  2647.         if ($width 0xFFFF{
  2648.             throw new Exception("$bitmap: largest image width supported is 65k.\n");
  2649.         }
  2650.         if ($height 0xFFFF{
  2651.             throw new Exception("$bitmap: largest image height supported is 65k.\n");
  2652.         }
  2653.  
  2654.         // Read and remove the bitmap planes and bpp data. Verify them.
  2655.         $planes_and_bitcount unpack("v2"substr($data04));
  2656.         $data substr($data4);
  2657.         if ($planes_and_bitcount[2!= 24// Bitcount
  2658.             throw new Exception("$bitmap isn't a 24bit true color bitmap.\n");
  2659.         }
  2660.         if ($planes_and_bitcount[1!= 1{
  2661.             throw new Exception("$bitmap: only 1 plane supported in bitmap image.\n");
  2662.         }
  2663.  
  2664.         // Read and remove the bitmap compression. Verify compression.
  2665.         $compression unpack("Vcomp"substr($data04));
  2666.         $data substr($data4);
  2667.  
  2668.         //$compression = 0;
  2669.         if ($compression['comp'!= 0{
  2670.             throw new Exception("$bitmap: compression not supported in bitmap image.\n");
  2671.         }
  2672.  
  2673.         // Remove bitmap data: data size, hres, vres, colours, imp. colours.
  2674.         $data substr($data20);
  2675.  
  2676.         // Add the BITMAPCOREHEADER data
  2677.         $header  pack("Vvvvv"0x000c$width$height0x010x18);
  2678.         $data    $header $data;
  2679.  
  2680.         return (array($width$height$size$data));
  2681.     }
  2682.  
  2683.     /**
  2684.      * Store the window zoom factor. This should be a reduced fraction but for
  2685.      * simplicity we will store all fractions with a numerator of 100.
  2686.      */
  2687.     private function _writeZoom()
  2688.     {
  2689.         // If scale is 100 we don't need to write a record
  2690.         if ($this->_phpSheet->getSheetView()->getZoomScale(== 100{
  2691.             return;
  2692.         }
  2693.  
  2694.         $record      0x00A0;               // Record identifier
  2695.         $length      0x0004;               // Bytes to follow
  2696.  
  2697.         $header      pack("vv"$record$length);
  2698.         $data        pack("vv"$this->_phpSheet->getSheetView()->getZoomScale()100);
  2699.         $this->_append($header $data);
  2700.     }
  2701.  
  2702.     /**
  2703.      * Get Escher object
  2704.      *
  2705.      * @return PHPExcel_Shared_Escher 
  2706.      */
  2707.     public function getEscher()
  2708.     {
  2709.         return $this->_escher;
  2710.     }
  2711.  
  2712.     /**
  2713.      * Set Escher object
  2714.      *
  2715.      * @param PHPExcel_Shared_Escher $pValue 
  2716.      */
  2717.     public function setEscher(PHPExcel_Shared_Escher $pValue null)
  2718.     {
  2719.         $this->_escher $pValue;
  2720.     }
  2721.  
  2722.     /**
  2723.      * Write MSODRAWING record
  2724.      */
  2725.     private function _writeMsoDrawing()
  2726.     {
  2727.         // write the Escher stream if necessary
  2728.         if (isset($this->_escher)) {
  2729.             $writer new PHPExcel_Writer_Excel5_Escher($this->_escher);
  2730.             $data $writer->close();
  2731.             $spOffsets $writer->getSpOffsets();
  2732.  
  2733.             // write the neccesary MSODRAWING, OBJ records
  2734.  
  2735.             // split the Escher stream
  2736.             $spOffsets[00;
  2737.             $nm count($spOffsets1// number of shapes excluding first shape
  2738.             for ($i 1$i <= $nm++$i{
  2739.                 // MSODRAWING record
  2740.                 $record 0x00EC;            // Record identifier
  2741.  
  2742.                 // chunk of Escher stream for one shape
  2743.  
  2744.                 $dataChunk substr($data$spOffsets[$i -1]$spOffsets[$i$spOffsets[$i 1]);
  2745.  
  2746.                 $length strlen($dataChunk);
  2747.                 $header pack("vv"$record$length);
  2748.  
  2749.                 $this->_append($header $dataChunk);
  2750.  
  2751.                 // OBJ record
  2752.                 $record 0x005D// record identifier
  2753.                 $objData '';
  2754.  
  2755.                 // ftCmo
  2756.                 $objData .=
  2757.                     pack('vvvvvVVV'
  2758.                         0x0015    // 0x0015 = ftCmo
  2759.                         0x0012    // length of ftCmo data
  2760.                         0x0008    // object type, 0x0008 = picture
  2761.                         $i        // object id number, Excel seems to use 1-based index, local for the sheet
  2762.                         0x6011    // option flags, 0x6011 is what OpenOffice.org uses
  2763.                         0            // reserved
  2764.                         0            // reserved
  2765.                         0            // reserved
  2766.                     );
  2767.                 // ftEnd
  2768.                 $objData .=
  2769.                     pack('vv'
  2770.                         0x0000    // 0x0000 = ftEnd
  2771.                         0x0000    // length of ftEnd data
  2772.                     );
  2773.  
  2774.                 $length strlen($objData);
  2775.                 $header pack('vv'$record$length);
  2776.                 $this->_append($header $objData);
  2777.             }
  2778.         }
  2779.     }
  2780.  
  2781.     /**
  2782.      * Store the DATAVALIDATIONS and DATAVALIDATION records.
  2783.      */
  2784.     private function _writeDataValidity()
  2785.     {
  2786.         // Datavalidation collection
  2787.         $dataValidationCollection $this->_phpSheet->getDataValidationCollection();
  2788.  
  2789.         // Write data validations?
  2790.         if (count($dataValidationCollection0{
  2791.  
  2792.             // DATAVALIDATIONS record
  2793.             $record 0x01B2;      // Record identifier
  2794.         $length      0x0012;      // Bytes to follow
  2795.  
  2796.             $grbit  0x0000;       // Prompt box at cell, no cached validity data at DV records
  2797.         $horPos      0x00000000;  // Horizontal position of prompt box, if fixed position
  2798.         $verPos      0x00000000;  // Vertical position of prompt box, if fixed position
  2799.             $objId  0xFFFFFFFF;  // Object identifier of drop down arrow object, or -1 if not visible
  2800.  
  2801.         $header      pack('vv'$record$length);
  2802.         $data        pack('vVVVV'$grbit$horPos$verPos$objId,
  2803.                                          count($dataValidationCollection));
  2804.         $this->_append($header.$data);
  2805.  
  2806.             // DATAVALIDATION records
  2807.             $record 0x01BE;              // Record identifier
  2808.  
  2809.             foreach ($dataValidationCollection as $cellCoordinate => $dataValidation{
  2810.                 // initialize record data
  2811.                 $data '';
  2812.  
  2813.                 // options
  2814.                 $options 0x00000000;
  2815.  
  2816.                 // data type
  2817.                 $type $dataValidation->getType();
  2818.                 switch ($type{
  2819.                     case PHPExcel_Cell_DataValidation::TYPE_NONE:        $type 0x00;    break;
  2820.                     case PHPExcel_Cell_DataValidation::TYPE_WHOLE:        $type 0x01;    break;
  2821.                     case PHPExcel_Cell_DataValidation::TYPE_DECIMAL:    $type 0x02;    break;
  2822.                     case PHPExcel_Cell_DataValidation::TYPE_LIST:        $type 0x03;    break;
  2823.                     case PHPExcel_Cell_DataValidation::TYPE_DATE:        $type 0x04;    break;
  2824.                     case PHPExcel_Cell_DataValidation::TYPE_TIME:        $type 0x05;    break;
  2825.                     case PHPExcel_Cell_DataValidation::TYPE_TEXTLENGTH:    $type 0x06;    break;
  2826.                     case PHPExcel_Cell_DataValidation::TYPE_CUSTOM:        $type 0x07;    break;
  2827.                 }
  2828.                 $options |= $type << 0;
  2829.  
  2830.                 // error style
  2831.                 $errorStyle $dataValidation->getType();
  2832.                 switch ($errorStyle{
  2833.                     case PHPExcel_Cell_DataValidation::STYLE_STOP:            $errorStyle 0x00;        break;
  2834.                     case PHPExcel_Cell_DataValidation::STYLE_WARNING:        $errorStyle 0x01;        break;
  2835.                     case PHPExcel_Cell_DataValidation::STYLE_INFORMATION:    $errorStyle 0x02;        break;
  2836.                 }
  2837.                 $options |= $errorStyle << 4;
  2838.  
  2839.                 // explicit formula?
  2840.                 if ($type == 0x03 && preg_match('/^\".*\"$/'$dataValidation->getFormula1())) {
  2841.                     $options |= 0x01                << 7;
  2842.                 }
  2843.  
  2844.                 // empty cells allowed
  2845.                 $options |= $dataValidation->getAllowBlank(<< 8;
  2846.  
  2847.                 // show drop down
  2848.                 $options |= (!$dataValidation->getShowDropDown()) << 9;
  2849.  
  2850.                 // show input message
  2851.                 $options |= $dataValidation->getShowInputMessage(<< 18;
  2852.  
  2853.                 // show error message
  2854.                 $options |= $dataValidation->getShowErrorMessage(<< 19;
  2855.  
  2856.                 // condition operator
  2857.                 $operator $dataValidation->getOperator();
  2858.                 switch ($operator{
  2859.                     case PHPExcel_Cell_DataValidation::OPERATOR_BETWEEN$operator 0x00            ;    break;
  2860.                     case PHPExcel_Cell_DataValidation::OPERATOR_NOTBETWEEN$operator 0x01        ;    break;
  2861.                     case PHPExcel_Cell_DataValidation::OPERATOR_EQUAL$operator 0x02                ;    break;
  2862.                     case PHPExcel_Cell_DataValidation::OPERATOR_NOTEQUAL$operator 0x03            ;    break;
  2863.                     case PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHAN$operator 0x04        ;    break;
  2864.                     case PHPExcel_Cell_DataValidation::OPERATOR_LESSTHAN$operator 0x05            ;    break;
  2865.                     case PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHANOREQUAL$operator 0x06;    break;
  2866.                     case PHPExcel_Cell_DataValidation::OPERATOR_LESSTHANOREQUAL$operator 0x07    ;    break;
  2867.                 }
  2868.                 $options |= $operator << 20;
  2869.  
  2870.                 $data        pack('V'$options);
  2871.  
  2872.                 // prompt title
  2873.                 $promptTitle $dataValidation->getPromptTitle(!== '' ?
  2874.                     $dataValidation->getPromptTitle(chr(0);
  2875.                 $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($promptTitle);
  2876.  
  2877.                 // error title
  2878.                 $errorTitle $dataValidation->getErrorTitle(!== '' ?
  2879.                     $dataValidation->getErrorTitle(chr(0);
  2880.                 $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($errorTitle);
  2881.  
  2882.                 // prompt text
  2883.                 $prompt $dataValidation->getPrompt(!== '' ?
  2884.                     $dataValidation->getPrompt(chr(0);
  2885.                 $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($prompt);
  2886.  
  2887.                 // error text
  2888.                 $error $dataValidation->getError(!== '' ?
  2889.                     $dataValidation->getError(chr(0);
  2890.                 $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($error);
  2891.  
  2892.                 // formula 1
  2893.                 try {
  2894.                     $formula1 $dataValidation->getFormula1();
  2895.                     if ($type == 0x03// list type
  2896.                         $formula1 str_replace(','chr(0)$formula1);
  2897.                     }
  2898.                     $this->_parser->parse($formula1);
  2899.                     $formula1 $this->_parser->toReversePolish();
  2900.                     $sz1 strlen($formula1);
  2901.  
  2902.                 catch(Exception $e{
  2903.                     $sz1 0;
  2904.                     $formula1 '';
  2905.                 }
  2906.                 $data .= pack('vv'$sz10x0000);
  2907.                 $data .= $formula1;
  2908.  
  2909.                 // formula 2
  2910.                 try {
  2911.                     $formula2 $dataValidation->getFormula2();
  2912.                     if ($formula2 === ''{
  2913.                         throw new Exception('No formula2');
  2914.                     }
  2915.                     $this->_parser->parse($formula2);
  2916.                     $formula2 $this->_parser->toReversePolish();
  2917.                     $sz2 strlen($formula2);
  2918.  
  2919.                 catch(Exception $e{
  2920.                     $sz2 0;
  2921.                     $formula2 '';
  2922.                 }
  2923.                 $data .= pack('vv'$sz20x0000);
  2924.                 $data .= $formula2;
  2925.  
  2926.                 // cell range address list
  2927.                 $data .= pack('v'0x0001);
  2928.                 $data .= $this->_writeBIFF8CellRangeAddressFixed($cellCoordinate);
  2929.  
  2930.                 $length strlen($data);
  2931.             $header pack("vv"$record$length);
  2932.  
  2933.                 $this->_append($header $data);
  2934.             }
  2935.         }
  2936.     }
  2937.  
  2938.     /**
  2939.      * Map Error code
  2940.      */
  2941.     private function _mapErrorCode($errorCode{
  2942.         switch ($errorCode{
  2943.             case '#NULL!':    return 0x00;
  2944.             case '#DIV/0!':    return 0x07;
  2945.             case '#VALUE!':    return 0x0F;
  2946.             case '#REF!':    return 0x17;
  2947.             case '#NAME?':    return 0x1D;
  2948.             case '#NUM!':    return 0x24;
  2949.             case '#N/A':    return 0x2A;
  2950.         }
  2951.  
  2952.         return 0;
  2953.     }
  2954.  
  2955. }

Documentation generated on Thu, 26 Aug 2010 17:47:02 +0200 by phpDocumentor 1.4.3