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

Documentation generated on Mon, 10 Aug 2009 08:11:45 +0200 by phpDocumentor 1.4.1