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

Documentation generated on Mon, 05 Jan 2009 20:38:56 +0100 by phpDocumentor 1.4.1