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

Documentation generated on Wed, 22 Apr 2009 09:06:42 +0200 by phpDocumentor 1.4.1