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

Documentation generated on Wed, 09 Feb 2011 09:05:45 +0700 by phpDocumentor 1.4.2