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

Source for file Excel5.php

Documentation is available at Excel5.php

  1. <?php
  2. /**
  3.  * PHPExcel
  4.  *
  5.  * Copyright (c) 2006 - 2010 PHPExcel
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  *
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  20.  *
  21.  * @category   PHPExcel
  22.  * @package    PHPExcel_Reader_Excel5
  23.  * @copyright  Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel)
  24.  * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
  25.  * @version    1.7.3c, 2010-06-01
  26.  */
  27.  
  28. // Original file header of ParseXL (used as the base for this class):
  29. // --------------------------------------------------------------------------------
  30. // Adapted from Excel_Spreadsheet_Reader developed by users bizon153,
  31. // trex005, and mmp11 (SourceForge.net)
  32. // http://sourceforge.net/projects/phpexcelreader/
  33. // Primary changes made by canyoncasa (dvc) for ParseXL 1.00 ...
  34. //     Modelled moreso after Perl Excel Parse/Write modules
  35. //     Added Parse_Excel_Spreadsheet object
  36. //         Reads a whole worksheet or tab as row,column array or as
  37. //         associated hash of indexed rows and named column fields
  38. //     Added variables for worksheet (tab) indexes and names
  39. //     Added an object call for loading individual woorksheets
  40. //     Changed default indexing defaults to 0 based arrays
  41. //     Fixed date/time and percent formats
  42. //     Includes patches found at SourceForge...
  43. //         unicode patch by nobody
  44. //         unpack("d") machine depedency patch by matchy
  45. //         boundsheet utf16 patch by bjaenichen
  46. //     Renamed functions for shorter names
  47. //     General code cleanup and rigor, including <80 column width
  48. //     Included a testcase Excel file and PHP example calls
  49. //     Code works for PHP 5.x
  50.  
  51. // Primary changes made by canyoncasa (dvc) for ParseXL 1.10 ...
  52. // http://sourceforge.net/tracker/index.php?func=detail&aid=1466964&group_id=99160&atid=623334
  53. //     Decoding of formula conditions, results, and tokens.
  54. //     Support for user-defined named cells added as an array "namedcells"
  55. //         Patch code for user-defined named cells supports single cells only.
  56. //         NOTE: this patch only works for BIFF8 as BIFF5-7 use a different
  57. //         external sheet reference structure
  58.  
  59.  
  60. /** PHPExcel root directory */
  61. if (!defined('PHPEXCEL_ROOT')) {
  62.     /**
  63.      * @ignore
  64.      */
  65.     define('PHPEXCEL_ROOT'dirname(__FILE__'/../../');
  66.     require(PHPEXCEL_ROOT 'PHPExcel/Autoloader.php');
  67.     // check mbstring.func_overload
  68.     if (ini_get('mbstring.func_overload'2{
  69.         throw new Exception('Multibyte function overloading in PHP must be disabled for string functions (2).');
  70.     }
  71. }
  72.  
  73. /**
  74.  * PHPExcel_Reader_Excel5
  75.  *
  76.  * This class uses {@link http://sourceforge.net/projects/phpexcelreader/parseXL}
  77.  *
  78.  * @category   PHPExcel
  79.  * @package    PHPExcel_Reader_Excel5
  80.  * @copyright  Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel)
  81.  */
  82. class PHPExcel_Reader_Excel5 implements PHPExcel_Reader_IReader
  83. {
  84.     // ParseXL definitions
  85.     const XLS_BIFF8                        0x0600;
  86.     const XLS_BIFF7                        0x0500;
  87.     const XLS_WorkbookGlobals            0x0005;
  88.     const XLS_Worksheet                    0x0010;
  89.  
  90.     // record identifiers
  91.     const XLS_Type_FORMULA                0x0006;
  92.     const XLS_Type_EOF                    0x000a;
  93.     const XLS_Type_PROTECT                0x0012;
  94.     const XLS_Type_OBJECTPROTECT        0x0063;
  95.     const XLS_Type_SCENPROTECT            0x00dd;
  96.     const XLS_Type_PASSWORD                0x0013;
  97.     const XLS_Type_HEADER                0x0014;
  98.     const XLS_Type_FOOTER                0x0015;
  99.     const XLS_Type_EXTERNSHEET            0x0017;
  100.     const XLS_Type_DEFINEDNAME            0x0018;
  101.     const XLS_Type_VERTICALPAGEBREAKS    0x001a;
  102.     const XLS_Type_HORIZONTALPAGEBREAKS    0x001b;
  103.     const XLS_Type_NOTE                    0x001c;
  104.     const XLS_Type_SELECTION            0x001d;
  105.     const XLS_Type_DATEMODE                0x0022;
  106.     const XLS_Type_EXTERNNAME            0x0023;
  107.     const XLS_Type_LEFTMARGIN            0x0026;
  108.     const XLS_Type_RIGHTMARGIN            0x0027;
  109.     const XLS_Type_TOPMARGIN            0x0028;
  110.     const XLS_Type_BOTTOMMARGIN            0x0029;
  111.     const XLS_Type_PRINTGRIDLINES        0x002b;
  112.     const XLS_Type_FILEPASS                0x002f;
  113.     const XLS_Type_FONT                    0x0031;
  114.     const XLS_Type_CONTINUE                0x003c;
  115.     const XLS_Type_PANE                    0x0041;
  116.     const XLS_Type_CODEPAGE                0x0042;
  117.     const XLS_Type_DEFCOLWIDTH             0x0055;
  118.     const XLS_Type_OBJ                    0x005d;
  119.     const XLS_Type_COLINFO                0x007d;
  120.     const XLS_Type_IMDATA                0x007f;
  121.     const XLS_Type_SHEETPR                0x0081;
  122.     const XLS_Type_HCENTER                0x0083;
  123.     const XLS_Type_VCENTER                0x0084;
  124.     const XLS_Type_SHEET                0x0085;
  125.     const XLS_Type_PALETTE                0x0092;
  126.     const XLS_Type_SCL                    0x00a0;
  127.     const XLS_Type_PAGESETUP            0x00a1;
  128.     const XLS_Type_MULRK                0x00bd;
  129.     const XLS_Type_MULBLANK                0x00be;
  130.     const XLS_Type_DBCELL                0x00d7;
  131.     const XLS_Type_XF                    0x00e0;
  132.     const XLS_Type_MERGEDCELLS            0x00e5;
  133.     const XLS_Type_MSODRAWINGGROUP        0x00eb;
  134.     const XLS_Type_MSODRAWING            0x00ec;
  135.     const XLS_Type_SST                    0x00fc;
  136.     const XLS_Type_LABELSST                0x00fd;
  137.     const XLS_Type_EXTSST                0x00ff;
  138.     const XLS_Type_EXTERNALBOOK            0x01ae;
  139.     const XLS_Type_DATAVALIDATIONS        0x01b2;
  140.     const XLS_Type_TXO                    0x01b6;
  141.     const XLS_Type_HYPERLINK            0x01b8;
  142.     const XLS_Type_DATAVALIDATION        0x01be;
  143.     const XLS_Type_DIMENSION            0x0200;
  144.     const XLS_Type_BLANK                0x0201;
  145.     const XLS_Type_NUMBER                0x0203;
  146.     const XLS_Type_LABEL                0x0204;
  147.     const XLS_Type_BOOLERR                0x0205;
  148.     const XLS_Type_STRING                0x0207;
  149.     const XLS_Type_ROW                    0x0208;
  150.     const XLS_Type_INDEX                0x020b;
  151.     const XLS_Type_ARRAY                0x0221;
  152.     const XLS_Type_DEFAULTROWHEIGHT     0x0225;
  153.     const XLS_Type_WINDOW2                0x023e;
  154.     const XLS_Type_RK                    0x027e;
  155.     const XLS_Type_STYLE                0x0293;
  156.     const XLS_Type_FORMAT                0x041e;
  157.     const XLS_Type_SHAREDFMLA            0x04bc;
  158.     const XLS_Type_BOF                    0x0809;
  159.     const XLS_Type_SHEETPROTECTION        0x0867;
  160.     const XLS_Type_RANGEPROTECTION        0x0868;
  161.     const XLS_Type_SHEETLAYOUT            0x0862;
  162.     const XLS_Type_XFEXT                0x087d;
  163.     const XLS_Type_UNKNOWN                0xffff;
  164.  
  165.     /**
  166.      * Read data only?
  167.      *
  168.      * @var boolean 
  169.      */
  170.     private $_readDataOnly false;
  171.  
  172.     /**
  173.      * Restict which sheets should be loaded?
  174.      *
  175.      * @var array 
  176.      */
  177.     private $_loadSheetsOnly null;
  178.  
  179.     /**
  180.      * PHPExcel_Reader_IReadFilter instance
  181.      *
  182.      * @var PHPExcel_Reader_IReadFilter 
  183.      */
  184.     private $_readFilter null;
  185.  
  186.     /**
  187.      * Summary Information stream data.
  188.      *
  189.      * @var string 
  190.      */
  191.     private $_summaryInformation;
  192.  
  193.     /**
  194.      * Workbook stream data. (Includes workbook globals substream as well as sheet substreams)
  195.      *
  196.      * @var string 
  197.      */
  198.     private $_data;
  199.  
  200.     /**
  201.      * Size in bytes of $this->_data
  202.      *
  203.      * @var int 
  204.      */
  205.     private $_dataSize;
  206.  
  207.     /**
  208.      * Current position in stream
  209.      *
  210.      * @var integer 
  211.      */
  212.     private $_pos;
  213.  
  214.     /**
  215.      * Workbook to be returned by the reader.
  216.      *
  217.      * @var PHPExcel 
  218.      */
  219.     private $_phpExcel;
  220.  
  221.     /**
  222.      * Worksheet that is currently being built by the reader.
  223.      *
  224.      * @var PHPExcel_Worksheet 
  225.      */
  226.     private $_phpSheet;
  227.  
  228.     /**
  229.      * BIFF version
  230.      *
  231.      * @var int 
  232.      */
  233.     private $_version;
  234.  
  235.     /**
  236.      * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
  237.      * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'
  238.      *
  239.      * @var string 
  240.      */
  241.     private $_codepage;
  242.  
  243.     /**
  244.      * Shared formats
  245.      *
  246.      * @var array 
  247.      */
  248.     private $_formats;
  249.  
  250.     /**
  251.      * Shared fonts
  252.      *
  253.      * @var array 
  254.      */
  255.     private $_objFonts;
  256.  
  257.     /**
  258.      * Color palette
  259.      *
  260.      * @var array 
  261.      */
  262.     private $_palette;
  263.  
  264.     /**
  265.      * Worksheets
  266.      *
  267.      * @var array 
  268.      */
  269.     private $_sheets;
  270.  
  271.     /**
  272.      * External books
  273.      *
  274.      * @var array 
  275.      */
  276.     private $_externalBooks;
  277.  
  278.     /**
  279.      * REF structures. Only applies to BIFF8.
  280.      *
  281.      * @var array 
  282.      */
  283.     private $_ref;
  284.  
  285.     /**
  286.      * External names
  287.      *
  288.      * @var array 
  289.      */
  290.     private $_externalNames;
  291.  
  292.     /**
  293.      * Defined names
  294.      *
  295.      * @var array 
  296.      */
  297.     private $_definedname;
  298.  
  299.     /**
  300.      * Shared strings. Only applies to BIFF8.
  301.      *
  302.      * @var array 
  303.      */
  304.     private $_sst;
  305.  
  306.     /**
  307.      * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
  308.      *
  309.      * @var boolean 
  310.      */
  311.     private $_frozen;
  312.  
  313.     /**
  314.      * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
  315.      *
  316.      * @var boolean 
  317.      */
  318.     private $_isFitToPages;
  319.  
  320.     /**
  321.      * Objects. One OBJ record contributes with one entry.
  322.      *
  323.      * @var array 
  324.      */
  325.     private $_objs;
  326.  
  327.     /**
  328.      * The combined MSODRAWINGGROUP data
  329.      *
  330.      * @var string 
  331.      */
  332.     private $_drawingGroupData;
  333.  
  334.     /**
  335.      * The combined MSODRAWING data (per sheet)
  336.      *
  337.      * @var string 
  338.      */
  339.     private $_drawingData;
  340.  
  341.     /**
  342.      * Keep track of XF index
  343.      *
  344.      * @var int 
  345.      */
  346.     private $_xfIndex;
  347.  
  348.     /**
  349.      * Mapping of XF index (that is a cell XF) to final index in cellXf collection
  350.      *
  351.      * @var array 
  352.      */
  353.     private $_mapCellXfIndex;
  354.  
  355.     /**
  356.      * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection
  357.      *
  358.      * @var array 
  359.      */
  360.     private $_mapCellStyleXfIndex;
  361.  
  362.     /**
  363.      * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
  364.      *
  365.      * @var array 
  366.      */
  367.     private $_sharedFormulas;
  368.  
  369.     /**
  370.      * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
  371.      * refers to a shared formula.
  372.      *
  373.      * @var array 
  374.      */
  375.     private $_sharedFormulaParts;
  376.  
  377.     /**
  378.      * Read data only?
  379.      *
  380.      * @return boolean 
  381.      */
  382.     public function getReadDataOnly()
  383.     {
  384.         return $this->_readDataOnly;
  385.     }
  386.  
  387.     /**
  388.      * Set read data only
  389.      *
  390.      * @param boolean $pValue 
  391.      * @return PHPExcel_Reader_Excel5 
  392.      */
  393.     public function setReadDataOnly($pValue false)
  394.     {
  395.         $this->_readDataOnly $pValue;
  396.         return $this;
  397.     }
  398.  
  399.     /**
  400.      * Get which sheets to load
  401.      *
  402.      * @return mixed 
  403.      */
  404.     public function getLoadSheetsOnly()
  405.     {
  406.         return $this->_loadSheetsOnly;
  407.     }
  408.  
  409.     /**
  410.      * Set which sheets to load
  411.      *
  412.      * @param mixed $value 
  413.      * @return PHPExcel_Reader_Excel5 
  414.      */
  415.     public function setLoadSheetsOnly($value null)
  416.     {
  417.         $this->_loadSheetsOnly is_array($value?
  418.             $value array($value);
  419.         return $this;
  420.     }
  421.  
  422.     /**
  423.      * Set all sheets to load
  424.      *
  425.      * @return PHPExcel_Reader_Excel5 
  426.      */
  427.     public function setLoadAllSheets()
  428.     {
  429.         $this->_loadSheetsOnly null;
  430.         return $this;
  431.     }
  432.  
  433.     /**
  434.      * Read filter
  435.      *
  436.      * @return PHPExcel_Reader_IReadFilter 
  437.      */
  438.     public function getReadFilter({
  439.         return $this->_readFilter;
  440.     }
  441.  
  442.     /**
  443.      * Set read filter
  444.      *
  445.      * @param PHPExcel_Reader_IReadFilter $pValue 
  446.      * @return PHPExcel_Reader_Excel5 
  447.      */
  448.     public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue{
  449.         $this->_readFilter $pValue;
  450.         return $this;
  451.     }
  452.  
  453.     /**
  454.      * Create a new PHPExcel_Reader_Excel5 instance
  455.      */
  456.     public function __construct({
  457.         $this->_readFilter new PHPExcel_Reader_DefaultReadFilter();
  458.     }
  459.  
  460.     /**
  461.      * Can the current PHPExcel_Reader_IReader read the file?
  462.      *
  463.      * @param     string         $pFileName 
  464.      * @return     boolean 
  465.      */
  466.     public function canRead($pFilename)
  467.     {
  468.         // Check if file exists
  469.         if (!file_exists($pFilename)) {
  470.             throw new Exception("Could not open " $pFilename " for reading! File does not exist.");
  471.         }
  472.  
  473.         try {
  474.             // Use ParseXL for the hard work.
  475.             $ole new PHPExcel_Shared_OLERead();
  476.  
  477.             // get excel data
  478.             $res $ole->read($pFilename);
  479.             return true;
  480.  
  481.         catch (Exception $e{
  482.             return false;
  483.         }
  484.     }
  485.  
  486.     /**
  487.      * Loads PHPExcel from file
  488.      *
  489.      * @param     string         $pFilename 
  490.      * @return     PHPExcel 
  491.      * @throws     Exception
  492.      */
  493.     public function load($pFilename)
  494.     {
  495.         // Read the OLE file
  496.         $this->_loadOLE($pFilename);
  497.  
  498.         // Initialisations
  499.         $this->_phpExcel new PHPExcel;
  500.         $this->_phpExcel->removeSheetByIndex(0)// remove 1st sheet
  501.         if (!$this->_readDataOnly{
  502.             $this->_phpExcel->removeCellStyleXfByIndex(0)// remove the default style
  503.             $this->_phpExcel->removeCellXfByIndex(0)// remove the default style
  504.         }
  505.  
  506.         // Read the summary information stream (containing meta data)
  507.         $this->_readSummaryInformation();
  508.  
  509.         // total byte size of Excel data (workbook global substream + sheet substreams)
  510.         $this->_dataSize strlen($this->_data);
  511.  
  512.         // initialize
  513.         $this->_pos                    0;
  514.         $this->_codepage            'CP1252';
  515.         $this->_formats                array();
  516.         $this->_objFonts            array();
  517.         $this->_palette                array();
  518.         $this->_sheets                array();
  519.         $this->_externalBooks        array();
  520.         $this->_ref                    array();
  521.         $this->_definedname            array();
  522.         $this->_sst                    array();
  523.         $this->_drawingGroupData    '';
  524.         $this->_xfIndex                '';
  525.         $this->_mapCellXfIndex        array();
  526.         $this->_mapCellStyleXfIndex    array();
  527.  
  528.         // Parse Workbook Global Substream
  529.         while ($this->_pos $this->_dataSize{
  530.             $code $this->_GetInt2d($this->_data$this->_pos);
  531.  
  532.             switch ($code{
  533.                 case self::XLS_Type_BOF:            $this->_readBof();                break;
  534.                 case self::XLS_Type_FILEPASS:        $this->_readFilepass();            break;
  535.                 case self::XLS_Type_CODEPAGE:        $this->_readCodepage();            break;
  536.                 case self::XLS_Type_DATEMODE:        $this->_readDateMode();            break;
  537.                 case self::XLS_Type_FONT:            $this->_readFont();                break;
  538.                 case self::XLS_Type_FORMAT:            $this->_readFormat();            break;
  539.                 case self::XLS_Type_XF:                $this->_readXf();                break;
  540.                 case self::XLS_Type_XFEXT:            $this->_readXfExt();            break;
  541.                 case self::XLS_Type_STYLE:            $this->_readStyle();            break;
  542.                 case self::XLS_Type_PALETTE:        $this->_readPalette();            break;
  543.                 case self::XLS_Type_SHEET:            $this->_readSheet();            break;
  544.                 case self::XLS_Type_EXTERNALBOOK:    $this->_readExternalBook();        break;
  545.                 case self::XLS_Type_EXTERNNAME:        $this->_readExternName();        break;
  546.                 case self::XLS_Type_EXTERNSHEET:    $this->_readExternSheet();        break;
  547.                 case self::XLS_Type_DEFINEDNAME:    $this->_readDefinedName();        break;
  548.                 case self::XLS_Type_MSODRAWINGGROUP:    $this->_readMsoDrawingGroup();    break;
  549.                 case self::XLS_Type_SST:            $this->_readSst();                break;
  550.                 case self::XLS_Type_EOF:            $this->_readDefault();            break 2;
  551.                 default:                            $this->_readDefault();            break;
  552.             }
  553.         }
  554.  
  555.         // Resolve indexed colors for font, fill, and border colors
  556.         // Cannot be resolved already in XF record, because PALETTE record comes afterwards
  557.         if (!$this->_readDataOnly{
  558.             foreach ($this->_objFonts as $objFont{
  559.                 if (isset($objFont->colorIndex)) {
  560.                     $color $this->_readColor($objFont->colorIndex);
  561.                     $objFont->getColor()->setRGB($color['rgb']);
  562.                 }
  563.             }
  564.  
  565.             foreach ($this->_phpExcel->getCellXfCollection(as $objStyle{
  566.                 // fill start and end color
  567.                 $fill $objStyle->getFill();
  568.  
  569.                 if (isset($fill->startcolorIndex)) {
  570.                     $startColor $this->_readColor($fill->startcolorIndex);
  571.                     $fill->getStartColor()->setRGB($startColor['rgb']);
  572.                 }
  573.  
  574.                 if (isset($fill->endcolorIndex)) {
  575.                     $endColor $this->_readColor($fill->endcolorIndex);
  576.                     $fill->getEndColor()->setRGB($endColor['rgb']);
  577.                 }
  578.  
  579.                 // border colors
  580.                 $top      $objStyle->getBorders()->getTop();
  581.                 $right    $objStyle->getBorders()->getRight();
  582.                 $bottom   $objStyle->getBorders()->getBottom();
  583.                 $left     $objStyle->getBorders()->getLeft();
  584.                 $diagonal $objStyle->getBorders()->getDiagonal();
  585.  
  586.                 if (isset($top->colorIndex)) {
  587.                     $borderTopColor $this->_readColor($top->colorIndex);
  588.                     $top->getColor()->setRGB($borderTopColor['rgb']);
  589.                 }
  590.  
  591.                 if (isset($right->colorIndex)) {
  592.                     $borderRightColor $this->_readColor($right->colorIndex);
  593.                     $right->getColor()->setRGB($borderRightColor['rgb']);
  594.                 }
  595.  
  596.                 if (isset($bottom->colorIndex)) {
  597.                     $borderBottomColor $this->_readColor($bottom->colorIndex);
  598.                     $bottom->getColor()->setRGB($borderBottomColor['rgb']);
  599.                 }
  600.  
  601.                 if (isset($left->colorIndex)) {
  602.                     $borderLeftColor $this->_readColor($left->colorIndex);
  603.                     $left->getColor()->setRGB($borderLeftColor['rgb']);
  604.                 }
  605.  
  606.                 if (isset($diagonal->colorIndex)) {
  607.                     $borderDiagonalColor $this->_readColor($diagonal->colorIndex);
  608.                     $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
  609.                 }
  610.             }
  611.         }
  612.  
  613.         // treat MSODRAWINGGROUP records, workbook-level Escher
  614.         if (!$this->_readDataOnly && $this->_drawingGroupData{
  615.             $escherWorkbook new PHPExcel_Shared_Escher();
  616.             $reader new PHPExcel_Reader_Excel5_Escher($escherWorkbook);
  617.             $escherWorkbook $reader->load($this->_drawingGroupData);
  618.  
  619.             // debug Escher stream
  620.             //$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
  621.             //$debug->load($this->_drawingGroupData);
  622.         }
  623.  
  624.         // Parse the individual sheets
  625.         foreach ($this->_sheets as $sheet{
  626.  
  627.             if ($sheet['sheetType'!= 0x00{
  628.                 // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
  629.                 continue;
  630.             }
  631.  
  632.             // check if sheet should be skipped
  633.             if (isset($this->_loadSheetsOnly&& !in_array($sheet['name']$this->_loadSheetsOnly)) {
  634.                 continue;
  635.             }
  636.  
  637.             // add sheet to PHPExcel object
  638.             $this->_phpSheet $this->_phpExcel->createSheet();
  639.             $this->_phpSheet->setTitle($sheet['name']);
  640.             $this->_phpSheet->setSheetState($sheet['sheetState']);
  641.  
  642.             $this->_pos $sheet['offset'];
  643.  
  644.             // Initialize isFitToPages. May change after reading SHEETPR record.
  645.             $this->_isFitToPages false;
  646.  
  647.             // Initialize drawingData
  648.             $this->_drawingData '';
  649.  
  650.             // Initialize objs
  651.             $this->_objs array();
  652.  
  653.             // Initialize shared formula parts
  654.             $this->_sharedFormulaParts array();
  655.  
  656.             // Initialize shared formulas
  657.             $this->_sharedFormulas array();
  658.  
  659.             while ($this->_pos <= $this->_dataSize 4{
  660.                 $code $this->_GetInt2d($this->_data$this->_pos);
  661.  
  662.                 switch ($code{
  663.                     case self::XLS_Type_BOF:                    $this->_readBof();                        break;
  664.                     case self::XLS_Type_PRINTGRIDLINES:            $this->_readPrintGridlines();            break;
  665.                     case self::XLS_Type_DEFAULTROWHEIGHT:        $this->_readDefaultRowHeight();            break;
  666.                     case self::XLS_Type_SHEETPR:                $this->_readSheetPr();                    break;
  667.                     case self::XLS_Type_HORIZONTALPAGEBREAKS:    $this->_readHorizontalPageBreaks();        break;
  668.                     case self::XLS_Type_VERTICALPAGEBREAKS:        $this->_readVerticalPageBreaks();        break;
  669.                     case self::XLS_Type_HEADER:                    $this->_readHeader();                    break;
  670.                     case self::XLS_Type_FOOTER:                    $this->_readFooter();                    break;
  671.                     case self::XLS_Type_HCENTER:                $this->_readHcenter();                    break;
  672.                     case self::XLS_Type_VCENTER:                $this->_readVcenter();                    break;
  673.                     case self::XLS_Type_LEFTMARGIN:                $this->_readLeftMargin();                break;
  674.                     case self::XLS_Type_RIGHTMARGIN:            $this->_readRightMargin();                break;
  675.                     case self::XLS_Type_TOPMARGIN:                $this->_readTopMargin();                break;
  676.                     case self::XLS_Type_BOTTOMMARGIN:            $this->_readBottomMargin();                break;
  677.                     case self::XLS_Type_PAGESETUP:                $this->_readPageSetup();                break;
  678.                     case self::XLS_Type_PROTECT:                $this->_readProtect();                    break;
  679.                     case self::XLS_Type_SCENPROTECT:            $this->_readScenProtect();                break;
  680.                     case self::XLS_Type_OBJECTPROTECT:            $this->_readObjectProtect();            break;
  681.                     case self::XLS_Type_PASSWORD:                $this->_readPassword();                    break;
  682.                     case self::XLS_Type_DEFCOLWIDTH:            $this->_readDefColWidth();                break;
  683.                     case self::XLS_Type_COLINFO:                $this->_readColInfo();                    break;
  684.                     case self::XLS_Type_DIMENSION:                $this->_readDefault();                    break;
  685.                     case self::XLS_Type_ROW:                    $this->_readRow();                        break;
  686.                     case self::XLS_Type_DBCELL:                    $this->_readDefault();                    break;
  687.                     case self::XLS_Type_RK:                        $this->_readRk();                        break;
  688.                     case self::XLS_Type_LABELSST:                $this->_readLabelSst();                    break;
  689.                     case self::XLS_Type_MULRK:                    $this->_readMulRk();                    break;
  690.                     case self::XLS_Type_NUMBER:                    $this->_readNumber();                    break;
  691.                     case self::XLS_Type_FORMULA:                $this->_readFormula();                    break;
  692.                     case self::XLS_Type_SHAREDFMLA:                $this->_readSharedFmla();                break;
  693.                     case self::XLS_Type_BOOLERR:                $this->_readBoolErr();                    break;
  694.                     case self::XLS_Type_MULBLANK:                $this->_readMulBlank();                    break;
  695.                     case self::XLS_Type_LABEL:                    $this->_readLabel();                    break;
  696.                     case self::XLS_Type_BLANK:                    $this->_readBlank();                    break;
  697.                     case self::XLS_Type_MSODRAWING:                $this->_readMsoDrawing();                break;
  698.                     case self::XLS_Type_OBJ:                    $this->_readObj();                        break;
  699.                     case self::XLS_Type_WINDOW2:                $this->_readWindow2();                    break;
  700.                     case self::XLS_Type_SCL:                    $this->_readScl();                        break;
  701.                     case self::XLS_Type_PANE:                    $this->_readPane();                        break;
  702.                     case self::XLS_Type_SELECTION:                $this->_readSelection();                break;
  703.                     case self::XLS_Type_MERGEDCELLS:            $this->_readMergedCells();                break;
  704.                     case self::XLS_Type_HYPERLINK:                $this->_readHyperLink();                break;
  705.                     case self::XLS_Type_DATAVALIDATIONS:        $this->_readDataValidations();            break;
  706.                     case self::XLS_Type_DATAVALIDATION:            $this->_readDataValidation();            break;
  707.                     case self::XLS_Type_SHEETLAYOUT:            $this->_readSheetLayout();                break;
  708.                     case self::XLS_Type_SHEETPROTECTION:        $this->_readSheetProtection();            break;
  709.                     case self::XLS_Type_RANGEPROTECTION:        $this->_readRangeProtection();            break;
  710.                     //case self::XLS_Type_IMDATA:                $this->_readImData();                    break;
  711.                     case self::XLS_Type_CONTINUE:                $this->_readContinue();                    break;
  712.                     case self::XLS_Type_EOF:                    $this->_readDefault();                    break 2;
  713.                     default:                                    $this->_readDefault();                    break;
  714.                 }
  715.  
  716.             }
  717.  
  718.             // treat MSODRAWING records, sheet-level Escher
  719.             if (!$this->_readDataOnly && $this->_drawingData{
  720.                 $escherWorksheet new PHPExcel_Shared_Escher();
  721.                 $reader new PHPExcel_Reader_Excel5_Escher($escherWorksheet);
  722.                 $escherWorksheet $reader->load($this->_drawingData);
  723.  
  724.                 // debug Escher stream
  725.                 //$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
  726.                 //$debug->load($this->_drawingData);
  727.  
  728.                 // get all spContainers in one long array, so they can be mapped to OBJ records
  729.                 $allSpContainers $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
  730.             }
  731.  
  732.             // treat OBJ records
  733.             foreach ($this->_objs as $n => $obj{
  734.  
  735.                 // the first shape container never has a corresponding OBJ record, hence $n + 1
  736.                 $spContainer $allSpContainers[$n 1];
  737.  
  738.                 // we skip all spContainers that are a part of a group shape since we cannot yet handle those
  739.                 if ($spContainer->getNestingLevel(1{
  740.                     continue;
  741.                 }
  742.  
  743.                 // calculate the width and height of the shape
  744.                 list($startColumn$startRowPHPExcel_Cell::coordinateFromString($spContainer->getStartCoordinates());
  745.                 list($endColumn$endRowPHPExcel_Cell::coordinateFromString($spContainer->getEndCoordinates());
  746.  
  747.                 $startOffsetX $spContainer->getStartOffsetX();
  748.                 $startOffsetY $spContainer->getStartOffsetY();
  749.                 $endOffsetX $spContainer->getEndOffsetX();
  750.                 $endOffsetY $spContainer->getEndOffsetY();
  751.  
  752.                 $width PHPExcel_Shared_Excel5::getDistanceX($this->_phpSheet$startColumn$startOffsetX$endColumn$endOffsetX);
  753.                 $height PHPExcel_Shared_Excel5::getDistanceY($this->_phpSheet$startRow$startOffsetY$endRow$endOffsetY);
  754.  
  755.                 // calculate offsetX and offsetY of the shape
  756.                 $offsetX $startOffsetX PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet$startColumn1024;
  757.                 $offsetY $startOffsetY PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet$startRow256;
  758.  
  759.                 switch ($obj['type']{
  760.  
  761.                 case 0x08:
  762.                     // picture
  763.  
  764.                     // get index to BSE entry (1-based)
  765.                     $BSEindex $spContainer->getOPT(0x0104);
  766.                     $BSECollection $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
  767.                     $BSE $BSECollection[$BSEindex 1];
  768.                     $blipType $BSE->getBlipType();
  769.  
  770.                     // need check because some blip types are not supported by Escher reader such as EMF
  771.                     if ($blip $BSE->getBlip()) {
  772.                         $ih imagecreatefromstring($blip->getData());
  773.                         $drawing new PHPExcel_Worksheet_MemoryDrawing();
  774.                         $drawing->setImageResource($ih);
  775.  
  776.                         // width, height, offsetX, offsetY
  777.                         $drawing->setResizeProportional(false);
  778.                         $drawing->setWidth($width);
  779.                         $drawing->setHeight($height);
  780.                         $drawing->setOffsetX($offsetX);
  781.                         $drawing->setOffsetY($offsetY);
  782.  
  783.                         switch ($blipType{
  784.  
  785.                         case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG:
  786.                             $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG);
  787.                             $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_JPEG);
  788.                             break;
  789.  
  790.                         case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG:
  791.                             $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG);
  792.                             $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_PNG);
  793.                             break;
  794.                         }
  795.  
  796.                         $drawing->setWorksheet($this->_phpSheet);
  797.                         $drawing->setCoordinates($spContainer->getStartCoordinates());
  798.                     }
  799.  
  800.                     break;
  801.  
  802.                 default:
  803.                     // other object type
  804.                     break;
  805.  
  806.                 }
  807.             }
  808.  
  809.             // treat SHAREDFMLA records
  810.             if ($this->_version == self::XLS_BIFF8{
  811.                 foreach ($this->_sharedFormulaParts as $cell => $baseCell{
  812.                     $formula $this->_getFormulaFromStructure($this->_sharedFormulas[$baseCell]$cell);
  813.                     $this->_phpSheet->getCell($cell)->setValueExplicit('=' $formulaPHPExcel_Cell_DataType::TYPE_FORMULA);
  814.                 }
  815.             }
  816.         }
  817.  
  818.         // add the named ranges (defined names)
  819.         foreach ($this->_definedname as $definedName{
  820.             if ($definedName['isBuiltInName']{
  821.                 switch ($definedName['name']{
  822.  
  823.                 case pack('C'0x06):
  824.                     // print area
  825.                     //    in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
  826.  
  827.                     $ranges explode(','$definedName['formula'])// FIXME: what if sheetname contains comma?
  828.  
  829.                     $extractedRanges array();
  830.                     foreach ($ranges as $range{
  831.                         // $range should look like one of these
  832.                         //        Foo!$C$7:$J$66
  833.                         //        Bar!$A$1:$IV$2
  834.  
  835.                         $explodes explode('!'$range);
  836.                         $sheetName $explodes[0];
  837.  
  838.                         if (count($explodes== 2{
  839.                             $extractedRanges[str_replace('$'''$explodes[1])// C7:J66
  840.                         }
  841.                     }
  842.                     if ($docSheet $this->_phpExcel->getSheetByName($sheetName)) {
  843.                         $docSheet->getPageSetup()->setPrintArea(implode(','$extractedRanges))// C7:J66,A1:IV2
  844.                     }
  845.                     break;
  846.  
  847.                 case pack('C'0x07):
  848.                     // print titles (repeating rows)
  849.                     // Assuming BIFF8, there are 3 cases
  850.                     // 1. repeating rows
  851.                     //        formula looks like this: Sheet!$A$1:$IV$2
  852.                     //        rows 1-2 repeat
  853.                     // 2. repeating columns
  854.                     //        formula looks like this: Sheet!$A$1:$B$65536
  855.                     //        columns A-B repeat
  856.                     // 3. both repeating rows and repeating columns
  857.                     //        formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
  858.  
  859.                     $ranges explode(','$definedName['formula'])// FIXME: what if sheetname contains comma?
  860.  
  861.                     foreach ($ranges as $range{
  862.                         // $range should look like this one of these
  863.                         //        Sheet!$A$1:$B$65536
  864.                         //        Sheet!$A$1:$IV$2
  865.  
  866.                         $explodes explode('!'$range);
  867.  
  868.                         if (count($explodes== 2{
  869.                             if ($docSheet $this->_phpExcel->getSheetByName($explodes[0])) {
  870.  
  871.                                 $extractedRange $explodes[1];
  872.                                 $extractedRange str_replace('$'''$extractedRange);
  873.  
  874.                                 $coordinateStrings explode(':'$extractedRange);
  875.                                 if (count($coordinateStrings== 2{
  876.                                     list($firstColumn$firstRowPHPExcel_Cell::coordinateFromString($coordinateStrings[0]);
  877.                                     list($lastColumn$lastRowPHPExcel_Cell::coordinateFromString($coordinateStrings[1]);
  878.  
  879.                                     if ($firstColumn == 'A' and $lastColumn == 'IV'{
  880.                                         // then we have repeating rows
  881.                                         $docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow$lastRow));
  882.                                     elseif ($firstRow == and $lastRow == 65536{
  883.                                         // then we have repeating columns
  884.                                         $docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn$lastColumn));
  885.                                     }
  886.                                 }
  887.                             }
  888.                         }
  889.                     }
  890.                     break;
  891.  
  892.                 }
  893.             else {
  894.                 // Extract range
  895.                 $explodes explode('!'$definedName['formula']);
  896.  
  897.                 if (count($explodes== 2{
  898.                     if ($docSheet $this->_phpExcel->getSheetByName($explodes[0])) {
  899.                         $extractedRange $explodes[1];
  900.                         $extractedRange str_replace('$'''$extractedRange);
  901.  
  902.                         $localOnly ($definedName['scope'== 0false true;
  903.                         $scope ($definedName['scope'== 0?
  904.                             null $this->_phpExcel->getSheetByName($this->_sheets[$definedName['scope'1]['name']);
  905.  
  906.                         $this->_phpExcel->addNamedRangenew PHPExcel_NamedRange((string)$definedName['name']$docSheet$extractedRange$localOnly$scope) );
  907.                     }
  908.                 }
  909.             }
  910.         }
  911.  
  912.         return $this->_phpExcel;
  913.     }
  914.  
  915.     /**
  916.      * Use OLE reader to extract the relevant data streams from the OLE file
  917.      *
  918.      * @param string $pFilename 
  919.      */
  920.     private function _loadOLE($pFilename)
  921.     {
  922.         // OLE reader
  923.         $ole new PHPExcel_Shared_OLERead();
  924.  
  925.         // get excel data
  926.         $res $ole->read($pFilename);
  927.         $this->_data $ole->getWorkBook();
  928.  
  929.         // Get summary information data
  930.         $this->_summaryInformation $ole->getSummaryInformation();
  931.     }
  932.  
  933.     /**
  934.      * Read summary information
  935.      */
  936.     private function _readSummaryInformation()
  937.     {
  938.         if (!isset($this->_summaryInformation)) {
  939.             return;
  940.         }
  941.  
  942.         // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
  943.         // offset: 2; size: 2;
  944.         // offset: 4; size: 2; OS version
  945.         // offset: 6; size: 2; OS indicator
  946.         // offset: 8; size: 16
  947.         // offset: 24; size: 4; section count
  948.  
  949.         // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
  950.         // offset: 44; size: 4
  951.  
  952.         // section header
  953.         // offset: 48; size: 4; section length
  954.         $secLength $this->_GetInt4d($this->_summaryInformation48);
  955.  
  956.         // offset: 52; size: 4; property count
  957.         $countProperties $this->_GetInt4d($this->_summaryInformation52);
  958.  
  959.         // initialize code page (used to resolve string values)
  960.         $codePage 'CP1252';
  961.  
  962.         // offset: 56; size: var
  963.         // loop through property decarations and properties
  964.         for ($i 0$i $countProperties++$i{
  965.  
  966.             // offset: 56 + 8 * $i; size: 4; property ID
  967.             $id $this->_GetInt4d($this->_summaryInformation56 $i);
  968.  
  969.             // offset: 60 + 8 * $i; size: 4; offset from beginning of section (48)
  970.             $offset $this->_GetInt4d($this->_summaryInformation60 $i);
  971.  
  972.             $type $this->_GetInt4d($this->_summaryInformation48 $offset);
  973.  
  974.             // initialize property value
  975.             $value null;
  976.  
  977.             // extract property value based on property type
  978.             switch ($type{
  979.                 case 0x02// 2 byte signed integer
  980.                     $value $this->_GetInt2d($this->_summaryInformation52 $offset);
  981.                     break;
  982.  
  983.                 case 0x03// 4 byte signed integer
  984.                     $value $this->_GetInt4d($this->_summaryInformation52 $offset);
  985.                     break;
  986.  
  987.                 case 0x13// 4 byte unsigned integer
  988.                     // not needed yet, fix later if necessary
  989.                     break;
  990.  
  991.                 case 0x1E// null-terminated string prepended by dword string length
  992.                     $byteLength $this->_GetInt4d($this->_summaryInformation52 $offset);
  993.                     $value substr($this->_summaryInformation56 $offset$byteLength);
  994.                     $value PHPExcel_Shared_String::ConvertEncoding($value'UTF-8'$codePage);
  995.                     $value rtrim($value);
  996.                     break;
  997.  
  998.                 case 0x40// Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
  999.                     // PHP-time
  1000.                     $value PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_summaryInformation52 $offset8));
  1001.                     break;
  1002.  
  1003.                 case 0x47// Clipboard format
  1004.                     // not needed yet, fix later if necessary
  1005.                     break;
  1006.             }
  1007.  
  1008.             // Use value of property id as appropriate
  1009.             switch ($id{
  1010.                 case 0x01// Code Page
  1011.                     $codePage PHPExcel_Shared_CodePage::NumberToName($value);
  1012.                     break;
  1013.  
  1014.                 case 0x02// Title
  1015.                     $this->_phpExcel->getProperties()->setTitle($value);
  1016.                     break;
  1017.  
  1018.                 case 0x03// Subject
  1019.                     $this->_phpExcel->getProperties()->setSubject($value);
  1020.                     break;
  1021.  
  1022.                 case 0x04// Author (Creator)
  1023.                     $this->_phpExcel->getProperties()->setCreator($value);
  1024.                     break;
  1025.  
  1026.                 case 0x05// Keywords
  1027.                     $this->_phpExcel->getProperties()->setKeywords($value);
  1028.                     break;
  1029.  
  1030.                 case 0x06// Comments (Description)
  1031.                     $this->_phpExcel->getProperties()->setDescription($value);
  1032.                     break;
  1033.  
  1034.                 case 0x08// Last Saved By (LastModifiedBy)
  1035.                     $this->_phpExcel->getProperties()->setLastModifiedBy($value);
  1036.                     break;
  1037.  
  1038.                 case 0x09// Revision
  1039.                     // not supported by PHPExcel
  1040.                     break;
  1041.  
  1042.                 case 0x0C// Created
  1043.                     $this->_phpExcel->getProperties()->setCreated($value);
  1044.                     break;
  1045.  
  1046.                 case 0x0D// Modified
  1047.                     $this->_phpExcel->getProperties()->setModified($value);
  1048.                     break;
  1049.  
  1050.                 case 0x12// Name of creating application
  1051.                     // not supported by PHPExcel
  1052.                     break;
  1053.             }
  1054.         }
  1055.     }
  1056.  
  1057.     /**
  1058.      * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
  1059.      */
  1060.     private function _readDefault()
  1061.     {
  1062.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1063.         $recordData substr($this->_data$this->_pos 4$length);
  1064.  
  1065.         // move stream pointer to next record
  1066.         $this->_pos += $length;
  1067.     }
  1068.  
  1069.     /**
  1070.      * Read BOF
  1071.      */
  1072.     private function _readBof()
  1073.     {
  1074.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1075.         $recordData substr($this->_data$this->_pos 4$length);
  1076.  
  1077.         // move stream pointer to next record
  1078.         $this->_pos += $length;
  1079.  
  1080.         // offset: 2; size: 2; type of the following data
  1081.         $substreamType $this->_GetInt2d($recordData2);
  1082.  
  1083.         switch ($substreamType{
  1084.             case self::XLS_WorkbookGlobals:
  1085.                 $version $this->_GetInt2d($recordData0);
  1086.                 if (($version != self::XLS_BIFF8&& ($version != self::XLS_BIFF7)) {
  1087.                     throw new Exception('Cannot read this Excel file. Version is too old.');
  1088.                 }
  1089.                 $this->_version $version;
  1090.                 break;
  1091.  
  1092.             case self::XLS_Worksheet:
  1093.                 // do not use this version information for anything
  1094.                 // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
  1095.                 break;
  1096.  
  1097.             default:
  1098.                 // substream, e.g. chart
  1099.                 // just skip the entire substream
  1100.                 do {
  1101.                     $code $this->_GetInt2d($this->_data$this->_pos);
  1102.                     $this->_readDefault();
  1103.                 while ($code != self::XLS_Type_EOF && $this->_pos $this->_dataSize);
  1104.                 break;
  1105.         }
  1106.     }
  1107.  
  1108.     /**
  1109.      * FILEPASS
  1110.      *
  1111.      * This record is part of the File Protection Block. It
  1112.      * contains information about the read/write password of the
  1113.      * file. All record contents following this record will be
  1114.      * encrypted.
  1115.      *
  1116.      * --    "OpenOffice.org's Documentation of the Microsoft
  1117.      *         Excel File Format"
  1118.      */
  1119.     private function _readFilepass()
  1120.     {
  1121.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1122.         $recordData substr($this->_data$this->_pos 4$length);
  1123.  
  1124.         // move stream pointer to next record
  1125.         $this->_pos += $length;
  1126.  
  1127.         throw new Exception('Cannot read encrypted file');
  1128.     }
  1129.  
  1130.     /**
  1131.      * CODEPAGE
  1132.      *
  1133.      * This record stores the text encoding used to write byte
  1134.      * strings, stored as MS Windows code page identifier.
  1135.      *
  1136.      * --    "OpenOffice.org's Documentation of the Microsoft
  1137.      *         Excel File Format"
  1138.      */
  1139.     private function _readCodepage()
  1140.     {
  1141.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1142.         $recordData substr($this->_data$this->_pos 4$length);
  1143.  
  1144.         // move stream pointer to next record
  1145.         $this->_pos += $length;
  1146.  
  1147.         // offset: 0; size: 2; code page identifier
  1148.         $codepage $this->_GetInt2d($recordData0);
  1149.  
  1150.         $this->_codepage PHPExcel_Shared_CodePage::NumberToName($codepage);
  1151.     }
  1152.  
  1153.     /**
  1154.      * DATEMODE
  1155.      *
  1156.      * This record specifies the base date for displaying date
  1157.      * values. All dates are stored as count of days past this
  1158.      * base date. In BIFF2-BIFF4 this record is part of the
  1159.      * Calculation Settings Block. In BIFF5-BIFF8 it is
  1160.      * stored in the Workbook Globals Substream.
  1161.      *
  1162.      * --    "OpenOffice.org's Documentation of the Microsoft
  1163.      *         Excel File Format"
  1164.      */
  1165.     private function _readDateMode()
  1166.     {
  1167.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1168.         $recordData substr($this->_data$this->_pos 4$length);
  1169.  
  1170.         // move stream pointer to next record
  1171.         $this->_pos += $length;
  1172.  
  1173.         // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
  1174.         PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900);
  1175.         if (ord($recordData{0}== 1{
  1176.             PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_MAC_1904);
  1177.         }
  1178.     }
  1179.  
  1180.     /**
  1181.      * Read a FONT record
  1182.      */
  1183.     private function _readFont()
  1184.     {
  1185.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1186.         $recordData substr($this->_data$this->_pos 4$length);
  1187.  
  1188.         // move stream pointer to next record
  1189.         $this->_pos += $length;
  1190.  
  1191.         if (!$this->_readDataOnly{
  1192.             $objFont new PHPExcel_Style_Font();
  1193.  
  1194.             // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
  1195.             $size $this->_GetInt2d($recordData0);
  1196.             $objFont->setSize($size 20);
  1197.  
  1198.             // offset: 2; size: 2; option flags
  1199.                 // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
  1200.                 // bit: 1; mask 0x0002; italic
  1201.                 $isItalic (0x0002 $this->_GetInt2d($recordData2)) >> 1;
  1202.                 if ($isItalic$objFont->setItalic(true);
  1203.  
  1204.                 // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
  1205.                 // bit: 3; mask 0x0008; strike
  1206.                 $isStrike (0x0008 $this->_GetInt2d($recordData2)) >> 3;
  1207.                 if ($isStrike$objFont->setStrikethrough(true);
  1208.  
  1209.             // offset: 4; size: 2; colour index
  1210.             $colorIndex $this->_GetInt2d($recordData4);
  1211.             $objFont->colorIndex $colorIndex;
  1212.  
  1213.             // offset: 6; size: 2; font weight
  1214.             $weight $this->_GetInt2d($recordData6);
  1215.             switch ($weight{
  1216.                 case 0x02BC:
  1217.                     $objFont->setBold(true);
  1218.                     break;
  1219.             }
  1220.  
  1221.             // offset: 8; size: 2; escapement type
  1222.             $escapement $this->_GetInt2d($recordData8);
  1223.             switch ($escapement{
  1224.                 case 0x0001:
  1225.                     $objFont->setSuperScript(true);
  1226.                     break;
  1227.                 case 0x0002:
  1228.                     $objFont->setSubScript(true);
  1229.                     break;
  1230.             }
  1231.  
  1232.             // offset: 10; size: 1; underline type
  1233.             $underlineType ord($recordData{10});
  1234.             switch ($underlineType{
  1235.                 case 0x00:
  1236.                     break// no underline
  1237.                 case 0x01:
  1238.                     $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE);
  1239.                     break;
  1240.                 case 0x02:
  1241.                     $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLE);
  1242.                     break;
  1243.                 case 0x21:
  1244.                     $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING);
  1245.                     break;
  1246.                 case 0x22:
  1247.                     $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING);
  1248.                     break;
  1249.             }
  1250.  
  1251.             // offset: 11; size: 1; font family
  1252.             // offset: 12; size: 1; character set
  1253.             // offset: 13; size: 1; not used
  1254.             // offset: 14; size: var; font name
  1255.             if ($this->_version == self::XLS_BIFF8{
  1256.                 $string $this->_readUnicodeStringShort(substr($recordData14));
  1257.             else {
  1258.                 $string $this->_readByteStringShort(substr($recordData14));
  1259.             }
  1260.             $objFont->setName($string['value']);
  1261.  
  1262.             $this->_objFonts[$objFont;
  1263.         }
  1264.     }
  1265.  
  1266.     /**
  1267.      * FORMAT
  1268.      *
  1269.      * This record contains information about a number format.
  1270.      * All FORMAT records occur together in a sequential list.
  1271.      *
  1272.      * In BIFF2-BIFF4 other records referencing a FORMAT record
  1273.      * contain a zero-based index into this list. From BIFF5 on
  1274.      * the FORMAT record contains the index itself that will be
  1275.      * used by other records.
  1276.      *
  1277.      * --    "OpenOffice.org's Documentation of the Microsoft
  1278.      *         Excel File Format"
  1279.      */
  1280.     private function _readFormat()
  1281.     {
  1282.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1283.         $recordData substr($this->_data$this->_pos 4$length);
  1284.  
  1285.         // move stream pointer to next record
  1286.         $this->_pos += $length;
  1287.  
  1288.         if (!$this->_readDataOnly{
  1289.             $indexCode $this->_GetInt2d($recordData0);
  1290.  
  1291.             if ($this->_version == self::XLS_BIFF8{
  1292.                 $string $this->_readUnicodeStringLong(substr($recordData2));
  1293.             else {
  1294.                 // BIFF7
  1295.                 $string $this->_readByteStringShort(substr($recordData2));
  1296.             }
  1297.  
  1298.             $formatString $string['value'];
  1299.             $this->_formats[$indexCode$formatString;
  1300.         }
  1301.     }
  1302.  
  1303.     /**
  1304.      * XF - Extended Format
  1305.      *
  1306.      * This record contains formatting information for cells, rows, columns or styles.
  1307.      * According to http://support.microsoft.com/kb/147732 there are always at least 15 cell style XF
  1308.      * and 1 cell XF.
  1309.      * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF
  1310.      * and XF record 15 is a cell XF
  1311.      * We only read the first cell style XF and skip the remaining cell style XF records
  1312.      * We read all cell XF records.
  1313.      *
  1314.      * --    "OpenOffice.org's Documentation of the Microsoft
  1315.      *         Excel File Format"
  1316.      */
  1317.     private function _readXf()
  1318.     {
  1319.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1320.         $recordData substr($this->_data$this->_pos 4$length);
  1321.  
  1322.         // move stream pointer to next record
  1323.         $this->_pos += $length;
  1324.  
  1325.         $objStyle new PHPExcel_Style();
  1326.  
  1327.         if (!$this->_readDataOnly{
  1328.             // offset:  0; size: 2; Index to FONT record
  1329.             if ($this->_GetInt2d($recordData04{
  1330.                 $fontIndex $this->_GetInt2d($recordData0);
  1331.             else {
  1332.                 // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
  1333.                 // check the OpenOffice documentation of the FONT record
  1334.                 $fontIndex $this->_GetInt2d($recordData01;
  1335.             }
  1336.             $objStyle->setFont($this->_objFonts[$fontIndex]);
  1337.  
  1338.             // offset:  2; size: 2; Index to FORMAT record
  1339.             $numberFormatIndex $this->_GetInt2d($recordData2);
  1340.             if (isset($this->_formats[$numberFormatIndex])) {
  1341.                 // then we have user-defined format code
  1342.                 $numberformat array('code' => $this->_formats[$numberFormatIndex]);
  1343.             elseif (($code PHPExcel_Style_NumberFormat::builtInFormatCode($numberFormatIndex)) !== ''{
  1344.                 // then we have built-in format code
  1345.                 $numberformat array('code' => $code);
  1346.             else {
  1347.                 // we set the general format code
  1348.                 $numberformat array('code' => 'General');
  1349.             }
  1350.             $objStyle->getNumberFormat()->setFormatCode($numberformat['code']);
  1351.  
  1352.             // offset:  4; size: 2; XF type, cell protection, and parent style XF
  1353.             // bit 2-0; mask 0x0007; XF_TYPE_PROT
  1354.             $xfTypeProt $this->_GetInt2d($recordData4);
  1355.             // bit 0; mask 0x01; 1 = cell is locked
  1356.             $isLocked (0x01 $xfTypeProt>> 0;
  1357.             $objStyle->getProtection()->setLocked($isLocked ?
  1358.                 PHPExcel_Style_Protection::PROTECTION_INHERIT PHPExcel_Style_Protection::PROTECTION_UNPROTECTED);
  1359.  
  1360.             // bit 1; mask 0x02; 1 = Formula is hidden
  1361.             $isHidden (0x02 $xfTypeProt>> 1;
  1362.             $objStyle->getProtection()->setHidden($isHidden ?
  1363.                 PHPExcel_Style_Protection::PROTECTION_PROTECTED PHPExcel_Style_Protection::PROTECTION_UNPROTECTED);
  1364.  
  1365.             // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF
  1366.             $isCellStyleXf (0x04 $xfTypeProt>> 2;
  1367.  
  1368.             // offset:  6; size: 1; Alignment and text break
  1369.             // bit 2-0, mask 0x07; horizontal alignment
  1370.             $horAlign (0x07 ord($recordData{6})) >> 0;
  1371.             switch ($horAlign{
  1372.                 case 0:
  1373.                     $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_GENERAL);
  1374.                     break;
  1375.                 case 1:
  1376.                     $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1377.                     break;
  1378.                 case 2:
  1379.                     $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1380.                     break;
  1381.                 case 3:
  1382.                     $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
  1383.                     break;
  1384.                 case 5:
  1385.                     $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY);
  1386.                     break;
  1387.                 case 6:
  1388.                     $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS);
  1389.                     break;
  1390.             }
  1391.             // bit 3, mask 0x08; wrap text
  1392.             $wrapText (0x08 ord($recordData{6})) >> 3;
  1393.             switch ($wrapText{
  1394.                 case 0:
  1395.                     $objStyle->getAlignment()->setWrapText(false);
  1396.                     break;
  1397.                 case 1:
  1398.                     $objStyle->getAlignment()->setWrapText(true);
  1399.                     break;
  1400.             }
  1401.             // bit 6-4, mask 0x70; vertical alignment
  1402.             $vertAlign (0x70 ord($recordData{6})) >> 4;
  1403.             switch ($vertAlign{
  1404.                 case 0:
  1405.                     $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_TOP);
  1406.                     break;
  1407.                 case 1:
  1408.                     $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);
  1409.                     break;
  1410.                 case 2:
  1411.                     $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_BOTTOM);
  1412.                     break;
  1413.                 case 3:
  1414.                     $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_JUSTIFY);
  1415.                     break;
  1416.             }
  1417.  
  1418.             if ($this->_version == self::XLS_BIFF8{
  1419.                 // offset:  7; size: 1; XF_ROTATION: Text rotation angle
  1420.                     $angle ord($recordData{7});
  1421.                     $rotation 0;
  1422.                     if ($angle <= 90{
  1423.                         $rotation $angle;
  1424.                     else if ($angle <= 180{
  1425.                         $rotation 90 $angle;
  1426.                     else if ($angle == 255{
  1427.                         $rotation = -165;
  1428.                     }
  1429.                     $objStyle->getAlignment()->setTextRotation($rotation);
  1430.  
  1431.                 // offset:  8; size: 1; Indentation, shrink to cell size, and text direction
  1432.                     // bit: 3-0; mask: 0x0F; indent level
  1433.                     $indent (0x0F ord($recordData{8})) >> 0;
  1434.                     $objStyle->getAlignment()->setIndent($indent);
  1435.  
  1436.                     // bit: 4; mask: 0x10; 1 = shrink content to fit into cell
  1437.                     $shrinkToFit (0x10 ord($recordData{8})) >> 4;
  1438.                     switch ($shrinkToFit{
  1439.                         case 0:
  1440.                             $objStyle->getAlignment()->setShrinkToFit(false);
  1441.                             break;
  1442.                         case 1:
  1443.                             $objStyle->getAlignment()->setShrinkToFit(true);
  1444.                             break;
  1445.                     }
  1446.  
  1447.                 // offset:  9; size: 1; Flags used for attribute groups
  1448.  
  1449.                 // offset: 10; size: 4; Cell border lines and background area
  1450.                     // bit: 3-0; mask: 0x0000000F; left style
  1451.                     if ($bordersLeftStyle $this->_mapBorderStyle((0x0000000F $this->_GetInt4d($recordData10)) >> 0)) {
  1452.                         $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
  1453.                     }
  1454.                     // bit: 7-4; mask: 0x000000F0; right style
  1455.                     if ($bordersRightStyle $this->_mapBorderStyle((0x000000F0 $this->_GetInt4d($recordData10)) >> 4)) {
  1456.                         $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
  1457.                     }
  1458.                     // bit: 11-8; mask: 0x00000F00; top style
  1459.                     if ($bordersTopStyle $this->_mapBorderStyle((0x00000F00 $this->_GetInt4d($recordData10)) >> 8)) {
  1460.                         $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
  1461.                     }
  1462.                     // bit: 15-12; mask: 0x0000F000; bottom style
  1463.                     if ($bordersBottomStyle $this->_mapBorderStyle((0x0000F000 $this->_GetInt4d($recordData10)) >> 12)) {
  1464.                         $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
  1465.                     }
  1466.                     // bit: 22-16; mask: 0x007F0000; left color
  1467.                     $objStyle->getBorders()->getLeft()->colorIndex (0x007F0000 $this->_GetInt4d($recordData10)) >> 16;
  1468.  
  1469.                     // bit: 29-23; mask: 0x3F800000; right color
  1470.                     $objStyle->getBorders()->getRight()->colorIndex (0x3F800000 $this->_GetInt4d($recordData10)) >> 23;
  1471.  
  1472.                     // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
  1473.                     $diagonalDown (0x40000000 $this->_GetInt4d($recordData10)) >> 30 ?
  1474.                         true false;
  1475.  
  1476.                     // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
  1477.                     $diagonalUp (0x80000000 $this->_GetInt4d($recordData10)) >> 31 ?
  1478.                         true false;
  1479.  
  1480.                     if ($diagonalUp == false && $diagonalDown == false{
  1481.                         $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_NONE);
  1482.                     elseif ($diagonalUp == true && $diagonalDown == false{
  1483.                         $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_UP);
  1484.                     elseif ($diagonalUp == false && $diagonalDown == true{
  1485.                         $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_DOWN);
  1486.                     elseif ($diagonalUp == true && $diagonalDown == true{
  1487.                         $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_BOTH);
  1488.                     }
  1489.  
  1490.                 // offset: 14; size: 4;
  1491.                     // bit: 6-0; mask: 0x0000007F; top color
  1492.                     $objStyle->getBorders()->getTop()->colorIndex (0x0000007F $this->_GetInt4d($recordData14)) >> 0;
  1493.  
  1494.                     // bit: 13-7; mask: 0x00003F80; bottom color
  1495.                     $objStyle->getBorders()->getBottom()->colorIndex (0x00003F80 $this->_GetInt4d($recordData14)) >> 7;
  1496.  
  1497.                     // bit: 20-14; mask: 0x001FC000; diagonal color
  1498.                     $objStyle->getBorders()->getDiagonal()->colorIndex (0x001FC000 $this->_GetInt4d($recordData14)) >> 14;
  1499.  
  1500.                     // bit: 24-21; mask: 0x01E00000; diagonal style
  1501.                     if ($bordersDiagonalStyle $this->_mapBorderStyle((0x01E00000 $this->_GetInt4d($recordData14)) >> 21)) {
  1502.                         $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
  1503.                     }
  1504.  
  1505.                     // bit: 31-26; mask: 0xFC000000 fill pattern
  1506.                     if ($fillType $this->_mapFillPattern((0xFC000000 $this->_GetInt4d($recordData14)) >> 26)) {
  1507.                         $objStyle->getFill()->setFillType($fillType);
  1508.                     }
  1509.                 // offset: 18; size: 2; pattern and background colour
  1510.                     // bit: 6-0; mask: 0x007F; color index for pattern color
  1511.                     $objStyle->getFill()->startcolorIndex (0x007F $this->_GetInt2d($recordData18)) >> 0;
  1512.  
  1513.                     // bit: 13-7; mask: 0x3F80; color index for pattern background
  1514.                     $objStyle->getFill()->endcolorIndex (0x3F80 $this->_GetInt2d($recordData18)) >> 7;
  1515.             else {
  1516.                 // BIFF5
  1517.  
  1518.                 // offset: 7; size: 1; Text orientation and flags
  1519.                 $orientationAndFlags ord($recordData{7});
  1520.  
  1521.                 // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
  1522.                 $xfOrientation (0x03 $orientationAndFlags>> 0;
  1523.                 switch ($xfOrientation{
  1524.                     case 0:
  1525.                         $objStyle->getAlignment()->setTextRotation(0);
  1526.                         break;
  1527.                     case 1:
  1528.                         $objStyle->getAlignment()->setTextRotation(-165);
  1529.                         break;
  1530.                     case 2:
  1531.                         $objStyle->getAlignment()->setTextRotation(90);
  1532.                         break;
  1533.                     case 3:
  1534.                         $objStyle->getAlignment()->setTextRotation(-90);
  1535.                         break;
  1536.                 }
  1537.  
  1538.                 // offset: 8; size: 4; cell border lines and background area
  1539.                 $borderAndBackground $this->_GetInt4d($recordData8);
  1540.  
  1541.                 // bit: 6-0; mask: 0x0000007F; color index for pattern color
  1542.                 $objStyle->getFill()->startcolorIndex (0x0000007F $borderAndBackground>> 0;
  1543.  
  1544.                 // bit: 13-7; mask: 0x00003F80; color index for pattern background
  1545.                 $objStyle->getFill()->endcolorIndex (0x00003F80 $borderAndBackground>> 7;
  1546.  
  1547.                 // bit: 21-16; mask: 0x003F0000; fill pattern
  1548.                 $objStyle->getFill()->setFillType($this->_mapFillPattern((0x003F0000 $borderAndBackground>> 16));
  1549.  
  1550.                 // bit: 24-22; mask: 0x01C00000; bottom line style
  1551.                 $objStyle->getBorders()->getBottom()->setBorderStyle($this->_mapBorderStyle((0x01C00000 $borderAndBackground>> 22));
  1552.  
  1553.                 // bit: 31-25; mask: 0xFE000000; bottom line color
  1554.                 $objStyle->getBorders()->getBottom()->colorIndex (0xFE000000 $borderAndBackground>> 25;
  1555.  
  1556.                 // offset: 12; size: 4; cell border lines
  1557.                 $borderLines $this->_GetInt4d($recordData12);
  1558.  
  1559.                 // bit: 2-0; mask: 0x00000007; top line style
  1560.                 $objStyle->getBorders()->getTop()->setBorderStyle($this->_mapBorderStyle((0x00000007 $borderLines>> 0));
  1561.  
  1562.                 // bit: 5-3; mask: 0x00000038; left line style
  1563.                 $objStyle->getBorders()->getLeft()->setBorderStyle($this->_mapBorderStyle((0x00000038 $borderLines>> 3));
  1564.  
  1565.                 // bit: 8-6; mask: 0x000001C0; right line style
  1566.                 $objStyle->getBorders()->getRight()->setBorderStyle($this->_mapBorderStyle((0x000001C0 $borderLines>> 6));
  1567.  
  1568.                 // bit: 15-9; mask: 0x0000FE00; top line color index
  1569.                 $objStyle->getBorders()->getTop()->colorIndex (0x0000FE00 $borderLines>> 9;
  1570.  
  1571.                 // bit: 22-16; mask: 0x007F0000; left line color index
  1572.                 $objStyle->getBorders()->getLeft()->colorIndex (0x007F0000 $borderLines>> 16;
  1573.  
  1574.                 // bit: 29-23; mask: 0x3F800000; right line color index
  1575.                 $objStyle->getBorders()->getRight()->colorIndex (0x3F800000 $borderLines>> 23;
  1576.             }
  1577.  
  1578.             // add cellStyleXf or cellXf and update mapping
  1579.             if ($isCellStyleXf{
  1580.                 // we only read one style XF record which is always the first
  1581.                 if ($this->_xfIndex == 0{
  1582.                     $this->_phpExcel->addCellStyleXf($objStyle);
  1583.                     $this->_mapCellStyleXfIndex[$this->_xfIndex0;
  1584.                 }
  1585.             else {
  1586.                 // we read all cell XF records
  1587.                 $this->_phpExcel->addCellXf($objStyle);
  1588.                 $this->_mapCellXfIndex[$this->_xfIndexcount($this->_phpExcel->getCellXfCollection()) 1;
  1589.             }
  1590.  
  1591.             // update XF index for when we read next record
  1592.             ++$this->_xfIndex;
  1593.         }
  1594.     }
  1595.  
  1596.     /**
  1597.      *
  1598.      */
  1599.     private function _readXfExt()
  1600.     {
  1601.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1602.         $recordData substr($this->_data$this->_pos 4$length);
  1603.  
  1604.         // move stream pointer to next record
  1605.         $this->_pos += $length;
  1606.  
  1607.         if (!$this->_readDataOnly{
  1608.             // offset: 0; size: 2; 0x087D = repeated header
  1609.  
  1610.             // offset: 2; size: 2
  1611.  
  1612.             // offset: 4; size: 8; not used
  1613.  
  1614.             // offset: 12; size: 2; record version
  1615.  
  1616.             // offset: 14; size: 2; index to XF record which this record modifies
  1617.             $ixfe $this->_GetInt2d($recordData14);
  1618.  
  1619.             // offset: 16; size: 2; not used
  1620.  
  1621.             // offset: 18; size: 2; number of extension properties that follow
  1622.             $cexts $this->_GetInt2d($recordData18);
  1623.  
  1624.             // start reading the actual extension data
  1625.             $offset 20;
  1626.             while ($offset $length{
  1627.                 // extension type
  1628.                 $extType $this->_GetInt2d($recordData$offset);
  1629.  
  1630.                 // extension length
  1631.                 $cb $this->_GetInt2d($recordData$offset 2);
  1632.  
  1633.                 // extension data
  1634.                 $extData substr($recordData$offset 4$cb);
  1635.  
  1636.                 switch ($extType{
  1637.                     case 4:        // fill start color
  1638.                         $xclfType  $this->_GetInt2d($extData0)// color type
  1639.                         $xclrValue substr($extData44)// color value (value based on color type)
  1640.  
  1641.                         if ($xclfType == 2{
  1642.                             $rgb sprintf('%02X%02X%02X'ord($xclrValue{0})ord($xclrValue{1})ord($xclrValue{2}));
  1643.  
  1644.                             // modify the relevant style property
  1645.                             if isset($this->_mapCellXfIndex[$ixfe]) ) {
  1646.                                 $fill $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill();
  1647.                                 $fill->getStartColor()->setRGB($rgb);
  1648.                                 unset($fill->startcolorIndex)// normal color index does not apply, discard
  1649.                             }
  1650.                         }
  1651.                         break;
  1652.  
  1653.                     case 5:        // fill end color
  1654.                         $xclfType  $this->_GetInt2d($extData0)// color type
  1655.                         $xclrValue substr($extData44)// color value (value based on color type)
  1656.  
  1657.                         if ($xclfType == 2{
  1658.                             $rgb sprintf('%02X%02X%02X'ord($xclrValue{0})ord($xclrValue{1})ord($xclrValue{2}));
  1659.  
  1660.                             // modify the relevant style property
  1661.                             if isset($this->_mapCellXfIndex[$ixfe]) ) {
  1662.                                 $fill $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill();
  1663.                                 $fill->getEndColor()->setRGB($rgb);
  1664.                                 unset($fill->endcolorIndex)// normal color index does not apply, discard
  1665.                             }
  1666.                         }
  1667.                         break;
  1668.  
  1669.                     case 7:        // border color top
  1670.                         $xclfType  $this->_GetInt2d($extData0)// color type
  1671.                         $xclrValue substr($extData44)// color value (value based on color type)
  1672.  
  1673.                         if ($xclfType == 2{
  1674.                             $rgb sprintf('%02X%02X%02X'ord($xclrValue{0})ord($xclrValue{1})ord($xclrValue{2}));
  1675.  
  1676.                             // modify the relevant style property
  1677.                             if isset($this->_mapCellXfIndex[$ixfe]) ) {
  1678.                                 $top $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getTop();
  1679.                                 $top->getColor()->setRGB($rgb);
  1680.                                 unset($top->colorIndex)// normal color index does not apply, discard
  1681.                             }
  1682.                         }
  1683.                         break;
  1684.  
  1685.                     case 8:        // border color bottom
  1686.                         $xclfType  $this->_GetInt2d($extData0)// color type
  1687.                         $xclrValue substr($extData44)// color value (value based on color type)
  1688.  
  1689.                         if ($xclfType == 2{
  1690.                             $rgb sprintf('%02X%02X%02X'ord($xclrValue{0})ord($xclrValue{1})ord($xclrValue{2}));
  1691.  
  1692.                             // modify the relevant style property
  1693.                             if isset($this->_mapCellXfIndex[$ixfe]) ) {
  1694.                                 $bottom $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getBottom();
  1695.                                 $bottom->getColor()->setRGB($rgb);
  1696.                                 unset($bottom->colorIndex)// normal color index does not apply, discard
  1697.                             }
  1698.                         }
  1699.                         break;
  1700.  
  1701.                     case 9:        // border color left
  1702.                         $xclfType  $this->_GetInt2d($extData0)// color type
  1703.                         $xclrValue substr($extData44)// color value (value based on color type)
  1704.  
  1705.                         if ($xclfType == 2{
  1706.                             $rgb sprintf('%02X%02X%02X'ord($xclrValue{0})ord($xclrValue{1})ord($xclrValue{2}));
  1707.  
  1708.                             // modify the relevant style property
  1709.                             if isset($this->_mapCellXfIndex[$ixfe]) ) {
  1710.                                 $left $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getLeft();
  1711.                                 $left->getColor()->setRGB($rgb);
  1712.                                 unset($left->colorIndex)// normal color index does not apply, discard
  1713.                             }
  1714.                         }
  1715.                         break;
  1716.  
  1717.                     case 10:        // border color right
  1718.                         $xclfType  $this->_GetInt2d($extData0)// color type
  1719.                         $xclrValue substr($extData44)// color value (value based on color type)
  1720.  
  1721.                         if ($xclfType == 2{
  1722.                             $rgb sprintf('%02X%02X%02X'ord($xclrValue{0})ord($xclrValue{1})ord($xclrValue{2}));
  1723.  
  1724.                             // modify the relevant style property
  1725.                             if isset($this->_mapCellXfIndex[$ixfe]) ) {
  1726.                                 $right $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getRight();
  1727.                                 $right->getColor()->setRGB($rgb);
  1728.                                 unset($right->colorIndex)// normal color index does not apply, discard
  1729.                             }
  1730.                         }
  1731.                         break;
  1732.  
  1733.                     case 11:        // border color diagonal
  1734.                         $xclfType  $this->_GetInt2d($extData0)// color type
  1735.                         $xclrValue substr($extData44)// color value (value based on color type)
  1736.  
  1737.                         if ($xclfType == 2{
  1738.                             $rgb sprintf('%02X%02X%02X'ord($xclrValue{0})ord($xclrValue{1})ord($xclrValue{2}));
  1739.  
  1740.                             // modify the relevant style property
  1741.                             if isset($this->_mapCellXfIndex[$ixfe]) ) {
  1742.                                 $diagonal $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
  1743.                                 $diagonal->getColor()->setRGB($rgb);
  1744.                                 unset($diagonal->colorIndex)// normal color index does not apply, discard
  1745.                             }
  1746.                         }
  1747.                         break;
  1748.  
  1749.                     case 13:    // font color
  1750.                         $xclfType  $this->_GetInt2d($extData0)// color type
  1751.                         $xclrValue substr($extData44)// color value (value based on color type)
  1752.  
  1753.                         if ($xclfType == 2{
  1754.                             $rgb sprintf('%02X%02X%02X'ord($xclrValue{0})ord($xclrValue{1})ord($xclrValue{2}));
  1755.  
  1756.                             // modify the relevant style property
  1757.                             if isset($this->_mapCellXfIndex[$ixfe]) ) {
  1758.                                 $font $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFont();
  1759.                                 $font->getColor()->setRGB($rgb);
  1760.                                 unset($font->colorIndex)// normal color index does not apply, discard
  1761.                             }
  1762.                         }
  1763.                         break;
  1764.                 }
  1765.  
  1766.                 $offset += $cb;
  1767.             }
  1768.         }
  1769.  
  1770.     }
  1771.  
  1772.     /**
  1773.      * Read STYLE record
  1774.      */
  1775.     private function _readStyle()
  1776.     {
  1777.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1778.         $recordData substr($this->_data$this->_pos 4$length);
  1779.  
  1780.         // move stream pointer to next record
  1781.         $this->_pos += $length;
  1782.  
  1783.         if (!$this->_readDataOnly{
  1784.             // offset: 0; size: 2; index to XF record and flag for built-in style
  1785.             $ixfe $this->_GetInt2d($recordData0);
  1786.  
  1787.             // bit: 11-0; mask 0x0FFF; index to XF record
  1788.             $xfIndex (0x0FFF $ixfe>> 0;
  1789.  
  1790.             // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style
  1791.             $isBuiltIn = (bool) ((0x8000 $ixfe>> 15);
  1792.  
  1793.             if ($isBuiltIn{
  1794.                 // offset: 2; size: 1; identifier for built-in style
  1795.                 $builtInId ord($recordData{2});
  1796.  
  1797.                 switch ($builtInId{
  1798.                 case 0x00:
  1799.                     // currently, we are not using this for anything
  1800.                     break;
  1801.  
  1802.                 default:
  1803.                     break;
  1804.                 }
  1805.  
  1806.             else {
  1807.                 // user-defined; not supported by PHPExcel
  1808.             }
  1809.         }
  1810.     }
  1811.  
  1812.     /**
  1813.      * Read PALETTE record
  1814.      */
  1815.     private function _readPalette()
  1816.     {
  1817.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1818.         $recordData substr($this->_data$this->_pos 4$length);
  1819.  
  1820.         // move stream pointer to next record
  1821.         $this->_pos += $length;
  1822.  
  1823.         if (!$this->_readDataOnly{
  1824.             // offset: 0; size: 2; number of following colors
  1825.             $nm $this->_GetInt2d($recordData0);
  1826.  
  1827.             // list of RGB colors
  1828.             for ($i 0$i $nm++$i{
  1829.                 $rgb substr($recordData$i4);
  1830.                 $this->_palette[$this->_readRGB($rgb);
  1831.             }
  1832.         }
  1833.     }
  1834.  
  1835.     /**
  1836.      * SHEET
  1837.      *
  1838.      * This record is  located in the  Workbook Globals
  1839.      * Substream  and represents a sheet inside the workbook.
  1840.      * One SHEET record is written for each sheet. It stores the
  1841.      * sheet name and a stream offset to the BOF record of the
  1842.      * respective Sheet Substream within the Workbook Stream.
  1843.      *
  1844.      * --    "OpenOffice.org's Documentation of the Microsoft
  1845.      *         Excel File Format"
  1846.      */
  1847.     private function _readSheet()
  1848.     {
  1849.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1850.         $recordData substr($this->_data$this->_pos 4$length);
  1851.  
  1852.         // move stream pointer to next record
  1853.         $this->_pos += $length;
  1854.  
  1855.         // offset: 0; size: 4; absolute stream position of the BOF record of the sheet
  1856.         $rec_offset $this->_GetInt4d($recordData0);
  1857.  
  1858.         // offset: 4; size: 1; sheet state
  1859.         switch (ord($recordData{4})) {
  1860.             case 0x00$sheetState PHPExcel_Worksheet::SHEETSTATE_VISIBLE;    break;
  1861.             case 0x01$sheetState PHPExcel_Worksheet::SHEETSTATE_HIDDEN;     break;
  1862.             case 0x02$sheetState PHPExcel_Worksheet::SHEETSTATE_VERYHIDDENbreak;
  1863.             default$sheetState PHPExcel_Worksheet::SHEETSTATE_VISIBLE;      break;
  1864.         }
  1865.  
  1866.         // offset: 5; size: 1; sheet type
  1867.         $sheetType ord($recordData{5});
  1868.  
  1869.         // offset: 6; size: var; sheet name
  1870.         if ($this->_version == self::XLS_BIFF8{
  1871.             $string $this->_readUnicodeStringShort(substr($recordData6));
  1872.             $rec_name $string['value'];
  1873.         elseif ($this->_version == self::XLS_BIFF7{
  1874.             $string $this->_readByteStringShort(substr($recordData6));
  1875.             $rec_name $string['value'];
  1876.         }
  1877.  
  1878.         $this->_sheets[array(
  1879.             'name' => $rec_name,
  1880.             'offset' => $rec_offset,
  1881.             'sheetState' => $sheetState,
  1882.             'sheetType' => $sheetType,
  1883.         );
  1884.     }
  1885.  
  1886.     /**
  1887.      * Read EXTERNALBOOK record
  1888.      */
  1889.     private function _readExternalBook()
  1890.     {
  1891.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1892.         $recordData substr($this->_data$this->_pos 4$length);
  1893.  
  1894.         // move stream pointer to next record
  1895.         $this->_pos += $length;
  1896.  
  1897.         // offset within record data
  1898.         $offset 0;
  1899.  
  1900.         // there are 4 types of records
  1901.         if (strlen($recordData4{
  1902.             // external reference
  1903.             // offset: 0; size: 2; number of sheet names ($nm)
  1904.             $nm $this->_GetInt2d($recordData0);
  1905.             $offset += 2;
  1906.  
  1907.             // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
  1908.             $encodedUrlString $this->_readUnicodeStringLong(substr($recordData2));
  1909.             $offset += $encodedUrlString['size'];
  1910.  
  1911.             // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
  1912.             $externalSheetNames array();
  1913.             for ($i 0$i $nm++$i{
  1914.                 $externalSheetNameString $this->_readUnicodeStringLong(substr($recordData$offset));
  1915.                 $externalSheetNames[$externalSheetNameString['value'];
  1916.                 $offset += $externalSheetNameString['size'];
  1917.             }
  1918.  
  1919.             // store the record data
  1920.             $this->_externalBooks[array(
  1921.                 'type' => 'external',
  1922.                 'encodedUrl' => $encodedUrlString['value'],
  1923.                 'externalSheetNames' => $externalSheetNames,
  1924.             );
  1925.  
  1926.         elseif (substr($recordData22== pack('CC'0x010x04)) {
  1927.             // internal reference
  1928.             // offset: 0; size: 2; number of sheet in this document
  1929.             // offset: 2; size: 2; 0x01 0x04
  1930.             $this->_externalBooks[array(
  1931.                 'type' => 'internal',
  1932.             );
  1933.         elseif (substr($recordData04== pack('vCC'0x00010x010x3A)) {
  1934.             // add-in function
  1935.             // offset: 0; size: 2; 0x0001
  1936.             $this->_externalBooks[array(
  1937.                 'type' => 'addInFunction',
  1938.             );
  1939.         elseif (substr($recordData02== pack('v'0x0000)) {
  1940.             // DDE links, OLE links
  1941.             // offset: 0; size: 2; 0x0000
  1942.             // offset: 2; size: var; encoded source document name
  1943.             $this->_externalBooks[array(
  1944.                 'type' => 'DDEorOLE',
  1945.             );
  1946.         }
  1947.     }
  1948.  
  1949.     /**
  1950.      * Read EXTERNNAME record.
  1951.      */
  1952.     private function _readExternName()
  1953.     {
  1954.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1955.         $recordData substr($this->_data$this->_pos 4$length);
  1956.  
  1957.         // move stream pointer to next record
  1958.         $this->_pos += $length;
  1959.  
  1960.         // external sheet references provided for named cells
  1961.         if ($this->_version == self::XLS_BIFF8{
  1962.             // offset: 0; size: 2; options
  1963.             $options $this->_GetInt2d($recordData0);
  1964.  
  1965.             // offset: 2; size: 2;
  1966.  
  1967.             // offset: 4; size: 2; not used
  1968.  
  1969.             // offset: 6; size: var
  1970.             $nameString $this->_readUnicodeStringShort(substr($recordData6));
  1971.  
  1972.             // offset: var; size: var; formula data
  1973.             $offset $nameString['size'];
  1974.             $formula $this->_getFormulaFromStructure(substr($recordData$offset));
  1975.  
  1976.             $this->_externalNames[array(
  1977.                 'name' => $nameString['value'],
  1978.                 'formula' => $formula,
  1979.             );
  1980.         }
  1981.     }
  1982.  
  1983.     /**
  1984.      * Read EXTERNSHEET record
  1985.      */
  1986.     private function _readExternSheet()
  1987.     {
  1988.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  1989.         $recordData substr($this->_data$this->_pos 4$length);
  1990.  
  1991.         // move stream pointer to next record
  1992.         $this->_pos += $length;
  1993.  
  1994.         // external sheet references provided for named cells
  1995.         if ($this->_version == self::XLS_BIFF8{
  1996.             // offset: 0; size: 2; number of following ref structures
  1997.             $nm $this->_GetInt2d($recordData0);
  1998.             for ($i 0$i $nm++$i{
  1999.                 $this->_ref[array(
  2000.                     // offset: 2 + 6 * $i; index to EXTERNALBOOK record
  2001.                     'externalBookIndex' => $this->_GetInt2d($recordData$i),
  2002.                     // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
  2003.                     'firstSheetIndex' => $this->_GetInt2d($recordData$i),
  2004.                     // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
  2005.                     'lastSheetIndex' => $this->_GetInt2d($recordData$i),
  2006.                 );
  2007.             }
  2008.         }
  2009.     }
  2010.  
  2011.     /**
  2012.      * DEFINEDNAME
  2013.      *
  2014.      * This record is part of a Link Table. It contains the name
  2015.      * and the token array of an internal defined name. Token
  2016.      * arrays of defined names contain tokens with aberrant
  2017.      * token classes.
  2018.      *
  2019.      * --    "OpenOffice.org's Documentation of the Microsoft
  2020.      *         Excel File Format"
  2021.      */
  2022.     private function _readDefinedName()
  2023.     {
  2024.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2025.         $recordData substr($this->_data$this->_pos 4$length);
  2026.  
  2027.         // move stream pointer to next record
  2028.         $this->_pos += $length;
  2029.  
  2030.         if ($this->_version == self::XLS_BIFF8{
  2031.             // retrieves named cells
  2032.  
  2033.             // offset: 0; size: 2; option flags
  2034.             $opts $this->_GetInt2d($recordData0);
  2035.  
  2036.                 // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
  2037.                 $isBuiltInName (0x0020 $opts>> 5;
  2038.  
  2039.             // offset: 2; size: 1; keyboard shortcut
  2040.  
  2041.             // offset: 3; size: 1; length of the name (character count)
  2042.             $nlen ord($recordData{3});
  2043.  
  2044.             // offset: 4; size: 2; size of the formula data (it can happen that this is zero)
  2045.             // note: there can also be additional data, this is not included in $flen
  2046.             $flen $this->_GetInt2d($recordData4);
  2047.  
  2048.             // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based)
  2049.             $scope $this->_GetInt2d($recordData8);
  2050.  
  2051.             // offset: 14; size: var; Name (Unicode string without length field)
  2052.             $string $this->_readUnicodeString(substr($recordData14)$nlen);
  2053.  
  2054.             // offset: var; size: $flen; formula data
  2055.             $offset 14 $string['size'];
  2056.             $formulaStructure pack('v'$flensubstr($recordData$offset);
  2057.  
  2058.             try {
  2059.                 $formula $this->_getFormulaFromStructure($formulaStructure);
  2060.             catch (Exception $e{
  2061.                 $formula '';
  2062.             }
  2063.  
  2064.             $this->_definedname[array(
  2065.                 'isBuiltInName' => $isBuiltInName,
  2066.                 'name' => $string['value'],
  2067.                 'formula' => $formula,
  2068.                 'scope' => $scope,
  2069.             );
  2070.         }
  2071.     }
  2072.  
  2073.     /**
  2074.      * Read MSODRAWINGGROUP record
  2075.      */
  2076.     private function _readMsoDrawingGroup()
  2077.     {
  2078.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2079.  
  2080.         // get spliced record data
  2081.         $splicedRecordData $this->_getSplicedRecordData();
  2082.         $recordData $splicedRecordData['recordData'];
  2083.  
  2084.         $this->_drawingGroupData .= $recordData;
  2085.     }
  2086.  
  2087.     /**
  2088.      * SST - Shared String Table
  2089.      *
  2090.      * This record contains a list of all strings used anywhere
  2091.      * in the workbook. Each string occurs only once. The
  2092.      * workbook uses indexes into the list to reference the
  2093.      * strings.
  2094.      *
  2095.      * --    "OpenOffice.org's Documentation of the Microsoft
  2096.      *         Excel File Format"
  2097.      **/
  2098.  
  2099.     private function _readSst()
  2100.     {
  2101.         // offset within (spliced) record data
  2102.         $pos 0;
  2103.  
  2104.         // get spliced record data
  2105.         $splicedRecordData $this->_getSplicedRecordData();
  2106.  
  2107.         $recordData $splicedRecordData['recordData'];
  2108.         $spliceOffsets $splicedRecordData['spliceOffsets'];
  2109.  
  2110.         // offset: 0; size: 4; total number of strings in the workbook
  2111.         $pos += 4;
  2112.  
  2113.         // offset: 4; size: 4; number of following strings ($nm)
  2114.         $nm $this->_GetInt4d($recordData4);
  2115.         $pos += 4;
  2116.  
  2117.         // loop through the Unicode strings (16-bit length)
  2118.         for ($i 0$i $nm++$i{
  2119.  
  2120.             // number of characters in the Unicode string
  2121.             $numChars $this->_GetInt2d($recordData$pos);
  2122.             $pos += 2;
  2123.  
  2124.             // option flags
  2125.             $optionFlags ord($recordData{$pos});
  2126.             ++$pos;
  2127.  
  2128.             // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
  2129.             $isCompressed (($optionFlags 0x01== 0;
  2130.  
  2131.             // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
  2132.             $hasAsian (($optionFlags 0x04!= 0);
  2133.  
  2134.             // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
  2135.             $hasRichText (($optionFlags 0x08!= 0);
  2136.  
  2137.             if ($hasRichText{
  2138.                 // number of Rich-Text formatting runs
  2139.                 $formattingRuns $this->_GetInt2d($recordData$pos);
  2140.                 $pos += 2;
  2141.             }
  2142.  
  2143.             if ($hasAsian{
  2144.                 // size of Asian phonetic setting
  2145.                 $extendedRunLength $this->_GetInt4d($recordData$pos);
  2146.                 $pos += 4;
  2147.             }
  2148.  
  2149.             // expected byte length of character array if not split
  2150.             $len ($isCompressed$numChars $numChars 2;
  2151.  
  2152.             // look up limit position
  2153.             foreach ($spliceOffsets as $spliceOffset{
  2154.                 // it can happen that the string is empty, therefore we need
  2155.                 // <= and not just <
  2156.                 if ($pos <= $spliceOffset{
  2157.                     $limitpos $spliceOffset;
  2158.                     break;
  2159.                 }
  2160.             }
  2161.  
  2162.             if ($pos $len <= $limitpos{
  2163.                 // character array is not split between records
  2164.  
  2165.                 $retstr substr($recordData$pos$len);
  2166.                 $pos += $len;
  2167.  
  2168.             else {
  2169.                 // character array is split between records
  2170.  
  2171.                 // first part of character array
  2172.                 $retstr substr($recordData$pos$limitpos $pos);
  2173.  
  2174.                 $bytesRead $limitpos $pos;
  2175.  
  2176.                 // remaining characters in Unicode string
  2177.                 $charsLeft $numChars (($isCompressed$bytesRead ($bytesRead 2));
  2178.  
  2179.                 $pos $limitpos;
  2180.  
  2181.                 // keep reading the characters
  2182.                 while ($charsLeft 0{
  2183.  
  2184.                     // look up next limit position, in case the string span more than one continue record
  2185.                     foreach ($spliceOffsets as $spliceOffset{
  2186.                         if ($pos $spliceOffset{
  2187.                             $limitpos $spliceOffset;
  2188.                             break;
  2189.                         }
  2190.                     }
  2191.  
  2192.                     // repeated option flags
  2193.                     // OpenOffice.org documentation 5.21
  2194.                     $option ord($recordData{$pos});
  2195.                     ++$pos;
  2196.  
  2197.                     if ($isCompressed && ($option == 0)) {
  2198.                         // 1st fragment compressed
  2199.                         // this fragment compressed
  2200.                         $len min($charsLeft$limitpos $pos);
  2201.                         $retstr .= substr($recordData$pos$len);
  2202.                         $charsLeft -= $len;
  2203.                         $isCompressed true;
  2204.  
  2205.                     elseif (!$isCompressed && ($option != 0)) {
  2206.                         // 1st fragment uncompressed
  2207.                         // this fragment uncompressed
  2208.                         $len min($charsLeft 2$limitpos $pos);
  2209.                         $retstr .= substr($recordData$pos$len);
  2210.                         $charsLeft -= $len 2;
  2211.                         $isCompressed false;
  2212.  
  2213.                     elseif (!$isCompressed && ($option == 0)) {
  2214.                         // 1st fragment uncompressed
  2215.                         // this fragment compressed
  2216.                         $len min($charsLeft$limitpos $pos);
  2217.                         for ($j 0$j $len++$j{
  2218.                             $retstr .= $recordData{$pos $jchr(0);
  2219.                         }
  2220.                         $charsLeft -= $len;
  2221.                         $isCompressed false;
  2222.  
  2223.                     else {
  2224.                         // 1st fragment compressed
  2225.                         // this fragment uncompressed
  2226.                         $newstr '';
  2227.                         for ($j 0$j strlen($retstr)++$j{
  2228.                             $newstr .= $retstr[$jchr(0);
  2229.                         }
  2230.                         $retstr $newstr;
  2231.                         $len min($charsLeft 2$limitpos $pos);
  2232.                         $retstr .= substr($recordData$pos$len);
  2233.                         $charsLeft -= $len 2;
  2234.                         $isCompressed false;
  2235.                     }
  2236.  
  2237.                     $pos += $len;
  2238.                 }
  2239.             }
  2240.  
  2241.             // convert to UTF-8
  2242.             $retstr $this->_encodeUTF16($retstr$isCompressed);
  2243.  
  2244.             // read additional Rich-Text information, if any
  2245.             $fmtRuns array();
  2246.             if ($hasRichText{
  2247.                 // list of formatting runs
  2248.                 for ($j 0$j $formattingRuns++$j{
  2249.                     // first formatted character; zero-based
  2250.                     $charPos $this->_GetInt2d($recordData$pos $j 4);
  2251.  
  2252.                     // index to font record
  2253.                     $fontIndex $this->_GetInt2d($recordData$pos $j 4);
  2254.  
  2255.                     $fmtRuns[array(
  2256.                         'charPos' => $charPos,
  2257.                         'fontIndex' => $fontIndex,
  2258.                     );
  2259.                 }
  2260.                 $pos += $formattingRuns;
  2261.             }
  2262.  
  2263.             // read additional Asian phonetics information, if any
  2264.             if ($hasAsian{
  2265.                 // For Asian phonetic settings, we skip the extended string data
  2266.                 $pos += $extendedRunLength;
  2267.             }
  2268.  
  2269.             // store the shared sting
  2270.             $this->_sst[array(
  2271.                 'value' => $retstr,
  2272.                 'fmtRuns' => $fmtRuns,
  2273.             );
  2274.         }
  2275.  
  2276.         // _getSplicedRecordData() takes care of moving current position in data stream
  2277.     }
  2278.  
  2279.     /**
  2280.      * Read PRINTGRIDLINES record
  2281.      */
  2282.     private function _readPrintGridlines()
  2283.     {
  2284.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2285.         $recordData substr($this->_data$this->_pos 4$length);
  2286.  
  2287.         // move stream pointer to next record
  2288.         $this->_pos += $length;
  2289.  
  2290.         if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly{
  2291.             // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines
  2292.             $printGridlines = (bool) $this->_GetInt2d($recordData0);
  2293.             $this->_phpSheet->setPrintGridlines($printGridlines);
  2294.         }
  2295.     }
  2296.  
  2297.     /**
  2298.      * Read DEFAULTROWHEIGHT record
  2299.      */
  2300.     private function _readDefaultRowHeight()
  2301.     {
  2302.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2303.         $recordData substr($this->_data$this->_pos 4$length);
  2304.  
  2305.         // move stream pointer to next record
  2306.         $this->_pos += $length;
  2307.  
  2308.         // offset: 0; size: 2; option flags
  2309.         // offset: 2; size: 2; default height for unused rows, (twips 1/20 point)
  2310.         $height $this->_GetInt2d($recordData2);
  2311.         $this->_phpSheet->getDefaultRowDimension()->setRowHeight($height 20);
  2312.     }
  2313.  
  2314.     /**
  2315.      * Read SHEETPR record
  2316.      */
  2317.     private function _readSheetPr()
  2318.     {
  2319.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2320.         $recordData substr($this->_data$this->_pos 4$length);
  2321.  
  2322.         // move stream pointer to next record
  2323.         $this->_pos += $length;
  2324.  
  2325.         // offset: 0; size: 2
  2326.  
  2327.         // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
  2328.         $isSummaryBelow (0x0040 $this->_GetInt2d($recordData0)) >> 6;
  2329.         $this->_phpSheet->setShowSummaryBelow($isSummaryBelow);
  2330.  
  2331.         // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
  2332.         $isSummaryRight (0x0080 $this->_GetInt2d($recordData0)) >> 7;
  2333.         $this->_phpSheet->setShowSummaryRight($isSummaryRight);
  2334.  
  2335.         // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
  2336.         // this corresponds to radio button setting in page setup dialog in Excel
  2337.         $this->_isFitToPages = (bool) ((0x0100 $this->_GetInt2d($recordData0)) >> 8);
  2338.     }
  2339.  
  2340.     /**
  2341.      * Read HORIZONTALPAGEBREAKS record
  2342.      */
  2343.     private function _readHorizontalPageBreaks()
  2344.     {
  2345.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2346.         $recordData substr($this->_data$this->_pos 4$length);
  2347.  
  2348.         // move stream pointer to next record
  2349.         $this->_pos += $length;
  2350.  
  2351.         if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly{
  2352.  
  2353.             // offset: 0; size: 2; number of the following row index structures
  2354.             $nm $this->_GetInt2d($recordData0);
  2355.  
  2356.             // offset: 2; size: 6 * $nm; list of $nm row index structures
  2357.             for ($i 0$i $nm++$i{
  2358.                 $r $this->_GetInt2d($recordData$i);
  2359.                 $cf $this->_GetInt2d($recordData$i 2);
  2360.                 $cl $this->_GetInt2d($recordData$i 4);
  2361.  
  2362.                 // not sure why two column indexes are necessary?
  2363.                 $this->_phpSheet->setBreakByColumnAndRow($cf$rPHPExcel_Worksheet::BREAK_ROW);
  2364.             }
  2365.         }
  2366.     }
  2367.  
  2368.     /**
  2369.      * Read VERTICALPAGEBREAKS record
  2370.      */
  2371.     private function _readVerticalPageBreaks()
  2372.     {
  2373.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2374.         $recordData substr($this->_data$this->_pos 4$length);
  2375.  
  2376.         // move stream pointer to next record
  2377.         $this->_pos += $length;
  2378.  
  2379.         if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly{
  2380.             // offset: 0; size: 2; number of the following column index structures
  2381.             $nm $this->_GetInt2d($recordData0);
  2382.  
  2383.             // offset: 2; size: 6 * $nm; list of $nm row index structures
  2384.             for ($i 0$i $nm++$i{
  2385.                 $c $this->_GetInt2d($recordData$i);
  2386.                 $rf $this->_GetInt2d($recordData$i 2);
  2387.                 $rl $this->_GetInt2d($recordData$i 4);
  2388.  
  2389.                 // not sure why two row indexes are necessary?
  2390.                 $this->_phpSheet->setBreakByColumnAndRow($c$rfPHPExcel_Worksheet::BREAK_COLUMN);
  2391.             }
  2392.         }
  2393.     }
  2394.  
  2395.     /**
  2396.      * Read HEADER record
  2397.      */
  2398.     private function _readHeader()
  2399.     {
  2400.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2401.         $recordData substr($this->_data$this->_pos 4$length);
  2402.  
  2403.         // move stream pointer to next record
  2404.         $this->_pos += $length;
  2405.  
  2406.         if (!$this->_readDataOnly{
  2407.             // offset: 0; size: var
  2408.             // realized that $recordData can be empty even when record exists
  2409.             if ($recordData{
  2410.                 if ($this->_version == self::XLS_BIFF8{
  2411.                     $string $this->_readUnicodeStringLong($recordData);
  2412.                 else {
  2413.                     $string $this->_readByteStringShort($recordData);
  2414.                 }
  2415.  
  2416.                 $this->_phpSheet->getHeaderFooter()->setOddHeader($string['value']);
  2417.                 $this->_phpSheet->getHeaderFooter()->setEvenHeader($string['value']);
  2418.             }
  2419.         }
  2420.     }
  2421.  
  2422.     /**
  2423.      * Read FOOTER record
  2424.      */
  2425.     private function _readFooter()
  2426.     {
  2427.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2428.         $recordData substr($this->_data$this->_pos 4$length);
  2429.  
  2430.         // move stream pointer to next record
  2431.         $this->_pos += $length;
  2432.  
  2433.         if (!$this->_readDataOnly{
  2434.             // offset: 0; size: var
  2435.             // realized that $recordData can be empty even when record exists
  2436.             if ($recordData{
  2437.                 if ($this->_version == self::XLS_BIFF8{
  2438.                     $string $this->_readUnicodeStringLong($recordData);
  2439.                 else {
  2440.                     $string $this->_readByteStringShort($recordData);
  2441.                 }
  2442.                 $this->_phpSheet->getHeaderFooter()->setOddFooter($string['value']);
  2443.                 $this->_phpSheet->getHeaderFooter()->setEvenFooter($string['value']);
  2444.             }
  2445.         }
  2446.     }
  2447.  
  2448.     /**
  2449.      * Read HCENTER record
  2450.      */
  2451.     private function _readHcenter()
  2452.     {
  2453.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2454.         $recordData substr($this->_data$this->_pos 4$length);
  2455.  
  2456.         // move stream pointer to next record
  2457.         $this->_pos += $length;
  2458.  
  2459.         if (!$this->_readDataOnly{
  2460.             // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
  2461.             $isHorizontalCentered = (bool) $this->_GetInt2d($recordData0);
  2462.  
  2463.             $this->_phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
  2464.         }
  2465.     }
  2466.  
  2467.     /**
  2468.      * Read VCENTER record
  2469.      */
  2470.     private function _readVcenter()
  2471.     {
  2472.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2473.         $recordData substr($this->_data$this->_pos 4$length);
  2474.  
  2475.         // move stream pointer to next record
  2476.         $this->_pos += $length;
  2477.  
  2478.         if (!$this->_readDataOnly{
  2479.             // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
  2480.             $isVerticalCentered = (bool) $this->_GetInt2d($recordData0);
  2481.  
  2482.             $this->_phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
  2483.         }
  2484.     }
  2485.  
  2486.     /**
  2487.      * Read LEFTMARGIN record
  2488.      */
  2489.     private function _readLeftMargin()
  2490.     {
  2491.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2492.         $recordData substr($this->_data$this->_pos 4$length);
  2493.  
  2494.         // move stream pointer to next record
  2495.         $this->_pos += $length;
  2496.  
  2497.         if (!$this->_readDataOnly{
  2498.             // offset: 0; size: 8
  2499.             $this->_phpSheet->getPageMargins()->setLeft($this->_extractNumber($recordData));
  2500.         }
  2501.     }
  2502.  
  2503.     /**
  2504.      * Read RIGHTMARGIN record
  2505.      */
  2506.     private function _readRightMargin()
  2507.     {
  2508.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2509.         $recordData substr($this->_data$this->_pos 4$length);
  2510.  
  2511.         // move stream pointer to next record
  2512.         $this->_pos += $length;
  2513.  
  2514.         if (!$this->_readDataOnly{
  2515.             // offset: 0; size: 8
  2516.             $this->_phpSheet->getPageMargins()->setRight($this->_extractNumber($recordData));
  2517.         }
  2518.     }
  2519.  
  2520.     /**
  2521.      * Read TOPMARGIN record
  2522.      */
  2523.     private function _readTopMargin()
  2524.     {
  2525.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2526.         $recordData substr($this->_data$this->_pos 4$length);
  2527.  
  2528.         // move stream pointer to next record
  2529.         $this->_pos += $length;
  2530.  
  2531.         if (!$this->_readDataOnly{
  2532.             // offset: 0; size: 8
  2533.             $this->_phpSheet->getPageMargins()->setTop($this->_extractNumber($recordData));
  2534.         }
  2535.     }
  2536.  
  2537.     /**
  2538.      * Read BOTTOMMARGIN record
  2539.      */
  2540.     private function _readBottomMargin()
  2541.     {
  2542.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2543.         $recordData substr($this->_data$this->_pos 4$length);
  2544.  
  2545.         // move stream pointer to next record
  2546.         $this->_pos += $length;
  2547.  
  2548.         if (!$this->_readDataOnly{
  2549.             // offset: 0; size: 8
  2550.             $this->_phpSheet->getPageMargins()->setBottom($this->_extractNumber($recordData));
  2551.         }
  2552.     }
  2553.  
  2554.     /**
  2555.      * Read PAGESETUP record
  2556.      */
  2557.     private function _readPageSetup()
  2558.     {
  2559.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2560.         $recordData substr($this->_data$this->_pos 4$length);
  2561.  
  2562.         // move stream pointer to next record
  2563.         $this->_pos += $length;
  2564.  
  2565.         if (!$this->_readDataOnly{
  2566.             // offset: 0; size: 2; paper size
  2567.             $paperSize $this->_GetInt2d($recordData0);
  2568.  
  2569.             // offset: 2; size: 2; scaling factor
  2570.             $scale $this->_GetInt2d($recordData2);
  2571.  
  2572.             // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed
  2573.             $fitToWidth $this->_GetInt2d($recordData6);
  2574.  
  2575.             // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed
  2576.             $fitToHeight $this->_GetInt2d($recordData8);
  2577.  
  2578.             // offset: 10; size: 2; option flags
  2579.  
  2580.                 // bit: 1; mask: 0x0002; 0=landscape, 1=portrait
  2581.                 $isPortrait (0x0002 $this->_GetInt2d($recordData10)) >> 1;
  2582.  
  2583.                 // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
  2584.                 // when this bit is set, do not use flags for those properties
  2585.                 $isNotInit (0x0004 $this->_GetInt2d($recordData10)) >> 2;
  2586.  
  2587.             if (!$isNotInit{
  2588.                 $this->_phpSheet->getPageSetup()->setPaperSize($paperSize);
  2589.                 switch ($isPortrait{
  2590.                 case 0$this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE)break;
  2591.                 case 1$this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_PORTRAIT)break;
  2592.                 }
  2593.  
  2594.                 $this->_phpSheet->getPageSetup()->setScale($scalefalse);
  2595.                 $this->_phpSheet->getPageSetup()->setFitToPage((bool) $this->_isFitToPages);
  2596.                 $this->_phpSheet->getPageSetup()->setFitToWidth($fitToWidthfalse);
  2597.                 $this->_phpSheet->getPageSetup()->setFitToHeight($fitToHeightfalse);
  2598.             }
  2599.  
  2600.             // offset: 16; size: 8; header margin (IEEE 754 floating-point value)
  2601.             $marginHeader $this->_extractNumber(substr($recordData168));
  2602.             $this->_phpSheet->getPageMargins()->setHeader($marginHeader);
  2603.  
  2604.             // offset: 24; size: 8; footer margin (IEEE 754 floating-point value)
  2605.             $marginFooter $this->_extractNumber(substr($recordData248));
  2606.             $this->_phpSheet->getPageMargins()->setFooter($marginFooter);
  2607.         }
  2608.     }
  2609.  
  2610.     /**
  2611.      * PROTECT - Sheet protection (BIFF2 through BIFF8)
  2612.      *   if this record is omitted, then it also means no sheet protection
  2613.      */
  2614.     private function _readProtect()
  2615.     {
  2616.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2617.         $recordData substr($this->_data$this->_pos 4$length);
  2618.  
  2619.         // move stream pointer to next record
  2620.         $this->_pos += $length;
  2621.  
  2622.         if ($this->_readDataOnly{
  2623.             return;
  2624.         }
  2625.  
  2626.         // offset: 0; size: 2;
  2627.  
  2628.         // bit 0, mask 0x01; 1 = sheet is protected
  2629.         $bool (0x01 $this->_GetInt2d($recordData0)) >> 0;
  2630.         $this->_phpSheet->getProtection()->setSheet((bool)$bool);
  2631.     }
  2632.  
  2633.     /**
  2634.      * SCENPROTECT
  2635.      */
  2636.     private function _readScenProtect()
  2637.     {
  2638.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2639.         $recordData substr($this->_data$this->_pos 4$length);
  2640.  
  2641.         // move stream pointer to next record
  2642.         $this->_pos += $length;
  2643.  
  2644.         if ($this->_readDataOnly{
  2645.             return;
  2646.         }
  2647.  
  2648.         // offset: 0; size: 2;
  2649.  
  2650.         // bit: 0, mask 0x01; 1 = scenarios are protected
  2651.         $bool (0x01 $this->_GetInt2d($recordData0)) >> 0;
  2652.  
  2653.         $this->_phpSheet->getProtection()->setScenarios((bool)$bool);
  2654.     }
  2655.  
  2656.     /**
  2657.      * OBJECTPROTECT
  2658.      */
  2659.     private function _readObjectProtect()
  2660.     {
  2661.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2662.         $recordData substr($this->_data$this->_pos 4$length);
  2663.  
  2664.         // move stream pointer to next record
  2665.         $this->_pos += $length;
  2666.  
  2667.         if ($this->_readDataOnly{
  2668.             return;
  2669.         }
  2670.  
  2671.         // offset: 0; size: 2;
  2672.  
  2673.         // bit: 0, mask 0x01; 1 = objects are protected
  2674.         $bool (0x01 $this->_GetInt2d($recordData0)) >> 0;
  2675.  
  2676.         $this->_phpSheet->getProtection()->setObjects((bool)$bool);
  2677.     }
  2678.  
  2679.     /**
  2680.      * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8)
  2681.      */
  2682.     private function _readPassword()
  2683.     {
  2684.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2685.         $recordData substr($this->_data$this->_pos 4$length);
  2686.  
  2687.         // move stream pointer to next record
  2688.         $this->_pos += $length;
  2689.  
  2690.         if (!$this->_readDataOnly{
  2691.             // offset: 0; size: 2; 16-bit hash value of password
  2692.             $password strtoupper(dechex($this->_GetInt2d($recordData0)))// the hashed password
  2693.             $this->_phpSheet->getProtection()->setPassword($passwordtrue);
  2694.         }
  2695.     }
  2696.  
  2697.     /**
  2698.      * Read DEFCOLWIDTH record
  2699.      */
  2700.     private function _readDefColWidth()
  2701.     {
  2702.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2703.         $recordData substr($this->_data$this->_pos 4$length);
  2704.  
  2705.         // move stream pointer to next record
  2706.         $this->_pos += $length;
  2707.  
  2708.         // offset: 0; size: 2; default column width
  2709.         $width $this->_GetInt2d($recordData0);
  2710.         if ($width != 8{
  2711.             $this->_phpSheet->getDefaultColumnDimension()->setWidth($width);
  2712.         }
  2713.     }
  2714.  
  2715.     /**
  2716.      * Read COLINFO record
  2717.      */
  2718.     private function _readColInfo()
  2719.     {
  2720.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2721.         $recordData substr($this->_data$this->_pos 4$length);
  2722.  
  2723.         // move stream pointer to next record
  2724.         $this->_pos += $length;
  2725.  
  2726.         if (!$this->_readDataOnly{
  2727.             // offset: 0; size: 2; index to first column in range
  2728.             $fc $this->_GetInt2d($recordData0)// first column index
  2729.  
  2730.             // offset: 2; size: 2; index to last column in range
  2731.             $lc $this->_GetInt2d($recordData2)// first column index
  2732.  
  2733.             // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
  2734.             $width $this->_GetInt2d($recordData4);
  2735.  
  2736.             // offset: 6; size: 2; index to XF record for default column formatting
  2737.             $xfIndex $this->_GetInt2d($recordData6);
  2738.  
  2739.             // offset: 8; size: 2; option flags
  2740.  
  2741.                 // bit: 0; mask: 0x0001; 1= columns are hidden
  2742.                 $isHidden (0x0001 $this->_GetInt2d($recordData8)) >> 0;
  2743.  
  2744.                 // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
  2745.                 $level (0x0700 $this->_GetInt2d($recordData8)) >> 8;
  2746.  
  2747.                 // bit: 12; mask: 0x1000; 1 = collapsed
  2748.                 $isCollapsed (0x1000 $this->_GetInt2d($recordData8)) >> 12;
  2749.  
  2750.             // offset: 10; size: 2; not used
  2751.  
  2752.             for ($i $fc$i <= $lc++$i{
  2753.                 if ($lc == 255 || $lc == 256{
  2754.                     $this->_phpSheet->getDefaultColumnDimension()->setWidth($width 256);
  2755.                     break;
  2756.                 }
  2757.                 $this->_phpSheet->getColumnDimensionByColumn($i)->setWidth($width 256);
  2758.                 $this->_phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
  2759.                 $this->_phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
  2760.                 $this->_phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
  2761.                 $this->_phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
  2762.             }
  2763.         }
  2764.     }
  2765.  
  2766.     /**
  2767.      * ROW
  2768.      *
  2769.      * This record contains the properties of a single row in a
  2770.      * sheet. Rows and cells in a sheet are divided into blocks
  2771.      * of 32 rows.
  2772.      *
  2773.      * --    "OpenOffice.org's Documentation of the Microsoft
  2774.      *         Excel File Format"
  2775.      */
  2776.     private function _readRow()
  2777.     {
  2778.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2779.         $recordData substr($this->_data$this->_pos 4$length);
  2780.  
  2781.         // move stream pointer to next record
  2782.         $this->_pos += $length;
  2783.  
  2784.         if (!$this->_readDataOnly{
  2785.             // offset: 0; size: 2; index of this row
  2786.             $r $this->_GetInt2d($recordData0);
  2787.  
  2788.             // offset: 2; size: 2; index to column of the first cell which is described by a cell record
  2789.  
  2790.             // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
  2791.  
  2792.             // offset: 6; size: 2;
  2793.  
  2794.                 // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point
  2795.                 $height (0x7FFF $this->_GetInt2d($recordData6)) >> 0;
  2796.  
  2797.                 // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
  2798.                 $useDefaultHeight (0x8000 $this->_GetInt2d($recordData6)) >> 15;
  2799.  
  2800.                 if (!$useDefaultHeight{
  2801.                     $this->_phpSheet->getRowDimension($r 1)->setRowHeight($height 20);
  2802.                 }
  2803.  
  2804.             // offset: 8; size: 2; not used
  2805.  
  2806.             // offset: 10; size: 2; not used in BIFF5-BIFF8
  2807.  
  2808.             // offset: 12; size: 4; option flags and default row formatting
  2809.  
  2810.                 // bit: 2-0: mask: 0x00000007; outline level of the row
  2811.                 $level (0x00000007 $this->_GetInt4d($recordData12)) >> 0;
  2812.                 $this->_phpSheet->getRowDimension($r 1)->setOutlineLevel($level);
  2813.  
  2814.                 // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
  2815.                 $isCollapsed (0x00000010 $this->_GetInt4d($recordData12)) >> 4;
  2816.                 $this->_phpSheet->getRowDimension($r 1)->setCollapsed($isCollapsed);
  2817.  
  2818.                 // bit: 5; mask: 0x00000020; 1 = row is hidden
  2819.                 $isHidden (0x00000020 $this->_GetInt4d($recordData12)) >> 5;
  2820.                 $this->_phpSheet->getRowDimension($r 1)->setVisible(!$isHidden);
  2821.  
  2822.                 // bit: 7; mask: 0x00000080; 1 = row has explicit format
  2823.                 $hasExplicitFormat (0x00000080 $this->_GetInt4d($recordData12)) >> 7;
  2824.  
  2825.                 // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record
  2826.                 $xfIndex (0x0FFF0000 $this->_GetInt4d($recordData12)) >> 16;
  2827.  
  2828.                 if ($hasExplicitFormat{
  2829.                     $this->_phpSheet->getRowDimension($r 1)->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
  2830.                 }
  2831.         }
  2832.     }
  2833.  
  2834.     /**
  2835.      * Read RK record
  2836.      * This record represents a cell that contains an RK value
  2837.      * (encoded integer or floating-point value). If a
  2838.      * floating-point value cannot be encoded to an RK value,
  2839.      * a NUMBER record will be written. This record replaces the
  2840.      * record INTEGER written in BIFF2.
  2841.      *
  2842.      * --    "OpenOffice.org's Documentation of the Microsoft
  2843.      *         Excel File Format"
  2844.      */
  2845.     private function _readRk()
  2846.     {
  2847.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2848.         $recordData substr($this->_data$this->_pos 4$length);
  2849.  
  2850.         // move stream pointer to next record
  2851.         $this->_pos += $length;
  2852.  
  2853.         // offset: 0; size: 2; index to row
  2854.         $row $this->_GetInt2d($recordData0);
  2855.  
  2856.         // offset: 2; size: 2; index to column
  2857.         $column $this->_GetInt2d($recordData2);
  2858.         $columnString PHPExcel_Cell::stringFromColumnIndex($column);
  2859.  
  2860.         // Read cell?
  2861.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_phpSheet->getTitle()) ) {
  2862.             // offset: 4; size: 2; index to XF record
  2863.             $xfIndex $this->_GetInt2d($recordData4);
  2864.  
  2865.             // offset: 6; size: 4; RK value
  2866.             $rknum $this->_GetInt4d($recordData6);
  2867.             $numValue $this->_GetIEEE754($rknum);
  2868.  
  2869.             $cell $this->_phpSheet->getCell($columnString ($row 1));
  2870.             if (!$this->_readDataOnly{
  2871.                 // add style information
  2872.                 $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
  2873.             }
  2874.  
  2875.             // add cell
  2876.             $cell->setValueExplicit($numValuePHPExcel_Cell_DataType::TYPE_NUMERIC);
  2877.         }
  2878.     }
  2879.  
  2880.     /**
  2881.      * Read LABELSST record
  2882.      * This record represents a cell that contains a string. It
  2883.      * replaces the LABEL record and RSTRING record used in
  2884.      * BIFF2-BIFF5.
  2885.      *
  2886.      * --    "OpenOffice.org's Documentation of the Microsoft
  2887.      *         Excel File Format"
  2888.      */
  2889.     private function _readLabelSst()
  2890.     {
  2891.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2892.         $recordData substr($this->_data$this->_pos 4$length);
  2893.  
  2894.         // move stream pointer to next record
  2895.         $this->_pos += $length;
  2896.  
  2897.         // offset: 0; size: 2; index to row
  2898.         $row $this->_GetInt2d($recordData0);
  2899.  
  2900.         // offset: 2; size: 2; index to column
  2901.         $column $this->_GetInt2d($recordData2);
  2902.         $columnString PHPExcel_Cell::stringFromColumnIndex($column);
  2903.  
  2904.         // Read cell?
  2905.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_phpSheet->getTitle()) ) {
  2906.             // offset: 4; size: 2; index to XF record
  2907.             $xfIndex $this->_GetInt2d($recordData4);
  2908.  
  2909.             // offset: 6; size: 4; index to SST record
  2910.             $index $this->_GetInt4d($recordData6);
  2911.  
  2912.             // add cell
  2913.             if (($fmtRuns $this->_sst[$index]['fmtRuns']&& !$this->_readDataOnly{
  2914.                 // then we should treat as rich text
  2915.                 $richText new PHPExcel_RichText();
  2916.                 $charPos 0;
  2917.                 for ($i 0$i <= count($this->_sst[$index]['fmtRuns'])++$i{
  2918.                     if (isset($fmtRuns[$i])) {
  2919.                         $text PHPExcel_Shared_String::Substring($this->_sst[$index]['value']$charPos$fmtRuns[$i]['charPos'$charPos);
  2920.                         $charPos $fmtRuns[$i]['charPos'];
  2921.                     else {
  2922.                         $text PHPExcel_Shared_String::Substring($this->_sst[$index]['value']$charPosPHPExcel_Shared_String::CountCharacters($this->_sst[$index]['value']));
  2923.                     }
  2924.  
  2925.                     if (PHPExcel_Shared_String::CountCharacters($text0{
  2926.                         if ($i == 0// first text run, no style
  2927.                             $richText->createText($text);
  2928.                         else {
  2929.                             $textRun $richText->createTextRun($text);
  2930.                             if (isset($fmtRuns[$i 1])) {
  2931.                                 if ($fmtRuns[$i 1]['fontIndex'4{
  2932.                                     $fontIndex $fmtRuns[$i 1]['fontIndex'];
  2933.                                 else {
  2934.                                     // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
  2935.                                     // check the OpenOffice documentation of the FONT record
  2936.                                     $fontIndex $fmtRuns[$i 1]['fontIndex'1;
  2937.                                 }
  2938.                                 $textRun->setFont(clone $this->_objFonts[$fontIndex]);
  2939.                             }
  2940.                         }
  2941.                     }
  2942.                 }
  2943.                 $cell $this->_phpSheet->getCell($columnString ($row 1));
  2944.                 $cell->setValueExplicit($richTextPHPExcel_Cell_DataType::TYPE_STRING);
  2945.             else {
  2946.                 $cell $this->_phpSheet->getCell($columnString ($row 1));
  2947.                 $cell->setValueExplicit($this->_sst[$index]['value']PHPExcel_Cell_DataType::TYPE_STRING);
  2948.             }
  2949.  
  2950.             if (!$this->_readDataOnly{
  2951.                 // add style information
  2952.                 $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
  2953.             }
  2954.         }
  2955.     }
  2956.  
  2957.     /**
  2958.      * Read MULRK record
  2959.      * This record represents a cell range containing RK value
  2960.      * cells. All cells are located in the same row.
  2961.      *
  2962.      * --    "OpenOffice.org's Documentation of the Microsoft
  2963.      *         Excel File Format"
  2964.      */
  2965.     private function _readMulRk()
  2966.     {
  2967.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  2968.         $recordData substr($this->_data$this->_pos 4$length);
  2969.  
  2970.         // move stream pointer to next record
  2971.         $this->_pos += $length;
  2972.  
  2973.         // offset: 0; size: 2; index to row
  2974.         $row $this->_GetInt2d($recordData0);
  2975.  
  2976.         // offset: 2; size: 2; index to first column
  2977.         $colFirst $this->_GetInt2d($recordData2);
  2978.  
  2979.         // offset: var; size: 2; index to last column
  2980.         $colLast $this->_GetInt2d($recordData$length 2);
  2981.         $columns $colLast $colFirst 1;
  2982.  
  2983.         // offset within record data
  2984.         $offset 4;
  2985.  
  2986.         for ($i 0$i $columns++$i{
  2987.             $columnString PHPExcel_Cell::stringFromColumnIndex($colFirst $i);
  2988.  
  2989.             // Read cell?
  2990.             if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_phpSheet->getTitle()) ) {
  2991.  
  2992.                 // offset: var; size: 2; index to XF record
  2993.                 $xfIndex $this->_GetInt2d($recordData$offset);
  2994.  
  2995.                 // offset: var; size: 4; RK value
  2996.                 $numValue $this->_GetIEEE754($this->_GetInt4d($recordData$offset 2));
  2997.                 $cell $this->_phpSheet->getCell($columnString ($row 1));
  2998.                 if (!$this->_readDataOnly{
  2999.                     // add style
  3000.                     $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
  3001.                 }
  3002.  
  3003.                 // add cell value
  3004.                 $cell->setValueExplicit($numValuePHPExcel_Cell_DataType::TYPE_NUMERIC);
  3005.             }
  3006.  
  3007.             $offset += 6;
  3008.         }
  3009.     }
  3010.  
  3011.     /**
  3012.      * Read NUMBER record
  3013.      * This record represents a cell that contains a
  3014.      * floating-point value.
  3015.      *
  3016.      * --    "OpenOffice.org's Documentation of the Microsoft
  3017.      *         Excel File Format"
  3018.      */
  3019.     private function _readNumber()
  3020.     {
  3021.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3022.         $recordData substr($this->_data$this->_pos 4$length);
  3023.  
  3024.         // move stream pointer to next record
  3025.         $this->_pos += $length;
  3026.  
  3027.         // offset: 0; size: 2; index to row
  3028.         $row $this->_GetInt2d($recordData0);
  3029.  
  3030.         // offset: 2; size 2; index to column
  3031.         $column $this->_GetInt2d($recordData2);
  3032.         $columnString PHPExcel_Cell::stringFromColumnIndex($column);
  3033.  
  3034.         // Read cell?
  3035.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_phpSheet->getTitle()) ) {
  3036.             // offset 4; size: 2; index to XF record
  3037.             $xfIndex $this->_GetInt2d($recordData4);
  3038.  
  3039.             $numValue $this->_extractNumber(substr($recordData68));
  3040.  
  3041.             $cell $this->_phpSheet->getCell($columnString ($row 1));
  3042.             if (!$this->_readDataOnly{
  3043.                 // add cell style
  3044.                 $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
  3045.             }
  3046.  
  3047.             // add cell value
  3048.             $cell->setValueExplicit($numValuePHPExcel_Cell_DataType::TYPE_NUMERIC);
  3049.         }
  3050.     }
  3051.  
  3052.     /**
  3053.      * Read FORMULA record + perhaps a following STRING record if formula result is a string
  3054.      * This record contains the token array and the result of a
  3055.      * formula cell.
  3056.      *
  3057.      * --    "OpenOffice.org's Documentation of the Microsoft
  3058.      *         Excel File Format"
  3059.      */
  3060.     private function _readFormula()
  3061.     {
  3062.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3063.         $recordData substr($this->_data$this->_pos 4$length);
  3064.  
  3065.         // move stream pointer to next record
  3066.         $this->_pos += $length;
  3067.  
  3068.         // offset: 0; size: 2; row index
  3069.         $row $this->_GetInt2d($recordData0);
  3070.  
  3071.         // offset: 2; size: 2; col index
  3072.         $column $this->_GetInt2d($recordData2);
  3073.         $columnString PHPExcel_Cell::stringFromColumnIndex($column);
  3074.  
  3075.         // offset: 20: size: variable; formula structure
  3076.         $formulaStructure substr($recordData20);
  3077.  
  3078.         // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
  3079.         $options $this->_GetInt2d($recordData14);
  3080.  
  3081.         // bit: 0; mask: 0x0001; 1 = recalculate always
  3082.         // bit: 1; mask: 0x0002; 1 = calculate on open
  3083.         // bit: 2; mask: 0x0008; 1 = part of a shared formula
  3084.         $isPartOfSharedFormula = (bool) (0x0008 $options);
  3085.  
  3086.         // WARNING:
  3087.         // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
  3088.         // the formula data may be ordinary formula data, therefore we need to check
  3089.         // explicitly for the tExp token (0x01)
  3090.         $isPartOfSharedFormula $isPartOfSharedFormula && ord($formulaStructure{2}== 0x01;
  3091.  
  3092.         if ($isPartOfSharedFormula{
  3093.             // part of shared formula which means there will be a formula with a tExp token and nothing else
  3094.             // get the base cell, grab tExp token
  3095.             $baseRow $this->_GetInt2d($formulaStructure3);
  3096.             $baseCol $this->_GetInt2d($formulaStructure5);
  3097.             $this->_baseCell PHPExcel_Cell::stringFromColumnIndex($baseCol)($baseRow 1);
  3098.         }
  3099.  
  3100.         // Read cell?
  3101.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_phpSheet->getTitle()) ) {
  3102.  
  3103.             if ($isPartOfSharedFormula{
  3104.                 // formula is added to this cell after the sheet has been read
  3105.                 $this->_sharedFormulaParts[$columnString ($row 1)$this->_baseCell;
  3106.             }
  3107.  
  3108.             // offset: 16: size: 4; not used
  3109.  
  3110.             // offset: 4; size: 2; XF index
  3111.             $xfIndex $this->_GetInt2d($recordData4);
  3112.  
  3113.             // offset: 6; size: 8; result of the formula
  3114.             if ( (ord($recordData{6}== 0)
  3115.                 && (ord($recordData{12}== 255)
  3116.                 && (ord($recordData{13}== 255) ) {
  3117.  
  3118.                 // String formula. Result follows in appended STRING record
  3119.                 $dataType PHPExcel_Cell_DataType::TYPE_STRING;
  3120.  
  3121.                 // read possible SHAREDFMLA record
  3122.                 $code $this->_GetInt2d($this->_data$this->_pos);
  3123.                 if ($code == self::XLS_Type_SHAREDFMLA{
  3124.                     $this->_readSharedFmla();
  3125.                 }
  3126.  
  3127.                 // read STRING record
  3128.                 $value $this->_readString();
  3129.  
  3130.             elseif ((ord($recordData{6}== 1)
  3131.                 && (ord($recordData{12}== 255)
  3132.                 && (ord($recordData{13}== 255)) {
  3133.  
  3134.                 // Boolean formula. Result is in +2; 0=false, 1=true
  3135.                 $dataType PHPExcel_Cell_DataType::TYPE_BOOL;
  3136.                 $value = (bool) ord($recordData{8});
  3137.  
  3138.             elseif ((ord($recordData{6}== 2)
  3139.                 && (ord($recordData{12}== 255)
  3140.                 && (ord($recordData{13}== 255)) {
  3141.  
  3142.                 // Error formula. Error code is in +2
  3143.                 $dataType PHPExcel_Cell_DataType::TYPE_ERROR;
  3144.                 $value $this->_mapErrorCode(ord($recordData{8}));
  3145.  
  3146.             elseif ((ord($recordData{6}== 3)
  3147.                 && (ord($recordData{12}== 255)
  3148.                 && (ord($recordData{13}== 255)) {
  3149.  
  3150.                 // Formula result is a null string
  3151.                 $dataType PHPExcel_Cell_DataType::TYPE_NULL;
  3152.                 $value '';
  3153.  
  3154.             else {
  3155.  
  3156.                 // forumla result is a number, first 14 bytes like _NUMBER record
  3157.                 $dataType PHPExcel_Cell_DataType::TYPE_NUMERIC;
  3158.                 $value $this->_extractNumber(substr($recordData68));
  3159.  
  3160.             }
  3161.  
  3162.             $cell $this->_phpSheet->getCell($columnString ($row 1));
  3163.             if (!$this->_readDataOnly{
  3164.                 // add cell style
  3165.                 $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
  3166.             }
  3167.  
  3168.             // store the formula
  3169.             if (!$isPartOfSharedFormula{
  3170.                 // not part of shared formula
  3171.                 // add cell value. If we can read formula, populate with formula, otherwise just used cached value
  3172.                 try {
  3173.                     if ($this->_version != self::XLS_BIFF8{
  3174.                         throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
  3175.                     }
  3176.                     $formula $this->_getFormulaFromStructure($formulaStructure)// get formula in human language
  3177.                     $cell->setValueExplicit('=' $formulaPHPExcel_Cell_DataType::TYPE_FORMULA);
  3178.  
  3179.                 catch (Exception $e{
  3180.                     $cell->setValueExplicit($value$dataType);
  3181.                 }
  3182.             else {
  3183.                 if ($this->_version == self::XLS_BIFF8{
  3184.                     // do nothing at this point, formula id added later in the code
  3185.                 else {
  3186.                     $cell->setValueExplicit($value$dataType);
  3187.                 }
  3188.             }
  3189.  
  3190.             // store the cached calculated value
  3191.             $cell->setCalculatedValue($value);
  3192.         }
  3193.     }
  3194.  
  3195.     /**
  3196.      * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader,
  3197.      * which usually contains relative references.
  3198.      * These will be used to construct the formula in each shared formula part after the sheet is read.
  3199.      */
  3200.     private function _readSharedFmla()
  3201.     {
  3202.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3203.         $recordData substr($this->_data$this->_pos 4$length);
  3204.  
  3205.         // move stream pointer to next record
  3206.         $this->_pos += $length;
  3207.  
  3208.         // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
  3209.         $cellRange substr($recordData06);
  3210.         $cellRange $this->_readBIFF5CellRangeAddressFixed($cellRange)// note: even BIFF8 uses BIFF5 syntax
  3211.  
  3212.         // offset: 6, size: 1; not used
  3213.  
  3214.         // offset: 7, size: 1; number of existing FORMULA records for this shared formula
  3215.         $no ord($recordData{7});
  3216.  
  3217.         // offset: 8, size: var; Binary token array of the shared formula
  3218.         $formula substr($recordData8);
  3219.  
  3220.         // at this point we only store the shared formula for later use
  3221.         $this->_sharedFormulas[$this->_baseCell$formula;
  3222.  
  3223.     }
  3224.  
  3225.     /**
  3226.      * Read a STRING record from current stream position and advance the stream pointer to next record
  3227.      * This record is used for storing result from FORMULA record when it is a string, and
  3228.      * it occurs directly after the FORMULA record
  3229.      *
  3230.      * @return string The string contents as UTF-8
  3231.      */
  3232.     private function _readString()
  3233.     {
  3234.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3235.         $recordData substr($this->_data$this->_pos 4$length);
  3236.  
  3237.         // move stream pointer to next record
  3238.         $this->_pos += $length;
  3239.  
  3240.         if ($this->_version == self::XLS_BIFF8{
  3241.             $string $this->_readUnicodeStringLong($recordData);
  3242.             $value $string['value'];
  3243.         else {
  3244.             $string $this->_readByteStringLong($recordData);
  3245.             $value $string['value'];
  3246.         }
  3247.  
  3248.         return $value;
  3249.     }
  3250.  
  3251.     /**
  3252.      * Read BOOLERR record
  3253.      * This record represents a Boolean value or error value
  3254.      * cell.
  3255.      *
  3256.      * --    "OpenOffice.org's Documentation of the Microsoft
  3257.      *         Excel File Format"
  3258.      */
  3259.     private function _readBoolErr()
  3260.     {
  3261.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3262.         $recordData substr($this->_data$this->_pos 4$length);
  3263.  
  3264.         // move stream pointer to next record
  3265.         $this->_pos += $length;
  3266.  
  3267.         // offset: 0; size: 2; row index
  3268.         $row $this->_GetInt2d($recordData0);
  3269.  
  3270.         // offset: 2; size: 2; column index
  3271.         $column $this->_GetInt2d($recordData2);
  3272.         $columnString PHPExcel_Cell::stringFromColumnIndex($column);
  3273.  
  3274.         // Read cell?
  3275.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_phpSheet->getTitle()) ) {
  3276.             // offset: 4; size: 2; index to XF record
  3277.             $xfIndex $this->_GetInt2d($recordData4);
  3278.  
  3279.             // offset: 6; size: 1; the boolean value or error value
  3280.             $boolErr ord($recordData{6});
  3281.  
  3282.             // offset: 7; size: 1; 0=boolean; 1=error
  3283.             $isError ord($recordData{7});
  3284.  
  3285.             $cell $this->_phpSheet->getCell($columnString ($row 1));
  3286.             switch ($isError{
  3287.                 case 0// boolean
  3288.                     $value = (bool) $boolErr;
  3289.  
  3290.                     // add cell value
  3291.                     $cell->setValueExplicit($valuePHPExcel_Cell_DataType::TYPE_BOOL);
  3292.                     break;
  3293.  
  3294.                 case 1// error type
  3295.                     $value $this->_mapErrorCode($boolErr);
  3296.  
  3297.                     // add cell value
  3298.                     $cell->setValueExplicit($valuePHPExcel_Cell_DataType::TYPE_ERROR);
  3299.                     break;
  3300.             }
  3301.  
  3302.             if (!$this->_readDataOnly{
  3303.                 // add cell style
  3304.                 $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
  3305.             }
  3306.         }
  3307.     }
  3308.  
  3309.     /**
  3310.      * Read MULBLANK record
  3311.      * This record represents a cell range of empty cells. All
  3312.      * cells are located in the same row
  3313.      *
  3314.      * --    "OpenOffice.org's Documentation of the Microsoft
  3315.      *         Excel File Format"
  3316.      */
  3317.     private function _readMulBlank()
  3318.     {
  3319.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3320.         $recordData substr($this->_data$this->_pos 4$length);
  3321.  
  3322.         // move stream pointer to next record
  3323.         $this->_pos += $length;
  3324.  
  3325.         // offset: 0; size: 2; index to row
  3326.         $row $this->_GetInt2d($recordData0);
  3327.  
  3328.         // offset: 2; size: 2; index to first column
  3329.         $fc $this->_GetInt2d($recordData2);
  3330.  
  3331.         // offset: 4; size: 2 x nc; list of indexes to XF records
  3332.         // add style information
  3333.         if (!$this->_readDataOnly{
  3334.             for ($i 0$i $length 3++$i{
  3335.                 $columnString PHPExcel_Cell::stringFromColumnIndex($fc $i);
  3336.  
  3337.                 // Read cell?
  3338.                 if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_phpSheet->getTitle()) ) {
  3339.                     $xfIndex $this->_GetInt2d($recordData$i);
  3340.                     $this->_phpSheet->getCell($columnString ($row 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
  3341.                 }
  3342.             }
  3343.         }
  3344.  
  3345.         // offset: 6; size 2; index to last column (not needed)
  3346.     }
  3347.  
  3348.     /**
  3349.      * Read LABEL record
  3350.      * This record represents a cell that contains a string. In
  3351.      * BIFF8 it is usually replaced by the LABELSST record.
  3352.      * Excel still uses this record, if it copies unformatted
  3353.      * text cells to the clipboard.
  3354.      *
  3355.      * --    "OpenOffice.org's Documentation of the Microsoft
  3356.      *         Excel File Format"
  3357.      */
  3358.     private function _readLabel()
  3359.     {
  3360.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3361.         $recordData substr($this->_data$this->_pos 4$length);
  3362.  
  3363.         // move stream pointer to next record
  3364.         $this->_pos += $length;
  3365.  
  3366.         // offset: 0; size: 2; index to row
  3367.         $row $this->_GetInt2d($recordData0);
  3368.  
  3369.         // offset: 2; size: 2; index to column
  3370.         $column $this->_GetInt2d($recordData2);
  3371.         $columnString PHPExcel_Cell::stringFromColumnIndex($column);
  3372.  
  3373.         // Read cell?
  3374.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_phpSheet->getTitle()) ) {
  3375.             // offset: 4; size: 2; XF index
  3376.             $xfIndex $this->_GetInt2d($recordData4);
  3377.  
  3378.             // add cell value
  3379.             // todo: what if string is very long? continue record
  3380.             if ($this->_version == self::XLS_BIFF8{
  3381.                 $string $this->_readUnicodeStringLong(substr($recordData6));
  3382.                 $value $string['value'];
  3383.             else {
  3384.                 $string $this->_readByteStringLong(substr($recordData6));
  3385.                 $value $string['value'];
  3386.             }
  3387.             $cell $this->_phpSheet->getCell($columnString ($row 1));
  3388.             $cell->setValueExplicit($valuePHPExcel_Cell_DataType::TYPE_STRING);
  3389.  
  3390.             if (!$this->_readDataOnly{
  3391.                 // add cell style
  3392.                 $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
  3393.             }
  3394.         }
  3395.     }
  3396.  
  3397.     /**
  3398.      * Read BLANK record
  3399.      */
  3400.     private function _readBlank()
  3401.     {
  3402.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3403.         $recordData substr($this->_data$this->_pos 4$length);
  3404.  
  3405.         // move stream pointer to next record
  3406.         $this->_pos += $length;
  3407.  
  3408.         // offset: 0; size: 2; row index
  3409.         $row $this->_GetInt2d($recordData0);
  3410.  
  3411.         // offset: 2; size: 2; col index
  3412.         $col $this->_GetInt2d($recordData2);
  3413.         $columnString PHPExcel_Cell::stringFromColumnIndex($col);
  3414.  
  3415.         // Read cell?
  3416.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_phpSheet->getTitle()) ) {
  3417.             // offset: 4; size: 2; XF index
  3418.             $xfIndex $this->_GetInt2d($recordData4);
  3419.  
  3420.             // add style information
  3421.             if (!$this->_readDataOnly{
  3422.                 $this->_phpSheet->getCell($columnString ($row 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
  3423.             }
  3424.         }
  3425.  
  3426.     }
  3427.  
  3428.     /**
  3429.      * Read MSODRAWING record
  3430.      */
  3431.     private function _readMsoDrawing()
  3432.     {
  3433.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3434.  
  3435.         // get spliced record data
  3436.         $splicedRecordData $this->_getSplicedRecordData();
  3437.         $recordData $splicedRecordData['recordData'];
  3438.  
  3439.         $this->_drawingData .= $recordData;
  3440.     }
  3441.  
  3442.     /**
  3443.      * Read OBJ record
  3444.      */
  3445.     private function _readObj()
  3446.     {
  3447.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3448.         $recordData substr($this->_data$this->_pos 4$length);
  3449.  
  3450.         // move stream pointer to next record
  3451.         $this->_pos += $length;
  3452.  
  3453.         if ($this->_readDataOnly || $this->_version != self::XLS_BIFF8{
  3454.             return;
  3455.         }
  3456.  
  3457.         // recordData consists of an array of subrecords looking like this:
  3458.         //    ft: 2 bytes; id number
  3459.         //    cb: 2 bytes; size in bytes of following data
  3460.         //    data: var; subrecord data
  3461.  
  3462.         // for now, we are just interested in the second subrecord containing the object type
  3463.         $ot $this->_GetInt2d($recordData4);
  3464.  
  3465.         $this->_objs[array(
  3466.             'type' => $ot,
  3467.         );
  3468.     }
  3469.  
  3470.     /**
  3471.      * Read WINDOW2 record
  3472.      */
  3473.     private function _readWindow2()
  3474.     {
  3475.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3476.         $recordData substr($this->_data$this->_pos 4$length);
  3477.  
  3478.         // move stream pointer to next record
  3479.         $this->_pos += $length;
  3480.  
  3481.         // offset: 0; size: 2; option flags
  3482.         $options $this->_GetInt2d($recordData0);
  3483.  
  3484.         // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines
  3485.         $showGridlines = (bool) ((0x0002 $options>> 1);
  3486.         $this->_phpSheet->setShowGridlines($showGridlines);
  3487.  
  3488.         // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers
  3489.         $showRowColHeaders = (bool) ((0x0004 $options>> 2);
  3490.         $this->_phpSheet->setShowRowColHeaders($showRowColHeaders);
  3491.  
  3492.         // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen
  3493.         $this->_frozen = (bool) ((0x0008 $options>> 3);
  3494.  
  3495.         // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left
  3496.         $this->_phpSheet->setRightToLeft((bool)((0x0040 $options>> 6));
  3497.  
  3498.         // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active
  3499.         $isActive = (bool) ((0x0400 $options>> 10);
  3500.         if ($isActive{
  3501.             $this->_phpExcel->setActiveSheetIndex($this->_phpExcel->getIndex($this->_phpSheet));
  3502.         }
  3503.     }
  3504.  
  3505.     /**
  3506.      * Read SCL record
  3507.      */
  3508.     private function _readScl()
  3509.     {
  3510.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3511.         $recordData substr($this->_data$this->_pos 4$length);
  3512.  
  3513.         // move stream pointer to next record
  3514.         $this->_pos += $length;
  3515.  
  3516.         // offset: 0; size: 2; numerator of the view magnification
  3517.         $numerator $this->_GetInt2d($recordData0);
  3518.  
  3519.         // offset: 2; size: 2; numerator of the view magnification
  3520.         $denumerator $this->_GetInt2d($recordData2);
  3521.  
  3522.         // set the zoom scale (in percent)
  3523.         $this->_phpSheet->getSheetView()->setZoomScale($numerator 100 $denumerator);
  3524.     }
  3525.  
  3526.     /**
  3527.      * Read PANE record
  3528.      */
  3529.     private function _readPane()
  3530.     {
  3531.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3532.         $recordData substr($this->_data$this->_pos 4$length);
  3533.  
  3534.         // move stream pointer to next record
  3535.         $this->_pos += $length;
  3536.  
  3537.         if (!$this->_readDataOnly{
  3538.             // offset: 0; size: 2; position of vertical split
  3539.             $px $this->_GetInt2d($recordData0);
  3540.  
  3541.             // offset: 2; size: 2; position of horizontal split
  3542.             $py $this->_GetInt2d($recordData2);
  3543.  
  3544.             if ($this->_frozen{
  3545.                 // frozen panes
  3546.                 $this->_phpSheet->freezePane(PHPExcel_Cell::stringFromColumnIndex($px($py 1));
  3547.             else {
  3548.                 // unfrozen panes; split windows; not supported by PHPExcel core
  3549.             }
  3550.         }
  3551.     }
  3552.  
  3553.     /**
  3554.      * Read SELECTION record. There is one such record for each pane in the sheet.
  3555.      */
  3556.     private function _readSelection()
  3557.     {
  3558.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3559.         $recordData substr($this->_data$this->_pos 4$length);
  3560.  
  3561.         // move stream pointer to next record
  3562.         $this->_pos += $length;
  3563.  
  3564.         if (!$this->_readDataOnly{
  3565.             // offset: 0; size: 1; pane identifier
  3566.             $paneId ord($recordData{0});
  3567.  
  3568.             // offset: 1; size: 2; index to row of the active cell
  3569.             $r $this->_GetInt2d($recordData1);
  3570.  
  3571.             // offset: 3; size: 2; index to column of the active cell
  3572.             $c $this->_GetInt2d($recordData3);
  3573.  
  3574.             // offset: 5; size: 2; index into the following cell range list to the
  3575.             //  entry that contains the active cell
  3576.             $index $this->_GetInt2d($recordData5);
  3577.  
  3578.             // offset: 7; size: var; cell range address list containing all selected cell ranges
  3579.             $data substr($recordData7);
  3580.             $cellRangeAddressList $this->_readBIFF5CellRangeAddressList($data)// note: also BIFF8 uses BIFF5 syntax
  3581.  
  3582.             $selectedCells $cellRangeAddressList['cellRangeAddresses'][0];
  3583.  
  3584.             // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!)
  3585.             if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/'$selectedCells)) {
  3586.                 $selectedCells preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/''${1}1048576'$selectedCells);
  3587.             }
  3588.  
  3589.             // first row '1' + last row '65536' indicates that full column is selected
  3590.             if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/'$selectedCells)) {
  3591.                 $selectedCells preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/''${1}1048576'$selectedCells);
  3592.             }
  3593.  
  3594.             // first column 'A' + last column 'IV' indicates that full row is selected
  3595.             if (preg_match('/^(A[0-9]+\:)IV([0-9]+)$/'$selectedCells)) {
  3596.                 $selectedCells preg_replace('/^(A[0-9]+\:)IV([0-9]+)$/''${1}XFD${2}'$selectedCells);
  3597.             }
  3598.  
  3599.             $this->_phpSheet->setSelectedCells($selectedCells);
  3600.         }
  3601.     }
  3602.  
  3603.     /**
  3604.      * MERGEDCELLS
  3605.      *
  3606.      * This record contains the addresses of merged cell ranges
  3607.      * in the current sheet.
  3608.      *
  3609.      * --    "OpenOffice.org's Documentation of the Microsoft
  3610.      *         Excel File Format"
  3611.      */
  3612.     private function _readMergedCells()
  3613.     {
  3614.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3615.         $recordData substr($this->_data$this->_pos 4$length);
  3616.  
  3617.         // move stream pointer to next record
  3618.         $this->_pos += $length;
  3619.  
  3620.         if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly{
  3621.             $cellRangeAddressList $this->_readBIFF8CellRangeAddressList($recordData);
  3622.             foreach ($cellRangeAddressList['cellRangeAddresses'as $cellRangeAddress{
  3623.                 $this->_phpSheet->mergeCells($cellRangeAddress);
  3624.             }
  3625.         }
  3626.     }
  3627.  
  3628.     /**
  3629.      * Read HYPERLINK record
  3630.      */
  3631.     private function _readHyperLink()
  3632.     {
  3633.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3634.         $recordData substr($this->_data$this->_pos 4$length);
  3635.  
  3636.         // move stream pointer forward to next record
  3637.         $this->_pos += $length;
  3638.  
  3639.         if (!$this->_readDataOnly{
  3640.             // offset: 0; size: 8; cell range address of all cells containing this hyperlink
  3641.             try {
  3642.                 $cellRange $this->_readBIFF8CellRangeAddressFixed($recordData08);
  3643.             catch (Exception $e{
  3644.                 return;
  3645.             }
  3646.  
  3647.             // offset: 8, size: 16; GUID of StdLink
  3648.  
  3649.             // offset: 24, size: 4; unknown value
  3650.  
  3651.             // offset: 28, size: 4; option flags
  3652.  
  3653.                 // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
  3654.                 $isFileLinkOrUrl (0x00000001 $this->_GetInt2d($recordData28)) >> 0;
  3655.  
  3656.                 // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
  3657.                 $isAbsPathOrUrl (0x00000001 $this->_GetInt2d($recordData28)) >> 1;
  3658.  
  3659.                 // bit: 2 (and 4); mask: 0x00000014; 0 = no description
  3660.                 $hasDesc (0x00000014 $this->_GetInt2d($recordData28)) >> 2;
  3661.  
  3662.                 // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
  3663.                 $hasText (0x00000008 $this->_GetInt2d($recordData28)) >> 3;
  3664.  
  3665.                 // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
  3666.                 $hasFrame (0x00000080 $this->_GetInt2d($recordData28)) >> 7;
  3667.  
  3668.                 // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
  3669.                 $isUNC (0x00000100 $this->_GetInt2d($recordData28)) >> 8;
  3670.  
  3671.             // offset within record data
  3672.             $offset 32;
  3673.  
  3674.             if ($hasDesc{
  3675.                 // offset: 32; size: var; character count of description text
  3676.                 $dl $this->_GetInt4d($recordData32);
  3677.                 // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
  3678.                 $desc $this->_encodeUTF16(substr($recordData36($dl 1))false);
  3679.                 $offset += $dl;
  3680.             }
  3681.             if ($hasFrame{
  3682.                 $fl $this->_GetInt4d($recordData$offset);
  3683.                 $offset += $fl;
  3684.             }
  3685.  
  3686.             // detect type of hyperlink (there are 4 types)
  3687.             $hyperlinkType null;
  3688.  
  3689.             if ($isUNC{
  3690.                 $hyperlinkType 'UNC';
  3691.             else if (!$isFileLinkOrUrl{
  3692.                 $hyperlinkType 'workbook';
  3693.             else if (ord($recordData{$offset}== 0x03{
  3694.                 $hyperlinkType 'local';
  3695.             else if (ord($recordData{$offset}== 0xE0{
  3696.                 $hyperlinkType 'URL';
  3697.             }
  3698.  
  3699.             switch ($hyperlinkType{
  3700.             case 'URL':
  3701.                 // section 5.58.2: Hyperlink containing a URL
  3702.                 // e.g. http://example.org/index.php
  3703.  
  3704.                 // offset: var; size: 16; GUID of URL Moniker
  3705.                 $offset += 16;
  3706.                 // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
  3707.                 $us $this->_GetInt4d($recordData$offset);
  3708.                 $offset += 4;
  3709.                 // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
  3710.                 $url $this->_encodeUTF16(substr($recordData$offset$us 2)false);
  3711.                 $url .= $hasText '#' '';
  3712.                 $offset += $us;
  3713.                 break;
  3714.  
  3715.             case 'local':
  3716.                 // section 5.58.3: Hyperlink to local file
  3717.                 // examples:
  3718.                 //   mydoc.txt
  3719.                 //   ../../somedoc.xls#Sheet!A1
  3720.  
  3721.                 // offset: var; size: 16; GUI of File Moniker
  3722.                 $offset += 16;
  3723.  
  3724.                 // offset: var; size: 2; directory up-level count.
  3725.                 $upLevelCount $this->_GetInt2d($recordData$offset);
  3726.                 $offset += 2;
  3727.  
  3728.                 // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word
  3729.                 $sl $this->_GetInt4d($recordData$offset);
  3730.                 $offset += 4;
  3731.  
  3732.                 // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string)
  3733.                 $shortenedFilePath substr($recordData$offset$sl);
  3734.                 $shortenedFilePath $this->_encodeUTF16($shortenedFilePathtrue);
  3735.                 $shortenedFilePath substr($shortenedFilePath0-1)// remove trailing zero
  3736.  
  3737.                 $offset += $sl;
  3738.  
  3739.                 // offset: var; size: 24; unknown sequence
  3740.                 $offset += 24;
  3741.  
  3742.                 // extended file path
  3743.                 // offset: var; size: 4; size of the following file link field including string lenth mark
  3744.                 $sz $this->_GetInt4d($recordData$offset);
  3745.                 $offset += 4;
  3746.  
  3747.                 // only present if $sz > 0
  3748.                 if ($sz 0{
  3749.                     // offset: var; size: 4; size of the character array of the extended file path and name
  3750.                     $xl $this->_GetInt4d($recordData$offset);
  3751.                     $offset += 4;
  3752.  
  3753.                     // offset: var; size 2; unknown
  3754.                     $offset += 2;
  3755.  
  3756.                     // offset: var; size $xl; character array of the extended file path and name.
  3757.                     $extendedFilePath substr($recordData$offset$xl);
  3758.                     $extendedFilePath $this->_encodeUTF16($extendedFilePathfalse);
  3759.                     $offset += $xl;
  3760.                 }
  3761.  
  3762.                 // construct the path
  3763.                 $url str_repeat('..\\'$upLevelCount);
  3764.                 $url .= ($sz 0?
  3765.                     $extendedFilePath $shortenedFilePath// use extended path if available
  3766.                 $url .= $hasText '#' '';
  3767.  
  3768.                 break;
  3769.  
  3770.  
  3771.             case 'UNC':
  3772.                 // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
  3773.                 // todo: implement
  3774.                 return;
  3775.  
  3776.             case 'workbook':
  3777.                 // section 5.58.5: Hyperlink to the Current Workbook
  3778.                 // e.g. Sheet2!B1:C2, stored in text mark field
  3779.                 $url 'sheet://';
  3780.                 break;
  3781.  
  3782.             default:
  3783.                 return;
  3784.  
  3785.             }
  3786.  
  3787.             if ($hasText{
  3788.                 // offset: var; size: 4; character count of text mark including trailing zero word
  3789.                 $tl $this->_GetInt4d($recordData$offset);
  3790.                 $offset += 4;
  3791.                 // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
  3792.                 $text $this->_encodeUTF16(substr($recordData$offset($tl 1))false);
  3793.                 $url .= $text;
  3794.             }
  3795.  
  3796.             // apply the hyperlink to all the relevant cells
  3797.             foreach (PHPExcel_Cell::extractAllCellReferencesInRange($cellRangeas $coordinate{
  3798.                 $this->_phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url);
  3799.             }
  3800.         }
  3801.     }
  3802.  
  3803.     /**
  3804.      * Read DATAVALIDATIONS record
  3805.      */
  3806.     private function _readDataValidations()
  3807.     {
  3808.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3809.         $recordData substr($this->_data$this->_pos 4$length);
  3810.  
  3811.         // move stream pointer forward to next record
  3812.         $this->_pos += $length;
  3813.     }
  3814.  
  3815.     /**
  3816.      * Read DATAVALIDATION record
  3817.      */
  3818.     private function _readDataValidation()
  3819.     {
  3820.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3821.         $recordData substr($this->_data$this->_pos 4$length);
  3822.  
  3823.         // move stream pointer forward to next record
  3824.         $this->_pos += $length;
  3825.  
  3826.         if ($this->_readDataOnly{
  3827.             return;
  3828.         }
  3829.  
  3830.         // offset: 0; size: 4; Options
  3831.         $options $this->_GetInt4d($recordData0);
  3832.  
  3833.         // bit: 0-3; mask: 0x0000000F; type
  3834.         $type (0x0000000F $options>> 0;
  3835.         switch ($type{
  3836.             case 0x00:    $type PHPExcel_Cell_DataValidation::TYPE_NONE;        break;
  3837.             case 0x01:    $type PHPExcel_Cell_DataValidation::TYPE_WHOLE;        break;
  3838.             case 0x02:    $type PHPExcel_Cell_DataValidation::TYPE_DECIMAL;        break;
  3839.             case 0x03:    $type PHPExcel_Cell_DataValidation::TYPE_LIST;        break;
  3840.             case 0x04:    $type PHPExcel_Cell_DataValidation::TYPE_DATE;        break;
  3841.             case 0x05:    $type PHPExcel_Cell_DataValidation::TYPE_TIME;        break;
  3842.             case 0x06:    $type PHPExcel_Cell_DataValidation::TYPE_TEXTLENGTH;    break;
  3843.             case 0x07:    $type PHPExcel_Cell_DataValidation::TYPE_CUSTOM;        break;
  3844.         }
  3845.  
  3846.         // bit: 4-6; mask: 0x00000070; error type
  3847.         $errorStyle (0x00000070 $options>> 4;
  3848.         switch ($errorStyle{
  3849.             case 0x00:    $errorStyle PHPExcel_Cell_DataValidation::STYLE_STOP;            break;
  3850.             case 0x01:    $errorStyle PHPExcel_Cell_DataValidation::STYLE_WARNING;        break;
  3851.             case 0x02:    $errorStyle PHPExcel_Cell_DataValidation::STYLE_INFORMATION;    break;
  3852.         }
  3853.  
  3854.         // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
  3855.         // I have only seen cases where this is 1
  3856.         $explicitFormula (0x00000080 $options>> 7;
  3857.  
  3858.         // bit: 8; mask: 0x00000100; 1= empty cells allowed
  3859.         $allowBlank (0x00000100 $options>> 8;
  3860.  
  3861.         // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity
  3862.         $suppressDropDown (0x00000200 $options>> 9;
  3863.  
  3864.         // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected
  3865.         $showInputMessage (0x00040000 $options>> 18;
  3866.  
  3867.         // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered
  3868.         $showErrorMessage (0x00080000 $options>> 19;
  3869.  
  3870.         // bit: 20-23; mask: 0x00F00000; condition operator
  3871.         $operator (0x00F00000 $options>> 20;
  3872.         switch ($operator{
  3873.             case 0x00$operator PHPExcel_Cell_DataValidation::OPERATOR_BETWEEN            ;    break;
  3874.             case 0x01$operator PHPExcel_Cell_DataValidation::OPERATOR_NOTBETWEEN        ;    break;
  3875.             case 0x02$operator PHPExcel_Cell_DataValidation::OPERATOR_EQUAL                ;    break;
  3876.             case 0x03$operator PHPExcel_Cell_DataValidation::OPERATOR_NOTEQUAL            ;    break;
  3877.             case 0x04$operator PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHAN        ;    break;
  3878.             case 0x05$operator PHPExcel_Cell_DataValidation::OPERATOR_LESSTHAN            ;    break;
  3879.             case 0x06$operator PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHANOREQUAL;    break;
  3880.             case 0x07$operator PHPExcel_Cell_DataValidation::OPERATOR_LESSTHANOREQUAL    ;    break;
  3881.         }
  3882.  
  3883.         // offset: 4; size: var; title of the prompt box
  3884.         $offset 4;
  3885.         $string $this->_readUnicodeStringLong(substr($recordData$offset));
  3886.         $promptTitle $string['value'!== chr(0?
  3887.             $string['value''';
  3888.         $offset += $string['size'];
  3889.  
  3890.         // offset: var; size: var; title of the error box
  3891.         $string $this->_readUnicodeStringLong(substr($recordData$offset));
  3892.         $errorTitle $string['value'!== chr(0?
  3893.             $string['value''';
  3894.         $offset += $string['size'];
  3895.  
  3896.         // offset: var; size: var; text of the prompt box
  3897.         $string $this->_readUnicodeStringLong(substr($recordData$offset));
  3898.         $prompt $string['value'!== chr(0?
  3899.             $string['value''';
  3900.         $offset += $string['size'];
  3901.  
  3902.         // offset: var; size: var; text of the error box
  3903.         $string $this->_readUnicodeStringLong(substr($recordData$offset));
  3904.         $error $string['value'!== chr(0?
  3905.             $string['value''';
  3906.         $offset += $string['size'];
  3907.  
  3908.         // offset: var; size: 2; size of the formula data for the first condition
  3909.         $sz1 $this->_GetInt2d($recordData$offset);
  3910.         $offset += 2;
  3911.  
  3912.         // offset: var; size: 2; not used
  3913.         $offset += 2;
  3914.  
  3915.         // offset: var; size: $sz1; formula data for first condition (without size field)
  3916.         $formula1 substr($recordData$offset$sz1);
  3917.         $formula1 pack('v'$sz1$formula1// prepend the length
  3918.         try {
  3919.             $formula1 $this->_getFormulaFromStructure($formula1);
  3920.  
  3921.             // in list type validity, null characters are used as item separators
  3922.             if ($type == PHPExcel_Cell_DataValidation::TYPE_LIST{
  3923.                 $formula1 str_replace(chr(0)','$formula1);
  3924.             }
  3925.         catch (Exception $e{
  3926.             return;
  3927.         }
  3928.         $offset += $sz1;
  3929.  
  3930.         // offset: var; size: 2; size of the formula data for the first condition
  3931.         $sz2 $this->_GetInt2d($recordData$offset);
  3932.         $offset += 2;
  3933.  
  3934.         // offset: var; size: 2; not used
  3935.         $offset += 2;
  3936.  
  3937.         // offset: var; size: $sz2; formula data for second condition (without size field)
  3938.         $formula2 substr($recordData$offset$sz2);
  3939.         $formula2 pack('v'$sz2$formula2// prepend the length
  3940.         try {
  3941.             $formula2 $this->_getFormulaFromStructure($formula2);
  3942.         catch (Exception $e{
  3943.             return;
  3944.         }
  3945.         $offset += $sz2;
  3946.  
  3947.         // offset: var; size: var; cell range address list with
  3948.         $cellRangeAddressList $this->_readBIFF8CellRangeAddressList(substr($recordData$offset));
  3949.         $cellRangeAddresses $cellRangeAddressList['cellRangeAddresses'];
  3950.  
  3951.         foreach ($cellRangeAddresses as $cellRange{
  3952.             $stRange $this->_phpSheet->shrinkRangeToFit($cellRange);
  3953.             $stRange PHPExcel_Cell::extractAllCellReferencesInRange($stRange);
  3954.             foreach ($stRange as $coordinate{
  3955.                 $objValidation $this->_phpSheet->getCell($coordinate)->getDataValidation();
  3956.                 $objValidation->setType($type);
  3957.                 $objValidation->setErrorStyle($errorStyle);
  3958.                 $objValidation->setAllowBlank((bool)$allowBlank);
  3959.                 $objValidation->setShowInputMessage((bool)$showInputMessage);
  3960.                 $objValidation->setShowErrorMessage((bool)$showErrorMessage);
  3961.                 $objValidation->setShowDropDown(!$suppressDropDown);
  3962.                 $objValidation->setOperator($operator);
  3963.                 $objValidation->setErrorTitle($errorTitle);
  3964.                 $objValidation->setError($error);
  3965.                 $objValidation->setPromptTitle($promptTitle);
  3966.                 $objValidation->setPrompt($prompt);
  3967.                 $objValidation->setFormula1($formula1);
  3968.                 $objValidation->setFormula2($formula2);
  3969.             }
  3970.         }
  3971.  
  3972.     }
  3973.  
  3974.     /**
  3975.      * Read SHEETLAYOUT record. Stores sheet tab color information.
  3976.      */
  3977.     private function _readSheetLayout()
  3978.     {
  3979.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  3980.         $recordData substr($this->_data$this->_pos 4$length);
  3981.  
  3982.         // move stream pointer to next record
  3983.         $this->_pos += $length;
  3984.  
  3985.         // local pointer in record data
  3986.         $offset 0;
  3987.  
  3988.         if (!$this->_readDataOnly{
  3989.             // offset: 0; size: 2; repeated record identifier 0x0862
  3990.  
  3991.             // offset: 2; size: 10; not used
  3992.  
  3993.             // offset: 12; size: 4; size of record data
  3994.             // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?)
  3995.             $sz $this->_GetInt4d($recordData12);
  3996.  
  3997.             switch ($sz{
  3998.                 case 0x14:
  3999.                     // offset: 16; size: 2; color index for sheet tab
  4000.                     $colorIndex $this->_GetInt2d($recordData16);
  4001.                     $color $this->_readColor($colorIndex);
  4002.                     $this->_phpSheet->getTabColor()->setRGB($color['rgb']);
  4003.                     break;
  4004.  
  4005.                 case 0x28:
  4006.                     // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007
  4007.                     return;
  4008.                     break;
  4009.             }
  4010.         }
  4011.     }
  4012.  
  4013.     /**
  4014.      * Read SHEETPROTECTION record (FEATHEADR)
  4015.      */
  4016.     private function _readSheetProtection()
  4017.     {
  4018.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  4019.         $recordData substr($this->_data$this->_pos 4$length);
  4020.  
  4021.         // move stream pointer to next record
  4022.         $this->_pos += $length;
  4023.  
  4024.         if ($this->_readDataOnly{
  4025.             return;
  4026.         }
  4027.  
  4028.         // offset: 0; size: 2; repeated record header
  4029.  
  4030.         // offset: 2; size: 2; FRT cell reference flag (=0 currently)
  4031.  
  4032.         // offset: 4; size: 8; Currently not used and set to 0
  4033.  
  4034.         // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag)
  4035.         $isf $this->_GetInt2d($recordData12);
  4036.         if ($isf != 2{
  4037.             return;
  4038.         }
  4039.  
  4040.         // offset: 14; size: 1; =1 since this is a feat header
  4041.  
  4042.         // offset: 15; size: 4; size of rgbHdrSData
  4043.  
  4044.         // rgbHdrSData, assume "Enhanced Protection"
  4045.         // offset: 19; size: 2; option flags
  4046.         $options $this->_GetInt2d($recordData19);
  4047.  
  4048.         // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects
  4049.         $bool (0x0001 $options>> 0;
  4050.         $this->_phpSheet->getProtection()->setObjects(!$bool);
  4051.  
  4052.         // bit: 1; mask 0x0002; edit scenarios
  4053.         $bool (0x0002 $options>> 1;
  4054.         $this->_phpSheet->getProtection()->setScenarios(!$bool);
  4055.  
  4056.         // bit: 2; mask 0x0004; format cells
  4057.         $bool (0x0004 $options>> 2;
  4058.         $this->_phpSheet->getProtection()->setFormatCells(!$bool);
  4059.  
  4060.         // bit: 3; mask 0x0008; format columns
  4061.         $bool (0x0008 $options>> 3;
  4062.         $this->_phpSheet->getProtection()->setFormatColumns(!$bool);
  4063.  
  4064.         // bit: 4; mask 0x0010; format rows
  4065.         $bool (0x0010 $options>> 4;
  4066.         $this->_phpSheet->getProtection()->setFormatRows(!$bool);
  4067.  
  4068.         // bit: 5; mask 0x0020; insert columns
  4069.         $bool (0x0020 $options>> 5;
  4070.         $this->_phpSheet->getProtection()->setInsertColumns(!$bool);
  4071.  
  4072.         // bit: 6; mask 0x0040; insert rows
  4073.         $bool (0x0040 $options>> 6;
  4074.         $this->_phpSheet->getProtection()->setInsertRows(!$bool);
  4075.  
  4076.         // bit: 7; mask 0x0080; insert hyperlinks
  4077.         $bool (0x0080 $options>> 7;
  4078.         $this->_phpSheet->getProtection()->setInsertHyperlinks(!$bool);
  4079.  
  4080.         // bit: 8; mask 0x0100; delete columns
  4081.         $bool (0x0100 $options>> 8;
  4082.         $this->_phpSheet->getProtection()->setDeleteColumns(!$bool);
  4083.  
  4084.         // bit: 9; mask 0x0200; delete rows
  4085.         $bool (0x0200 $options>> 9;
  4086.         $this->_phpSheet->getProtection()->setDeleteRows(!$bool);
  4087.  
  4088.         // bit: 10; mask 0x0400; select locked cells
  4089.         $bool (0x0400 $options>> 10;
  4090.         $this->_phpSheet->getProtection()->setSelectLockedCells(!$bool);
  4091.  
  4092.         // bit: 11; mask 0x0800; sort cell range
  4093.         $bool (0x0800 $options>> 11;
  4094.         $this->_phpSheet->getProtection()->setSort(!$bool);
  4095.  
  4096.         // bit: 12; mask 0x1000; auto filter
  4097.         $bool (0x1000 $options>> 12;
  4098.         $this->_phpSheet->getProtection()->setAutoFilter(!$bool);
  4099.  
  4100.         // bit: 13; mask 0x2000; pivot tables
  4101.         $bool (0x2000 $options>> 13;
  4102.         $this->_phpSheet->getProtection()->setPivotTables(!$bool);
  4103.  
  4104.         // bit: 14; mask 0x4000; select unlocked cells
  4105.         $bool (0x4000 $options>> 14;
  4106.         $this->_phpSheet->getProtection()->setSelectUnlockedCells(!$bool);
  4107.  
  4108.         // offset: 21; size: 2; not used
  4109.     }
  4110.  
  4111.     /**
  4112.      * Read RANGEPROTECTION record
  4113.      * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification,
  4114.      * where it is referred to as FEAT record
  4115.      */
  4116.     private function _readRangeProtection()
  4117.     {
  4118.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  4119.         $recordData substr($this->_data$this->_pos 4$length);
  4120.  
  4121.         // move stream pointer to next record
  4122.         $this->_pos += $length;
  4123.  
  4124.         // local pointer in record data
  4125.         $offset 0;
  4126.  
  4127.         if (!$this->_readDataOnly{
  4128.             $offset += 12;
  4129.  
  4130.             // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag
  4131.             $isf $this->_GetInt2d($recordData12);
  4132.             if ($isf != 2{
  4133.                 // we only read FEAT records of type 2
  4134.                 return;
  4135.             }
  4136.             $offset += 2;
  4137.  
  4138.             $offset += 5;
  4139.  
  4140.             // offset: 19; size: 2; count of ref ranges this feature is on
  4141.             $cref $this->_GetInt2d($recordData19);
  4142.             $offset += 2;
  4143.  
  4144.             $offset += 6;
  4145.  
  4146.             // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record)
  4147.             $cellRanges array();
  4148.             for ($i 0$i $cref++$i{
  4149.                 try {
  4150.                     $cellRange $this->_readBIFF8CellRangeAddressFixed(substr($recordData27 $i8));
  4151.                 catch (Exception $e{
  4152.                     return;
  4153.                 }
  4154.                 $cellRanges[$cellRange;
  4155.                 $offset += 8;
  4156.             }
  4157.  
  4158.             // offset: var; size: var; variable length of feature specific data
  4159.             $rgbFeat substr($recordData$offset);
  4160.             $offset += 4;
  4161.  
  4162.             // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit)
  4163.             $wPassword $this->_GetInt4d($recordData$offset);
  4164.             $offset += 4;
  4165.  
  4166.             // Apply range protection to sheet
  4167.             if ($cellRanges{
  4168.                 $this->_phpSheet->protectCells(implode(' '$cellRanges)strtoupper(dechex($wPassword))true);
  4169.             }
  4170.         }
  4171.     }
  4172.  
  4173.     /**
  4174.      * Read IMDATA record
  4175.      */
  4176.     private function _readImData()
  4177.     {
  4178.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  4179.  
  4180.         // get spliced record data
  4181.         $splicedRecordData $this->_getSplicedRecordData();
  4182.         $recordData $splicedRecordData['recordData'];
  4183.  
  4184.         // UNDER CONSTRUCTION
  4185.  
  4186.         // offset: 0; size: 2; image format
  4187.         $cf $this->_GetInt2d($recordData0);
  4188.  
  4189.         // offset: 2; size: 2; environment from which the file was written
  4190.         $env $this->_GetInt2d($recordData2);
  4191.  
  4192.         // offset: 4; size: 4; length of the image data
  4193.         $lcb $this->_GetInt4d($recordData4);
  4194.  
  4195.         // offset: 8; size: var; image data
  4196.         $iData substr($recordData8);
  4197.  
  4198.         switch ($cf{
  4199.         case 0x09// Windows bitmap format
  4200.             // BITMAPCOREINFO
  4201.             // 1. BITMAPCOREHEADER
  4202.             // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure
  4203.             $bcSize $this->_GetInt4d($iData0);
  4204.             var_dump($bcSize);
  4205.  
  4206.             // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels
  4207.             $bcWidth $this->_GetInt2d($iData4);
  4208.             var_dump($bcWidth);
  4209.  
  4210.             // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels.
  4211.             $bcHeight $this->_GetInt2d($iData6);
  4212.             var_dump($bcHeight);
  4213.             $ih imagecreatetruecolor($bcWidth$bcHeight);
  4214.  
  4215.             // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1
  4216.  
  4217.             // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24
  4218.             $bcBitCount $this->_GetInt2d($iData10);
  4219.             var_dump($bcBitCount);
  4220.  
  4221.             $rgbString substr($iData12);
  4222.             $rgbTriples array();
  4223.             while (strlen($rgbString0{
  4224.                 $rgbTriples[unpack('Cb/Cg/Cr'$rgbString);
  4225.                 $rgbString substr($rgbString3);
  4226.             }
  4227.             $x 0;
  4228.             $y 0;
  4229.             foreach ($rgbTriples as $i => $rgbTriple{
  4230.                 $color imagecolorallocate($ih$rgbTriple['r']$rgbTriple['g']$rgbTriple['b']);
  4231.                 imagesetpixel($ih$x$bcHeight $y$color);
  4232.                 $x ($x 1$bcWidth;
  4233.                 $y $y floor(($x 1$bcWidth);
  4234.             }
  4235.             //imagepng($ih, 'image.png');
  4236.  
  4237.             $drawing new PHPExcel_Worksheet_Drawing();
  4238.             $drawing->setPath($filename);
  4239.             $drawing->setWorksheet($this->_phpSheet);
  4240.  
  4241.             break;
  4242.  
  4243.         case 0x02// Windows metafile or Macintosh PICT format
  4244.         case 0x0e// native format
  4245.         default;
  4246.             break;
  4247.  
  4248.         }
  4249.  
  4250.         // _getSplicedRecordData() takes care of moving current position in data stream
  4251.     }
  4252.  
  4253.     /**
  4254.      * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record
  4255.      * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented.
  4256.      * In this case, we must treat the CONTINUE record as a MSODRAWING record
  4257.      */
  4258.     private function _readContinue()
  4259.     {
  4260.         $length $this->_GetInt2d($this->_data$this->_pos 2);
  4261.         $recordData substr($this->_data$this->_pos 4$length);
  4262.  
  4263.         // check if we are reading drawing data
  4264.         // this is in case a free CONTINUE record occurs in other circumstances we are unaware of
  4265.         if ($this->_drawingData == ''{
  4266.             // move stream pointer to next record
  4267.             $this->_pos += $length;
  4268.  
  4269.             return;
  4270.         }
  4271.  
  4272.         // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data
  4273.         if ($length 4{
  4274.             // move stream pointer to next record
  4275.             $this->_pos += $length;
  4276.  
  4277.             return;
  4278.         }
  4279.  
  4280.         // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record
  4281.         // look inside CONTINUE record to see if it looks like a part of an Escher stream
  4282.         // we know that Escher stream may be split at least at
  4283.         //        0xF003 MsofbtSpgrContainer
  4284.         //        0xF004 MsofbtSpContainer
  4285.         //        0xF00D MsofbtClientTextbox
  4286.         $validSplitPoints array(0xF0030xF0040xF00D)// add identifiers if we find more
  4287.  
  4288.         $splitPoint $this->_GetInt2d($recordData2);
  4289.         if (in_array($splitPoint$validSplitPoints)) {
  4290.             // get spliced record data (and move pointer to next record)
  4291.             $splicedRecordData $this->_getSplicedRecordData();
  4292.             $this->_drawingData .= $splicedRecordData['recordData'];
  4293.  
  4294.             return;
  4295.         }
  4296.  
  4297.         // move stream pointer to next record
  4298.         $this->_pos += $length;
  4299.  
  4300.     }
  4301.  
  4302.  
  4303.     /**
  4304.      * Reads a record from current position in data stream and continues reading data as long as CONTINUE
  4305.      * records are found. Splices the record data pieces and returns the combined string as if record data
  4306.      * is in one piece.
  4307.      * Moves to next current position in data stream to start of next record different from a CONtINUE record
  4308.      *
  4309.      * @return array 
  4310.      */
  4311.     private function _getSplicedRecordData()
  4312.     {
  4313.         $data '';
  4314.         $spliceOffsets array();
  4315.  
  4316.         $i 0;
  4317.         $spliceOffsets[00;
  4318.  
  4319.         do {
  4320.             ++$i;
  4321.  
  4322.             // offset: 0; size: 2; identifier
  4323.             $identifier $this->_GetInt2d($this->_data$this->_pos);
  4324.             // offset: 2; size: 2; length
  4325.             $length $this->_GetInt2d($this->_data$this->_pos 2);
  4326.             $data .= substr($this->_data$this->_pos 4$length);
  4327.  
  4328.             $spliceOffsets[$i$spliceOffsets[$i 1$length;
  4329.  
  4330.             $this->_pos += $length;
  4331.             $nextIdentifier $this->_GetInt2d($this->_data$this->_pos);
  4332.         }
  4333.         while ($nextIdentifier == self::XLS_Type_CONTINUE);
  4334.  
  4335.         $splicedData array(
  4336.             'recordData' => $data,
  4337.             'spliceOffsets' => $spliceOffsets,
  4338.         );
  4339.  
  4340.         return $splicedData;
  4341.  
  4342.     }
  4343.  
  4344.     /**
  4345.      * Convert formula structure into human readable Excel formula like 'A3+A5*5'
  4346.      *
  4347.      * @param string $formulaStructure The complete binary data for the formula
  4348.      * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
  4349.      * @return string Human readable formula
  4350.      */
  4351.     private function _getFormulaFromStructure($formulaStructure$baseCell 'A1')
  4352.     {
  4353.         // offset: 0; size: 2; size of the following formula data
  4354.         $sz $this->_GetInt2d($formulaStructure0);
  4355.  
  4356.         // offset: 2; size: sz
  4357.         $formulaData substr($formulaStructure2$sz);
  4358.  
  4359.         // for debug: dump the formula data
  4360.         //echo '<xmp>';
  4361.         //echo 'size: ' . $sz . "\n";
  4362.         //echo 'the entire formula data: ';
  4363.         //Debug::dump($formulaData);
  4364.         //echo "\n----\n";
  4365.  
  4366.         // offset: 2 + sz; size: variable (optional)
  4367.         if (strlen($formulaStructure$sz{
  4368.             $additionalData substr($formulaStructure$sz);
  4369.  
  4370.             // for debug: dump the additional data
  4371.             //echo 'the entire additional data: ';
  4372.             //Debug::dump($additionalData);
  4373.             //echo "\n----\n";
  4374.  
  4375.         else {
  4376.             $additionalData '';
  4377.         }
  4378.  
  4379.         return $this->_getFormulaFromData($formulaData$additionalData$baseCell);
  4380.     }
  4381.  
  4382.     /**
  4383.      * Take formula data and additional data for formula and return human readable formula
  4384.      *
  4385.      * @param string $formulaData The binary data for the formula itself
  4386.      * @param string $additionalData Additional binary data going with the formula
  4387.      * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
  4388.      * @return string Human readable formula
  4389.      */
  4390.     private function _getFormulaFromData($formulaData,  $additionalData ''$baseCell 'A1')
  4391.     {
  4392.         // start parsing the formula data
  4393.         $tokens array();
  4394.  
  4395.         while (strlen($formulaDataand $token $this->_getNextToken($formulaData$baseCell)) {
  4396.             $tokens[$token;
  4397.             $formulaData substr($formulaData$token['size']);
  4398.  
  4399.             // for debug: dump the token
  4400.             //var_dump($token);
  4401.         }
  4402.  
  4403.         $formulaString $this->_createFormulaFromTokens($tokens$additionalData);
  4404.  
  4405.         return $formulaString;
  4406.     }
  4407.  
  4408.     /**
  4409.      * Take array of tokens together with additional data for formula and return human readable formula
  4410.      *
  4411.      * @param array $tokens 
  4412.      * @param array $additionalData Additional binary data going with the formula
  4413.      * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
  4414.      * @return string Human readable formula
  4415.      */
  4416.     private function _createFormulaFromTokens($tokens$additionalData)
  4417.     {
  4418.         // empty formula?
  4419.         if (count($tokens== 0{
  4420.             return '';
  4421.         }
  4422.  
  4423.         $formulaStrings array();
  4424.         foreach ($tokens as $token{
  4425.             // initialize spaces
  4426.             $space0 = isset($space0$space0 ''// spaces before next token, not tParen
  4427.             $space1 = isset($space1$space1 ''// carriage returns before next token, not tParen
  4428.             $space2 = isset($space2$space2 ''// spaces before opening parenthesis
  4429.             $space3 = isset($space3$space3 ''// carriage returns before opening parenthesis
  4430.             $space4 = isset($space4$space4 ''// spaces before closing parenthesis
  4431.             $space5 = isset($space5$space5 ''// carriage returns before closing parenthesis
  4432.  
  4433.             switch ($token['name']{
  4434.             case 'tAdd'// addition
  4435.             case 'tConcat'// addition
  4436.             case 'tDiv'// division
  4437.             case 'tEQ'// equaltiy
  4438.             case 'tGE'// greater than or equal
  4439.             case 'tGT'// greater than
  4440.             case 'tIsect'// intersection
  4441.             case 'tLE'// less than or equal
  4442.             case 'tList'// less than or equal
  4443.             case 'tLT'// less than
  4444.             case 'tMul'// multiplication
  4445.             case 'tNE'// multiplication
  4446.             case 'tPower'// power
  4447.             case 'tRange'// range
  4448.             case 'tSub'// subtraction
  4449.                 $op2 array_pop($formulaStrings);
  4450.                 $op1 array_pop($formulaStrings);
  4451.                 $formulaStrings["$op1$space1$space0{$token['data']}$op2";
  4452.                 unset($space0$space1);
  4453.                 break;
  4454.             case 'tUplus'// unary plus
  4455.             case 'tUminus'// unary minus
  4456.                 $op array_pop($formulaStrings);
  4457.                 $formulaStrings["$space1$space0{$token['data']}$op";
  4458.                 unset($space0$space1);
  4459.                 break;
  4460.             case 'tPercent'// percent sign
  4461.                 $op array_pop($formulaStrings);
  4462.                 $formulaStrings["$op$space1$space0{$token['data']}";
  4463.                 unset($space0$space1);
  4464.                 break;
  4465.             case 'tAttrVolatile'// indicates volatile function
  4466.             case 'tAttrIf':
  4467.             case 'tAttrSkip':
  4468.             case 'tAttrChoose':
  4469.                 // token is only important for Excel formula evaluator
  4470.                 // do nothing
  4471.                 break;
  4472.             case 'tAttrSpace'// space / carriage return
  4473.                 // space will be used when next token arrives, do not alter formulaString stack
  4474.                 switch ($token['data']['spacetype']{
  4475.                 case 'type0':
  4476.                     $space0 str_repeat(' '$token['data']['spacecount']);
  4477.                     break;
  4478.                 case 'type1':
  4479.                     $space1 str_repeat("\n"$token['data']['spacecount']);
  4480.                     break;
  4481.                 case 'type2':
  4482.                     $space2 str_repeat(' '$token['data']['spacecount']);
  4483.                     break;
  4484.                 case 'type3':
  4485.                     $space3 str_repeat("\n"$token['data']['spacecount']);
  4486.                     break;
  4487.                 case 'type4':
  4488.                     $space4 str_repeat(' '$token['data']['spacecount']);
  4489.                     break;
  4490.                 case 'type5':
  4491.                     $space5 str_repeat("\n"$token['data']['spacecount']);
  4492.                     break;
  4493.                 }
  4494.                 break;
  4495.             case 'tAttrSum'// SUM function with one parameter
  4496.                 $op array_pop($formulaStrings);
  4497.                 $formulaStrings["{$space1}{$space0}SUM($op)";
  4498.                 unset($space0$space1);
  4499.                 break;
  4500.             case 'tFunc'// function with fixed number of arguments
  4501.             case 'tFuncV'// function with variable number of arguments
  4502.                 if ($token['data']['function'!= ''{
  4503.                     // normal function
  4504.                     $ops array()// array of operators
  4505.                     for ($i 0$i $token['data']['args']++$i{
  4506.                         $ops[array_pop($formulaStrings);
  4507.                     }
  4508.                     $ops array_reverse($ops);
  4509.                     $formulaStrings["$space1$space0{$token['data']['function']}(implode(','$ops")";
  4510.                     unset($space0$space1);
  4511.                 else {
  4512.                     // add-in function
  4513.                     $ops array()// array of operators
  4514.                     for ($i 0$i $token['data']['args'1++$i{
  4515.                         $ops[array_pop($formulaStrings);
  4516.                     }
  4517.                     $ops array_reverse($ops);
  4518.                     $function array_pop($formulaStrings);
  4519.                     $formulaStrings["$space1$space0$function(implode(','$ops")";
  4520.                     unset($space0$space1);
  4521.                 }
  4522.                 break;
  4523.             case 'tParen'// parenthesis
  4524.                 $expression array_pop($formulaStrings);
  4525.                 $formulaStrings["$space3$space2($expression$space5$space4)";
  4526.                 unset($space2$space3$space4$space5);
  4527.                 break;
  4528.             case 'tArray'// array constant
  4529.                 $constantArray $this->_readBIFF8ConstantArray($additionalData);
  4530.                 $formulaStrings[$space1 $space0 $constantArray['value'];
  4531.                 $additionalData substr($additionalData$constantArray['size'])// bite of chunk of additional data
  4532.                 unset($space0$space1);
  4533.                 break;
  4534.             case 'tMemArea':
  4535.                 // bite off chunk of additional data
  4536.                 $cellRangeAddressList $this->_readBIFF8CellRangeAddressList($additionalData);
  4537.                 $additionalData substr($additionalData$cellRangeAddressList['size']);
  4538.                 $formulaStrings["$space1$space0{$token['data']}";
  4539.                 unset($space0$space1);
  4540.                 break;
  4541.             case 'tArea'// cell range address
  4542.             case 'tBool'// boolean
  4543.             case 'tErr'// error code
  4544.             case 'tInt'// integer
  4545.             case 'tMemErr':
  4546.             case 'tMemFunc':
  4547.             case 'tMissArg':
  4548.             case 'tName':
  4549.             case 'tNameX':
  4550.             case 'tNum'// number
  4551.             case 'tRef'// single cell reference
  4552.             case 'tRef3d'// 3d cell reference
  4553.             case 'tArea3d'// 3d cell range reference
  4554.             case 'tRefN':
  4555.             case 'tAreaN':
  4556.             case 'tStr'// string
  4557.                 $formulaStrings["$space1$space0{$token['data']}";
  4558.                 unset($space0$space1);
  4559.                 break;
  4560.             }
  4561.         }
  4562.         $formulaString $formulaStrings[0];
  4563.  
  4564.         // for debug: dump the human readable formula
  4565.         //echo '----' . "\n";
  4566.         //echo 'Formula: ' . $formulaString;
  4567.  
  4568.         return $formulaString;
  4569.     }
  4570.  
  4571.     /**
  4572.      * Fetch next token from binary formula data
  4573.      *
  4574.      * @param string Formula data
  4575.      * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
  4576.      * @return array 
  4577.      * @throws Exception
  4578.      */
  4579.     private function _getNextToken($formulaData$baseCell 'A1')
  4580.     {
  4581.         // offset: 0; size: 1; token id
  4582.         $id ord($formulaData[0])// token id
  4583.         $name false// initialize token name
  4584.  
  4585.         switch ($id{
  4586.         case 0x03$name 'tAdd';        $size 1;    $data '+';    break;
  4587.         case 0x04$name 'tSub';        $size 1;    $data '-';    break;
  4588.         case 0x05$name 'tMul';        $size 1;    $data '*';    break;
  4589.         case 0x06$name 'tDiv';        $size 1;    $data '/';    break;
  4590.         case 0x07$name 'tPower';    $size 1;    $data '^';    break;
  4591.         case 0x08$name 'tConcat';    $size 1;    $data '&';    break;
  4592.         case 0x09$name 'tLT';        $size 1;    $data '<';    break;
  4593.         case 0x0A$name 'tLE';        $size 1;    $data '<=';    break;
  4594.         case 0x0B$name 'tEQ';        $size 1;    $data '=';    break;
  4595.         case 0x0C$name 'tGE';        $size 1;    $data '>=';    break;
  4596.         case 0x0D$name 'tGT';        $size 1;    $data '>';    break;
  4597.         case 0x0E$name 'tNE';        $size 1;    $data '<>';    break;
  4598.         case 0x0F$name 'tIsect';    $size 1;    $data ' ';    break;
  4599.         case 0x10$name 'tList';        $size 1;    $data ',';    break;
  4600.         case 0x11$name 'tRange';    $size 1;    $data ':';    break;
  4601.         case 0x12$name 'tUplus';    $size 1;    $data '+';    break;
  4602.         case 0x13$name 'tUminus';    $size 1;    $data '-';    break;
  4603.         case 0x14$name 'tPercent';    $size 1;    $data '%';    break;
  4604.         case 0x15// parenthesis
  4605.             $name  'tParen';
  4606.             $size  1;
  4607.             $data null;
  4608.             break;
  4609.         case 0x16// missing argument
  4610.             $name 'tMissArg';
  4611.             $size 1;
  4612.             $data '';
  4613.             break;
  4614.         case 0x17// string
  4615.             $name 'tStr';
  4616.             // offset: 1; size: var; Unicode string, 8-bit string length
  4617.             $string $this->_readUnicodeStringShort(substr($formulaData1));
  4618.             $size $string['size'];
  4619.             $data $this->_UTF8toExcelDoubleQuoted($string['value']);
  4620.             break;
  4621.         case 0x19// Special attribute
  4622.             // offset: 1; size: 1; attribute type flags:
  4623.             switch (ord($formulaData[1])) {
  4624.             case 0x01:
  4625.                 $name 'tAttrVolatile';
  4626.                 $size 4;
  4627.                 $data null;
  4628.                 break;
  4629.             case 0x02:
  4630.                 $name 'tAttrIf';
  4631.                 $size 4;
  4632.                 $data null;
  4633.                 break;
  4634.             case 0x04:
  4635.                 $name 'tAttrChoose';
  4636.                 // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
  4637.                 $nc $this->_GetInt2d($formulaData2);
  4638.                 // offset: 4; size: 2 * $nc
  4639.                 // offset: 4 + 2 * $nc; size: 2
  4640.                 $size $nc 6;
  4641.                 $data null;
  4642.                 break;
  4643.             case 0x08:
  4644.                 $name 'tAttrSkip';
  4645.                 $size 4;
  4646.                 $data null;
  4647.                 break;
  4648.             case 0x10:
  4649.                 $name 'tAttrSum';
  4650.                 $size 4;
  4651.                 $data null;
  4652.                 break;
  4653.             case 0x40:
  4654.             case 0x41:
  4655.                 $name 'tAttrSpace';
  4656.                 $size 4;
  4657.                 // offset: 2; size: 2; space type and position
  4658.                 switch (ord($formulaData[2])) {
  4659.                 case 0x00:
  4660.                     $spacetype 'type0';
  4661.                     break;
  4662.                 case 0x01:
  4663.                     $spacetype 'type1';
  4664.                     break;
  4665.                 case 0x02:
  4666.                     $spacetype 'type2';
  4667.                     break;
  4668.                 case 0x03:
  4669.                     $spacetype 'type3';
  4670.                     break;
  4671.                 case 0x04:
  4672.                     $spacetype 'type4';
  4673.                     break;
  4674.                 case 0x05:
  4675.                     $spacetype 'type5';
  4676.                     break;
  4677.                 default:
  4678.                     throw new Exception('Unrecognized space type in tAttrSpace token');
  4679.                     break;
  4680.                 }
  4681.                 // offset: 3; size: 1; number of inserted spaces/carriage returns
  4682.                 $spacecount ord($formulaData[3]);
  4683.  
  4684.                 $data array('spacetype' => $spacetype'spacecount' => $spacecount);
  4685.                 break;
  4686.             default:
  4687.                 throw new Exception('Unrecognized attribute flag in tAttr token');
  4688.                 break;
  4689.             }
  4690.             break;
  4691.         case 0x1C// error code
  4692.             // offset: 1; size: 1; error code
  4693.             $name 'tErr';
  4694.             $size 2;
  4695.             $data $this->_mapErrorCode(ord($formulaData[1]));
  4696.             break;
  4697.         case 0x1D// boolean
  4698.             // offset: 1; size: 1; 0 = false, 1 = true;
  4699.             $name 'tBool';
  4700.             $size 2;
  4701.             $data ord($formulaData[1]'TRUE' 'FALSE';
  4702.             break;
  4703.         case 0x1E// integer
  4704.             // offset: 1; size: 2; unsigned 16-bit integer
  4705.             $name 'tInt';
  4706.             $size 3;
  4707.             $data $this->_GetInt2d($formulaData1);
  4708.             break;
  4709.         case 0x1F// number
  4710.             // offset: 1; size: 8;
  4711.             $name 'tNum';
  4712.             $size 9;
  4713.             $data $this->_extractNumber(substr($formulaData1));
  4714.             $data str_replace(',''.'(string)$data)// in case non-English locale
  4715.             break;
  4716.         case 0x40// array constant
  4717.         case 0x60// array constant
  4718.             // offset: 1; size: 7; not used
  4719.             $name 'tArray';
  4720.             $size 8;
  4721.             $data null;
  4722.             break;
  4723.         case 0x41// function with fixed number of arguments
  4724.             $name 'tFunc';
  4725.             $size 3;
  4726.             // offset: 1; size: 2; index to built-in sheet function
  4727.             switch ($this->_GetInt2d($formulaData1)) {
  4728.             case   2$function 'ISNA';             $args 1;     break;
  4729.             case   3$function 'ISERROR';         $args 1;     break;
  4730.             case  10$function 'NA';             $args 0;     break;
  4731.             case  15$function 'SIN';             $args 1;     break;
  4732.             case  16$function 'COS';             $args 1;     break;
  4733.             case  17$function 'TAN';             $args 1;     break;
  4734.             case  18$function 'ATAN';             $args 1;     break;
  4735.             case  19$function 'PI';             $args 0;     break;
  4736.             case  20$function 'SQRT';             $args 1;     break;
  4737.             case  21$function 'EXP';             $args 1;     break;
  4738.             case  22$function 'LN';             $args 1;     break;
  4739.             case  23$function 'LOG10';             $args 1;     break;
  4740.             case  24$function 'ABS';             $args 1;     break;
  4741.             case  25$function 'INT';             $args 1;     break;
  4742.             case  26$function 'SIGN';             $args 1;     break;
  4743.             case  27$function 'ROUND';             $args 2;     break;
  4744.             case  30$function 'REPT';             $args 2;     break;
  4745.             case  31$function 'MID';             $args 3;     break;
  4746.             case  32$function 'LEN';             $args 1;     break;
  4747.             case  33$function 'VALUE';             $args 1;     break;
  4748.             case  34$function 'TRUE';             $args 0;     break;
  4749.             case  35$function 'FALSE';             $args 0;     break;
  4750.             case  38$function 'NOT';             $args 1;     break;
  4751.             case  39$function 'MOD';             $args 2;    break;
  4752.             case  40$function 'DCOUNT';         $args 3;    break;
  4753.             case  41$function 'DSUM';             $args 3;    break;
  4754.             case  42$function 'DAVERAGE';         $args 3;    break;
  4755.             case  43$function 'DMIN';             $args 3;    break;
  4756.             case  44$function 'DMAX';             $args 3;    break;
  4757.             case  45$function 'DSTDEV';         $args 3;    break;
  4758.             case  48$function 'TEXT';             $args 2;    break;
  4759.             case  61$function 'MIRR';             $args 3;    break;
  4760.             case  63$function 'RAND';             $args 0;    break;
  4761.             case  65$function 'DATE';             $args 3;    break;
  4762.             case  66$function 'TIME';             $args 3;    break;
  4763.             case  67$function 'DAY';             $args 1;    break;
  4764.             case  68$function 'MONTH';             $args 1;    break;
  4765.             case  69$function 'YEAR';             $args 1;    break;
  4766.             case  71$function 'HOUR';             $args 1;    break;
  4767.             case  72$function 'MINUTE';         $args 1;    break;
  4768.             case  73$function 'SECOND';         $args 1;    break;
  4769.             case  74$function 'NOW';             $args 0;    break;
  4770.             case  75$function 'AREAS';             $args 1;    break;
  4771.             case  76$function 'ROWS';             $args 1;    break;
  4772.             case  77$function 'COLUMNS';         $args 1;    break;
  4773.             case  83$function 'TRANSPOSE';         $args 1;    break;
  4774.             case  86$function 'TYPE';             $args 1;    break;
  4775.             case  97$function 'ATAN2';             $args 2;    break;
  4776.             case  98$function 'ASIN';             $args 1;    break;
  4777.             case  99$function 'ACOS';             $args 1;    break;
  4778.             case 105$function 'ISREF';             $args 1;    break;
  4779.             case 111$function 'CHAR';             $args 1;    break;
  4780.             case 112$function 'LOWER';             $args 1;    break;
  4781.             case 113$function 'UPPER';             $args 1;    break;
  4782.             case 114$function 'PROPER';         $args 1;    break;
  4783.             case 117$function 'EXACT';             $args 2;    break;
  4784.             case 118$function 'TRIM';             $args 1;    break;
  4785.             case 119$function 'REPLACE';         $args 4;    break;
  4786.             case 121$function 'CODE';             $args 1;    break;
  4787.             case 126$function 'ISERR';             $args 1;    break;
  4788.             case 127$function 'ISTEXT';         $args 1;    break;
  4789.             case 128$function 'ISNUMBER';         $args 1;    break;
  4790.             case 129$function 'ISBLANK';         $args 1;    break;
  4791.             case 130$function 'T';                 $args 1;    break;
  4792.             case 131$function 'N';                 $args 1;    break;
  4793.             case 140$function 'DATEVALUE';         $args 1;    break;
  4794.             case 141$function 'TIMEVALUE';         $args 1;    break;
  4795.             case 142$function 'SLN';             $args 3;    break;
  4796.             case 143$function 'SYD';             $args 4;    break;
  4797.             case 162$function 'CLEAN';             $args 1;    break;
  4798.             case 163$function 'MDETERM';         $args 1;    break;
  4799.             case 164$function 'MINVERSE';         $args 1;    break;
  4800.             case 165$function 'MMULT';             $args 2;    break;
  4801.             case 184$function 'FACT';             $args 1;    break;
  4802.             case 189$function 'DPRODUCT';         $args 3;    break;
  4803.             case 190$function 'ISNONTEXT';         $args 1;    break;
  4804.             case 195$function 'DSTDEVP';         $args 3;    break;
  4805.             case 196$function 'DVARP';             $args 3;    break;
  4806.             case 198$function 'ISLOGICAL';         $args 1;    break;
  4807.             case 199$function 'DCOUNTA';         $args 3;    break;
  4808.             case 207$function 'REPLACEB';         $args 4;    break;
  4809.             case 210$function 'MIDB';             $args 3;    break;
  4810.             case 211$function 'LENB';             $args 1;    break;
  4811.             case 212$function 'ROUNDUP';         $args 2;    break;
  4812.             case 213$function 'ROUNDDOWN';         $args 2;    break;
  4813.             case 214$function 'ASC';             $args 1;    break;
  4814.             case 215$function 'DBCS';             $args 1;    break;
  4815.             case 221$function 'TODAY';             $args 0;    break;
  4816.             case 229$function 'SINH';             $args 1;    break;
  4817.             case 230$function 'COSH';             $args 1;    break;
  4818.             case 231$function 'TANH';             $args 1;    break;
  4819.             case 232$function 'ASINH';             $args 1;    break;
  4820.             case 233$function 'ACOSH';             $args 1;    break;
  4821.             case 234$function 'ATANH';             $args 1;    break;
  4822.             case 235$function 'DGET';             $args 3;    break;
  4823.             case 244$function 'INFO';             $args 1;    break;
  4824.             case 252$function 'FREQUENCY';         $args 2;    break;
  4825.             case 261$function 'ERROR.TYPE';     $args 1;    break;
  4826.             case 271$function 'GAMMALN';         $args 1;    break;
  4827.             case 273$function 'BINOMDIST';         $args 4;    break;
  4828.             case 274$function 'CHIDIST';         $args 2;    break;
  4829.             case 275$function 'CHIINV';         $args 2;    break;
  4830.             case 276$function 'COMBIN';         $args 2;    break;
  4831.             case 277$function 'CONFIDENCE';     $args 3;    break;
  4832.             case 278$function 'CRITBINOM';         $args 3;    break;
  4833.             case 279$function 'EVEN';             $args 1;    break;
  4834.             case 280$function 'EXPONDIST';         $args 3;    break;
  4835.             case 281$function 'FDIST';             $args 3;    break;
  4836.             case 282$function 'FINV';             $args 3;    break;
  4837.             case 283$function 'FISHER';         $args 1;    break;
  4838.             case 284$function 'FISHERINV';         $args 1;    break;
  4839.             case 285$function 'FLOOR';             $args 2;    break;
  4840.             case 286$function 'GAMMADIST';         $args 4;    break;
  4841.             case 287$function 'GAMMAINV';         $args 3;    break;
  4842.             case 288$function 'CEILING';         $args 2;    break;
  4843.             case 289$function 'HYPGEOMDIST';    $args 4;    break;
  4844.             case 290$function 'LOGNORMDIST';    $args 3;    break;
  4845.             case 291$function 'LOGINV';            $args 3;    break;
  4846.             case 292$function 'NEGBINOMDIST';    $args 3;    break;
  4847.             case 293$function 'NORMDIST';        $args 4;    break;
  4848.             case 294$function 'NORMSDIST';        $args 1;    break;
  4849.             case 295$function 'NORMINV';        $args 3;    break;
  4850.             case 296$function 'NORMSINV';        $args 1;    break;
  4851.             case 297$function 'STANDARDIZE';    $args 3;    break;
  4852.             case 298$function 'ODD';            $args 1;    break;
  4853.             case 299$function 'PERMUT';            $args 2;    break;
  4854.             case 300$function 'POISSON';        $args 3;    break;
  4855.             case 301$function 'TDIST';            $args 3;    break;
  4856.             case 302$function 'WEIBULL';        $args 4;    break;
  4857.             case 303$function 'SUMXMY2';        $args 2;    break;
  4858.             case 304$function 'SUMX2MY2';        $args 2;    break;
  4859.             case 305$function 'SUMX2PY2';        $args 2;    break;
  4860.             case 306$function 'CHITEST';        $args 2;    break;
  4861.             case 307$function 'CORREL';            $args 2;    break;
  4862.             case 308$function 'COVAR';            $args 2;    break;
  4863.             case 309$function 'FORECAST';        $args 3;    break;
  4864.             case 310$function 'FTEST';            $args 2;    break;
  4865.             case 311$function 'INTERCEPT';        $args 2;    break;
  4866.             case 312$function 'PEARSON';        $args 2;    break;
  4867.             case 313$function 'RSQ';            $args 2;    break;
  4868.             case 314$function 'STEYX';            $args 2;    break;
  4869.             case 315$function 'SLOPE';            $args 2;    break;
  4870.             case 316$function 'TTEST';            $args 4;    break;
  4871.             case 325$function 'LARGE';            $args 2;    break;
  4872.             case 326$function 'SMALL';            $args 2;    break;
  4873.             case 327$function 'QUARTILE';        $args 2;    break;
  4874.             case 328$function 'PERCENTILE';        $args 2;    break;
  4875.             case 331$function 'TRIMMEAN';        $args 2;    break;
  4876.             case 332$function 'TINV';            $args 2;    break;
  4877.             case 337$function 'POWER';            $args 2;    break;
  4878.             case 342$function 'RADIANS';        $args 1;    break;
  4879.             case 343$function 'DEGREES';        $args 1;    break;
  4880.             case 346$function 'COUNTIF';        $args 2;    break;
  4881.             case 347$function 'COUNTBLANK';        $args 1;    break;
  4882.             case 350$function 'ISPMT';            $args 4;    break;
  4883.             case 351$function 'DATEDIF';        $args 3;    break;
  4884.             case 352$function 'DATESTRING';        $args 1;    break;
  4885.             case 353$function 'NUMBERSTRING';    $args 2;    break;
  4886.             case 360$function 'PHONETIC';        $args 1;    break;
  4887.             default:
  4888.                 throw new Exception('Unrecognized function in formula');
  4889.                 break;
  4890.             }
  4891.             $data array('function' => $function'args' => $args);
  4892.             break;
  4893.         case 0x22// function with variable number of arguments
  4894.         case 0x42// function with variable number of arguments
  4895.         case 0x62// function with variable number of arguments
  4896.             $name 'tFuncV';
  4897.             $size 4;
  4898.             // offset: 1; size: 1; number of arguments
  4899.             $args ord($formulaData[1]);
  4900.             // offset: 2: size: 2; index to built-in sheet function
  4901.             $index $this->_GetInt2d($formulaData2);
  4902.             switch ($index{
  4903.             case   0$function 'COUNT';            break;
  4904.             case   1$function 'IF';                break;
  4905.             case   4$function 'SUM';            break;
  4906.             case   5$function 'AVERAGE';        break;
  4907.             case   6$function 'MIN';            break;
  4908.             case   7$function 'MAX';            break;
  4909.             case   8$function 'ROW';            break;
  4910.             case   9$function 'COLUMN';            break;
  4911.             case  11$function 'NPV';            break;
  4912.             case  12$function 'STDEV';            break;
  4913.             case  13$function 'DOLLAR';            break;
  4914.             case  14$function 'FIXED';            break;
  4915.             case  28$function 'LOOKUP';            break;
  4916.             case  29$function 'INDEX';            break;
  4917.             case  36$function 'AND';            break;
  4918.             case  37$function 'OR';                break;
  4919.             case  46$function 'VAR';            break;
  4920.             case  49$function 'LINEST';            break;
  4921.             case  50$function 'TREND';            break;
  4922.             case  51$function 'LOGEST';            break;
  4923.             case  52$function 'GROWTH';            break;
  4924.             case  56$function 'PV';                break;
  4925.             case  57$function 'FV';                break;
  4926.             case  58$function 'NPER';            break;
  4927.             case  59$function 'PMT';            break;
  4928.             case  60$function 'RATE';            break;
  4929.             case  62$function 'IRR';            break;
  4930.             case  64$function 'MATCH';            break;
  4931.             case  70$function 'WEEKDAY';        break;
  4932.             case  78$function 'OFFSET';            break;
  4933.             case  82$function 'SEARCH';            break;
  4934.             case 100$function 'CHOOSE';            break;
  4935.             case 101$function 'HLOOKUP';        break;
  4936.             case 102$function 'VLOOKUP';        break;
  4937.             case 109$function 'LOG';            break;
  4938.             case 115$function 'LEFT';            break;
  4939.             case 116$function 'RIGHT';            break;
  4940.             case 120$function 'SUBSTITUTE';        break;
  4941.             case 124$function 'FIND';            break;
  4942.             case 125$function 'CELL';            break;
  4943.             case 144$function 'DDB';            break;
  4944.             case 148$function 'INDIRECT';        break;
  4945.             case 167$function 'IPMT';            break;
  4946.             case 168$function 'PPMT';            break;
  4947.             case 169$function 'COUNTA';            break;
  4948.             case 183$function 'PRODUCT';        break;
  4949.             case 193$function 'STDEVP';            break;
  4950.             case 194$function 'VARP';            break;
  4951.             case 197$function 'TRUNC';            break;
  4952.             case 204$function 'USDOLLAR';        break;
  4953.             case 205$function 'FINDB';            break;
  4954.             case 206$function 'SEARCHB';        break;
  4955.             case 208$function 'LEFTB';            break;
  4956.             case 209$function 'RIGHTB';            break;
  4957.             case 216$function 'RANK';            break;
  4958.             case 219$function 'ADDRESS';        break;
  4959.             case 220$function 'DAYS360';        break;
  4960.             case 222$function 'VDB';            break;
  4961.             case 227$function 'MEDIAN';            break;
  4962.             case 228$function 'SUMPRODUCT';        break;
  4963.             case 247$function 'DB';                break;
  4964.             case 255$function '';                break;
  4965.             case 269$function 'AVEDEV';            break;
  4966.             case 270$function 'BETADIST';        break;
  4967.             case 272$function 'BETAINV';        break;
  4968.             case 317$function 'PROB';            break;
  4969.             case 318$function 'DEVSQ';            break;
  4970.             case 319$function 'GEOMEAN';        break;
  4971.             case 320$function 'HARMEAN';        break;
  4972.             case 321$function 'SUMSQ';            break;
  4973.             case 322$function 'KURT';            break;
  4974.             case 323$function 'SKEW';            break;
  4975.             case 324$function 'ZTEST';            break;
  4976.             case 329$function 'PERCENTRANK';    break;
  4977.             case 330$function 'MODE';            break;
  4978.             case 336$function 'CONCATENATE';    break;
  4979.             case 344$function 'SUBTOTAL';        break;
  4980.             case 345$function 'SUMIF';            break;
  4981.             case 354$function 'ROMAN';            break;
  4982.             case 358$function 'GETPIVOTDATA';    break;
  4983.             case 359$function 'HYPERLINK';        break;
  4984.             case 361$function 'AVERAGEA';        break;
  4985.             case 362$function 'MAXA';            break;
  4986.             case 363$function 'MINA';            break;
  4987.             case 364$function 'STDEVPA';        break;
  4988.             case 365$function 'VARPA';            break;
  4989.             case 366$function 'STDEVA';            break;
  4990.             case 367$function 'VARA';            break;
  4991.             default:
  4992.                 throw new Exception('Unrecognized function in formula');
  4993.                 break;
  4994.             }
  4995.             $data array('function' => $function'args' => $args);
  4996.             break;
  4997.         case 0x23// index to defined name
  4998.         case 0x43:
  4999.             $name 'tName';
  5000.             $size 5;
  5001.             // offset: 1; size: 2; one-based index to definedname record
  5002.             $definedNameIndex $this->_GetInt2d($formulaData11;
  5003.             // offset: 2; size: 2; not used
  5004.             $data $this->_definedname[$definedNameIndex]['name'];
  5005.             break;
  5006.         case 0x24// single cell reference e.g. A5
  5007.         case 0x44:
  5008.         case 0x64:
  5009.             $name 'tRef';
  5010.             $size 5;
  5011.             $data $this->_readBIFF8CellAddress(substr($formulaData14));
  5012.             break;
  5013.         case 0x25// cell range reference to cells in the same sheet
  5014.         case 0x45:
  5015.         case 0x65:
  5016.             $name 'tArea';
  5017.             $size 9;
  5018.             $data $this->_readBIFF8CellRangeAddress(substr($formulaData18));
  5019.             break;
  5020.         case 0x26:
  5021.         case 0x46:
  5022.             $name 'tMemArea';
  5023.             // offset: 1; size: 4; not used
  5024.             // offset: 5; size: 2; size of the following subexpression
  5025.             $subSize $this->_GetInt2d($formulaData5);
  5026.             $size $subSize;
  5027.             $data $this->_getFormulaFromData(substr($formulaData7$subSize));
  5028.             break;
  5029.         case 0x47:
  5030.             $name 'tMemErr';
  5031.             // offset: 1; size: 4; not used
  5032.             // offset: 5; size: 2; size of the following subexpression
  5033.             $subSize $this->_GetInt2d($formulaData5);
  5034.             $size $subSize;
  5035.             $data $this->_getFormulaFromData(substr($formulaData7$subSize));
  5036.             break;
  5037.         case 0x29:
  5038.         case 0x49:
  5039.             $name 'tMemFunc';
  5040.             // offset: 1; size: 2; size of the following subexpression
  5041.             $subSize $this->_GetInt2d($formulaData1);
  5042.             $size $subSize;
  5043.             $data $this->_getFormulaFromData(substr($formulaData3$subSize));
  5044.             break;
  5045.  
  5046.         case 0x2C// Relative reference, used in shared formulas and some other places
  5047.         case 0x4C:
  5048.         case 0x6C:
  5049.             $name 'tRefN';
  5050.             $size 5;
  5051.             $data $this->_readBIFF8CellAddressB(substr($formulaData14)$baseCell);
  5052.             break;
  5053.  
  5054.         case 0x2D:
  5055.         case 0x4D:
  5056.         case 0x6D:
  5057.             $name 'tAreaN';
  5058.             $size 9;
  5059.             $data $this->_readBIFF8CellRangeAddressB(substr($formulaData18)$baseCell);
  5060.             break;
  5061.  
  5062.         case 0x39:
  5063.         case 0x59:
  5064.         case 0x79:
  5065.             $name 'tNameX';
  5066.             $size 7;
  5067.             // offset: 1; size: 2; index to REF entry in EXTERNSHEET record
  5068.             // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record
  5069.             $index $this->_GetInt2d($formulaData3);
  5070.             // assume index is to EXTERNNAME record
  5071.             $data $this->_externalNames[$index 1]['name'];
  5072.             // offset: 5; size: 2; not used
  5073.             break;
  5074.  
  5075.         case 0x3A// 3d reference to cell
  5076.         case 0x5A:
  5077.             $name 'tRef3d';
  5078.             $size 7;
  5079.  
  5080.             try {
  5081.                 // offset: 1; size: 2; index to REF entry
  5082.                 $sheetRange $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData1));
  5083.                 // offset: 3; size: 4; cell address
  5084.                 $cellAddress $this->_readBIFF8CellAddress(substr($formulaData34));
  5085.  
  5086.                 $data "$sheetRange!$cellAddress";
  5087.  
  5088.             catch (Exception $e{
  5089.                 // deleted sheet reference
  5090.                 $data '#REF!';
  5091.             }
  5092.  
  5093.             break;
  5094.         case 0x3B// 3d reference to cell range
  5095.         case 0x5B:
  5096.             $name 'tArea3d';
  5097.             $size 11;
  5098.  
  5099.             try {
  5100.                 // offset: 1; size: 2; index to REF entry
  5101.                 $sheetRange $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData1));
  5102.                 // offset: 3; size: 8; cell address
  5103.                 $cellRangeAddress $this->_readBIFF8CellRangeAddress(substr($formulaData38));
  5104.  
  5105.                 $data "$sheetRange!$cellRangeAddress";
  5106.  
  5107.             catch (Exception $e{
  5108.                 // deleted sheet reference
  5109.                 $data '#REF!';
  5110.  
  5111.             }
  5112.  
  5113.             break;
  5114.         // case 0x39: // don't know how to deal with
  5115.         default:
  5116.             throw new Exception('Unrecognized token ' sprintf('%02X'$id' in formula');
  5117.             break;
  5118.         }
  5119.  
  5120.         return array(
  5121.             'id' => $id,
  5122.             'name' => $name,
  5123.             'size' => $size,
  5124.             'data' => $data,
  5125.         );
  5126.     }
  5127.  
  5128.     /**
  5129.      * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
  5130.      * section 3.3.4
  5131.      *
  5132.      * @param string $cellAddressStructure 
  5133.      * @return string 
  5134.      */
  5135.     private function _readBIFF8CellAddress($cellAddressStructure)
  5136.     {
  5137.         // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
  5138.             $row $this->_GetInt2d($cellAddressStructure01;
  5139.  
  5140.         // offset: 2; size: 2; index to column or column offset + relative flags
  5141.  
  5142.             // bit: 7-0; mask 0x00FF; column index
  5143.             $column PHPExcel_Cell::stringFromColumnIndex(0x00FF $this->_GetInt2d($cellAddressStructure2));
  5144.  
  5145.             // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
  5146.             if (!(0x4000 $this->_GetInt2d($cellAddressStructure2))) {
  5147.                 $column '$' $column;
  5148.             }
  5149.             // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
  5150.             if (!(0x8000 $this->_GetInt2d($cellAddressStructure2))) {
  5151.                 $row '$' $row;
  5152.             }
  5153.  
  5154.         return $column $row;
  5155.     }
  5156.  
  5157.     /**
  5158.      * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
  5159.      * to indicate offsets from a base cell
  5160.      * section 3.3.4
  5161.      *
  5162.      * @param string $cellAddressStructure 
  5163.      * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
  5164.      * @return string 
  5165.      */
  5166.     private function _readBIFF8CellAddressB($cellAddressStructure$baseCell 'A1')
  5167.     {
  5168.         list($baseCol$baseRowPHPExcel_Cell::coordinateFromString($baseCell);
  5169.         $baseCol PHPExcel_Cell::columnIndexFromString($baseCol1;
  5170.  
  5171.         // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
  5172.             $rowIndex $this->_GetInt2d($cellAddressStructure0);
  5173.             $row $this->_GetInt2d($cellAddressStructure01;
  5174.  
  5175.         // offset: 2; size: 2; index to column or column offset + relative flags
  5176.  
  5177.             // bit: 7-0; mask 0x00FF; column index
  5178.             $colIndex 0x00FF $this->_GetInt2d($cellAddressStructure2);
  5179.  
  5180.             // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
  5181.             if (!(0x4000 $this->_GetInt2d($cellAddressStructure2))) {
  5182.                 $column PHPExcel_Cell::stringFromColumnIndex($colIndex);
  5183.                 $column '$' $column;
  5184.             else {
  5185.                 $colIndex ($colIndex <= 127$colIndex $colIndex 256;
  5186.                 $column PHPExcel_Cell::stringFromColumnIndex($baseCol $colIndex);
  5187.             }
  5188.  
  5189.             // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
  5190.             if (!(0x8000 $this->_GetInt2d($cellAddressStructure2))) {
  5191.                 $row '$' $row;
  5192.             else {
  5193.                 $rowIndex ($rowIndex <= 32767$rowIndex $rowIndex 65536;
  5194.                 $row $baseRow $rowIndex;
  5195.             }
  5196.  
  5197.         return $column $row;
  5198.     }
  5199.  
  5200.     /**
  5201.      * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
  5202.      * always fixed range
  5203.      * section 2.5.14
  5204.      *
  5205.      * @param string $subData 
  5206.      * @return string 
  5207.      * @throws Exception
  5208.      */
  5209.     private function _readBIFF5CellRangeAddressFixed($subData)
  5210.     {
  5211.         // offset: 0; size: 2; index to first row
  5212.         $fr $this->_GetInt2d($subData01;
  5213.  
  5214.         // offset: 2; size: 2; index to last row
  5215.         $lr $this->_GetInt2d($subData21;
  5216.  
  5217.         // offset: 4; size: 1; index to first column
  5218.         $fc ord($subData{4});
  5219.  
  5220.         // offset: 5; size: 1; index to last column
  5221.         $lc ord($subData{5});
  5222.  
  5223.         // check values
  5224.         if ($fr $lr || $fc $lc{
  5225.             throw new Exception('Not a cell range address');
  5226.         }
  5227.  
  5228.         // column index to letter
  5229.         $fc PHPExcel_Cell::stringFromColumnIndex($fc);
  5230.         $lc PHPExcel_Cell::stringFromColumnIndex($lc);
  5231.  
  5232.         if ($fr == $lr and $fc == $lc{
  5233.             return "$fc$fr";
  5234.         }
  5235.         return "$fc$fr:$lc$lr";
  5236.     }
  5237.  
  5238.     /**
  5239.      * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
  5240.      * always fixed range
  5241.      * section 2.5.14
  5242.      *
  5243.      * @param string $subData 
  5244.      * @return string 
  5245.      * @throws Exception
  5246.      */
  5247.     private function _readBIFF8CellRangeAddressFixed($subData)
  5248.     {
  5249.         // offset: 0; size: 2; index to first row
  5250.         $fr $this->_GetInt2d($subData01;
  5251.  
  5252.         // offset: 2; size: 2; index to last row
  5253.         $lr $this->_GetInt2d($subData21;
  5254.  
  5255.         // offset: 4; size: 2; index to first column
  5256.         $fc $this->_GetInt2d($subData4);
  5257.  
  5258.         // offset: 6; size: 2; index to last column
  5259.         $lc $this->_GetInt2d($subData6);
  5260.  
  5261.         // check values
  5262.         if ($fr $lr || $fc $lc{
  5263.             throw new Exception('Not a cell range address');
  5264.         }
  5265.  
  5266.         // column index to letter
  5267.         $fc PHPExcel_Cell::stringFromColumnIndex($fc);
  5268.         $lc PHPExcel_Cell::stringFromColumnIndex($lc);
  5269.  
  5270.         if ($fr == $lr and $fc == $lc{
  5271.             return "$fc$fr";
  5272.         }
  5273.         return "$fc$fr:$lc$lr";
  5274.     }
  5275.  
  5276.     /**
  5277.      * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
  5278.      * there are flags indicating whether column/row index is relative
  5279.      * section 3.3.4
  5280.      *
  5281.      * @param string $subData 
  5282.      * @return string 
  5283.      */
  5284.     private function _readBIFF8CellRangeAddress($subData)
  5285.     {
  5286.         // todo: if cell range is just a single cell, should this funciton
  5287.         // not just return e.g. 'A1' and not 'A1:A1' ?
  5288.  
  5289.         // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767))
  5290.             $fr $this->_GetInt2d($subData01;
  5291.  
  5292.         // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767))
  5293.             $lr $this->_GetInt2d($subData21;
  5294.  
  5295.         // offset: 4; size: 2; index to first column or column offset + relative flags
  5296.  
  5297.             // bit: 7-0; mask 0x00FF; column index
  5298.             $fc PHPExcel_Cell::stringFromColumnIndex(0x00FF $this->_GetInt2d($subData4));
  5299.  
  5300.             // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
  5301.             if (!(0x4000 $this->_GetInt2d($subData4))) {
  5302.                 $fc '$' $fc;
  5303.             }
  5304.  
  5305.             // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
  5306.             if (!(0x8000 $this->_GetInt2d($subData4))) {
  5307.                 $fr '$' $fr;
  5308.             }
  5309.  
  5310.         // offset: 6; size: 2; index to last column or column offset + relative flags
  5311.  
  5312.             // bit: 7-0; mask 0x00FF; column index
  5313.             $lc PHPExcel_Cell::stringFromColumnIndex(0x00FF $this->_GetInt2d($subData6));
  5314.  
  5315.             // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
  5316.             if (!(0x4000 $this->_GetInt2d($subData6))) {
  5317.                 $lc '$' $lc;
  5318.             }
  5319.  
  5320.             // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
  5321.             if (!(0x8000 $this->_GetInt2d($subData6))) {
  5322.                 $lr '$' $lr;
  5323.             }
  5324.  
  5325.         return "$fc$fr:$lc$lr";
  5326.     }
  5327.  
  5328.     /**
  5329.      * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
  5330.      * to indicate offsets from a base cell
  5331.      * section 3.3.4
  5332.      *
  5333.      * @param string $subData 
  5334.      * @param string $baseCell Base cell
  5335.      * @return string Cell range address
  5336.      */
  5337.     private function _readBIFF8CellRangeAddressB($subData$baseCell 'A1')
  5338.     {
  5339.         list($baseCol$baseRowPHPExcel_Cell::coordinateFromString($baseCell);
  5340.         $baseCol PHPExcel_Cell::columnIndexFromString($baseCol1;
  5341.  
  5342.         // TODO: if cell range is just a single cell, should this funciton
  5343.         // not just return e.g. 'A1' and not 'A1:A1' ?
  5344.  
  5345.         // offset: 0; size: 2; first row
  5346.         $frIndex $this->_GetInt2d($subData0)// adjust below
  5347.  
  5348.         // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
  5349.         $lrIndex $this->_GetInt2d($subData2)// adjust below
  5350.  
  5351.         // offset: 4; size: 2; first column with relative/absolute flags
  5352.  
  5353.             // bit: 7-0; mask 0x00FF; column index
  5354.             $fcIndex 0x00FF $this->_GetInt2d($subData4);
  5355.  
  5356.             // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
  5357.             if (!(0x4000 $this->_GetInt2d($subData4))) {
  5358.                 // absolute column index
  5359.                 $fc PHPExcel_Cell::stringFromColumnIndex($fcIndex);
  5360.                 $fc '$' $fc;
  5361.             else {
  5362.                 // column offset
  5363.                 $fcIndex ($fcIndex <= 127$fcIndex $fcIndex 256;
  5364.                 $fc PHPExcel_Cell::stringFromColumnIndex($baseCol $fcIndex);
  5365.             }
  5366.  
  5367.             // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
  5368.             if (!(0x8000 $this->_GetInt2d($subData4))) {
  5369.                 // absolute row index
  5370.                 $fr $frIndex 1;
  5371.                 $fr '$' $fr;
  5372.             else {
  5373.                 // row offset
  5374.                 $frIndex ($frIndex <= 32767$frIndex $frIndex 65536;
  5375.                 $fr $baseRow $frIndex;
  5376.             }
  5377.  
  5378.         // offset: 6; size: 2; last column with relative/absolute flags
  5379.  
  5380.             // bit: 7-0; mask 0x00FF; column index
  5381.             $lcIndex 0x00FF $this->_GetInt2d($subData6);
  5382.             $lcIndex ($lcIndex <= 127$lcIndex $lcIndex 256;
  5383.             $lc PHPExcel_Cell::stringFromColumnIndex($baseCol $lcIndex);
  5384.  
  5385.             // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
  5386.             if (!(0x4000 $this->_GetInt2d($subData6))) {
  5387.                 // absolute column index
  5388.                 $lc PHPExcel_Cell::stringFromColumnIndex($lcIndex);
  5389.                 $lc '$' $lc;
  5390.             else {
  5391.                 // column offset
  5392.                 $lcIndex ($lcIndex <= 127$lcIndex $lcIndex 256;
  5393.                 $lc PHPExcel_Cell::stringFromColumnIndex($baseCol $lcIndex);
  5394.             }
  5395.  
  5396.             // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
  5397.             if (!(0x8000 $this->_GetInt2d($subData6))) {
  5398.                 // absolute row index
  5399.                 $lr $lrIndex 1;
  5400.                 $lr '$' $lr;
  5401.             else {
  5402.                 // row offset
  5403.                 $lrIndex ($lrIndex <= 32767$lrIndex $lrIndex 65536;
  5404.                 $lr $baseRow $lrIndex;
  5405.             }
  5406.  
  5407.         return "$fc$fr:$lc$lr";
  5408.     }
  5409.  
  5410.     /**
  5411.      * Read BIFF8 cell range address list
  5412.      * section 2.5.15
  5413.      *
  5414.      * @param string $subData 
  5415.      * @return array 
  5416.      */
  5417.     private function _readBIFF8CellRangeAddressList($subData)
  5418.     {
  5419.         $cellRangeAddresses array();
  5420.  
  5421.         // offset: 0; size: 2; number of the following cell range addresses
  5422.         $nm $this->_GetInt2d($subData0);
  5423.  
  5424.         $offset 2;
  5425.         // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
  5426.         for ($i 0$i $nm++$i{
  5427.             $cellRangeAddresses[$this->_readBIFF8CellRangeAddressFixed(substr($subData$offset8));
  5428.             $offset += 8;
  5429.         }
  5430.  
  5431.         return array(
  5432.             'size' => $nm,
  5433.             'cellRangeAddresses' => $cellRangeAddresses,
  5434.         );
  5435.     }
  5436.  
  5437.     /**
  5438.      * Read BIFF5 cell range address list
  5439.      * section 2.5.15
  5440.      *
  5441.      * @param string $subData 
  5442.      * @return array 
  5443.      */
  5444.     private function _readBIFF5CellRangeAddressList($subData)
  5445.     {
  5446.         $cellRangeAddresses array();
  5447.  
  5448.         // offset: 0; size: 2; number of the following cell range addresses
  5449.         $nm $this->_GetInt2d($subData0);
  5450.  
  5451.         $offset 2;
  5452.         // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses
  5453.         for ($i 0$i $nm++$i{
  5454.             $cellRangeAddresses[$this->_readBIFF5CellRangeAddressFixed(substr($subData$offset6));
  5455.             $offset += 6;
  5456.         }
  5457.  
  5458.         return array(
  5459.             'size' => $nm,
  5460.             'cellRangeAddresses' => $cellRangeAddresses,
  5461.         );
  5462.     }
  5463.  
  5464.     /**
  5465.      * Get a sheet range like Sheet1:Sheet3 from REF index
  5466.      * Note: If there is only one sheet in the range, one gets e.g Sheet1
  5467.      * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
  5468.      * in which case an exception is thrown
  5469.      *
  5470.      * @param int $index 
  5471.      * @return string|false
  5472.      * @throws Exception
  5473.      */
  5474.     private function _readSheetRangeByRefIndex($index)
  5475.     {
  5476.         if (isset($this->_ref[$index])) {
  5477.  
  5478.             $type $this->_externalBooks[$this->_ref[$index]['externalBookIndex']]['type'];
  5479.  
  5480.             switch ($type{
  5481.                 case 'internal':
  5482.                     // check if we have a deleted 3d reference
  5483.                     if ($this->_ref[$index]['firstSheetIndex'== 0xFFFF or $this->_ref[$index]['lastSheetIndex'== 0xFFFF{
  5484.                         throw new Exception('Deleted sheet reference');
  5485.                     }
  5486.  
  5487.                     // we have normal sheet range (collapsed or uncollapsed)
  5488.                     $firstSheetName $this->_sheets[$this->_ref[$index]['firstSheetIndex']]['name'];
  5489.                     $lastSheetName $this->_sheets[$this->_ref[$index]['lastSheetIndex']]['name'];
  5490.  
  5491.                     if ($firstSheetName == $lastSheetName{
  5492.                         // collapsed sheet range
  5493.                         $sheetRange $firstSheetName;
  5494.                     else {
  5495.                         $sheetRange "$firstSheetName:$lastSheetName";
  5496.                     }
  5497.  
  5498.                     // escape the single-quotes
  5499.                     $sheetRange str_replace("'""''"$sheetRange);
  5500.  
  5501.                     // if there are special characters, we need to enclose the range in single-quotes
  5502.                     // todo: check if we have identified the whole set of special characters
  5503.                     // it seems that the following characters are not accepted for sheet names
  5504.                     // and we may assume that they are not present: []*/:\?
  5505.                     if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/"$sheetRange)) {
  5506.                         $sheetRange "'$sheetRange'";
  5507.                     }
  5508.  
  5509.                     return $sheetRange;
  5510.                     break;
  5511.  
  5512.                 default:
  5513.                     // TODO: external sheet support
  5514.                     throw new Exception('Excel5 reader only supports internal sheets in fomulas');
  5515.                     break;
  5516.             }
  5517.         }
  5518.         return false;
  5519.     }
  5520.  
  5521.     /**
  5522.      * read BIFF8 constant value array from array data
  5523.      * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40}
  5524.      * section 2.5.8
  5525.      *
  5526.      * @param string $arrayData 
  5527.      * @return array 
  5528.      */
  5529.     private function _readBIFF8ConstantArray($arrayData)
  5530.     {
  5531.         // offset: 0; size: 1; number of columns decreased by 1
  5532.         $nc ord($arrayData[0]);
  5533.  
  5534.         // offset: 1; size: 2; number of rows decreased by 1
  5535.         $nr $this->_GetInt2d($arrayData1);
  5536.         $size 3// initialize
  5537.         $arrayData substr($arrayData3);
  5538.  
  5539.         // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values
  5540.         $matrixChunks array();
  5541.         for ($r 1$r <= $nr 1++$r{
  5542.             $items array();
  5543.             for ($c 1$c <= $nc 1++$c{
  5544.                 $constant $this->_readBIFF8Constant($arrayData);
  5545.                 $items[$constant['value'];
  5546.                 $arrayData substr($arrayData$constant['size']);
  5547.                 $size += $constant['size'];
  5548.             }
  5549.             $matrixChunks[implode(','$items)// looks like e.g. '1,"hello"'
  5550.         }
  5551.         $matrix '{' implode(';'$matrixChunks'}';
  5552.  
  5553.         return array(
  5554.             'value' => $matrix,
  5555.             'size' => $size,
  5556.         );
  5557.     }
  5558.  
  5559.     /**
  5560.      * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
  5561.      * section 2.5.7
  5562.      * returns e.g. array('value' => '5', 'size' => 9)
  5563.      *
  5564.      * @param string $valueData 
  5565.      * @return array 
  5566.      */
  5567.     private function _readBIFF8Constant($valueData)
  5568.     {
  5569.         // offset: 0; size: 1; identifier for type of constant
  5570.         $identifier ord($valueData[0]);
  5571.  
  5572.         switch ($identifier{
  5573.         case 0x00// empty constant (what is this?)
  5574.             $value '';
  5575.             $size 9;
  5576.             break;
  5577.         case 0x01// number
  5578.             // offset: 1; size: 8; IEEE 754 floating-point value
  5579.             $value $this->_extractNumber(substr($valueData18));
  5580.             $size 9;
  5581.             break;
  5582.         case 0x02// string value
  5583.             // offset: 1; size: var; Unicode string, 16-bit string length
  5584.             $string $this->_readUnicodeStringLong(substr($valueData1));
  5585.             $value '"' $string['value''"';
  5586.             $size $string['size'];
  5587.             break;
  5588.         case 0x04// boolean
  5589.             // offset: 1; size: 1; 0 = FALSE, 1 = TRUE
  5590.             if (ord($valueData[1])) {
  5591.                 $value 'TRUE';
  5592.             else {
  5593.                 $value 'FALSE';
  5594.             }
  5595.             $size 9;
  5596.             break;
  5597.         case 0x10// error code
  5598.             // offset: 1; size: 1; error code
  5599.             $value $this->_mapErrorCode(ord($valueData[1]));
  5600.             $size 9;
  5601.             break;
  5602.         }
  5603.         return array(
  5604.             'value' => $value,
  5605.             'size' => $size,
  5606.         );
  5607.     }
  5608.  
  5609.     /**
  5610.      * Extract RGB color
  5611.      * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4
  5612.      *
  5613.      * @param string $rgb Encoded RGB value (4 bytes)
  5614.      * @return array 
  5615.      */
  5616.     private function _readRGB($rgb)
  5617.     {
  5618.         // offset: 0; size 1; Red component
  5619.         $r ord($rgb{0});
  5620.  
  5621.         // offset: 1; size: 1; Green component
  5622.         $g ord($rgb{1});
  5623.  
  5624.         // offset: 2; size: 1; Blue component
  5625.         $b ord($rgb{2});
  5626.  
  5627.         // HEX notation, e.g. 'FF00FC'
  5628.         $rgb sprintf('%02X'$rsprintf('%02X'$gsprintf('%02X'$b);
  5629.  
  5630.         return array('rgb' => $rgb);
  5631.     }
  5632.  
  5633.     /**
  5634.      * Read byte string (8-bit string length)
  5635.      * OpenOffice documentation: 2.5.2
  5636.      *
  5637.      * @param string $subData 
  5638.      * @return array 
  5639.      */
  5640.     private function _readByteStringShort($subData)
  5641.     {
  5642.         // offset: 0; size: 1; length of the string (character count)
  5643.         $ln ord($subData[0]);
  5644.  
  5645.         // offset: 1: size: var; character array (8-bit characters)
  5646.         $value $this->_decodeCodepage(substr($subData1$ln));
  5647.  
  5648.         return array(
  5649.             'value' => $value,
  5650.             'size' => $ln// size in bytes of data structure
  5651.         );
  5652.     }
  5653.  
  5654.     /**
  5655.      * Read byte string (16-bit string length)
  5656.      * OpenOffice documentation: 2.5.2
  5657.      *
  5658.      * @param string $subData 
  5659.      * @return array 
  5660.      */
  5661.     private function _readByteStringLong($subData)
  5662.     {
  5663.         // offset: 0; size: 2; length of the string (character count)
  5664.         $ln $this->_GetInt2d($subData0);
  5665.  
  5666.         // offset: 2: size: var; character array (8-bit characters)
  5667.         $value $this->_decodeCodepage(substr($subData2));
  5668.  
  5669.         //return $string;
  5670.         return array(
  5671.             'value' => $value,
  5672.             'size' => $ln// size in bytes of data structure
  5673.         );
  5674.     }
  5675.  
  5676.     /**
  5677.      * Extracts an Excel Unicode short string (8-bit string length)
  5678.      * OpenOffice documentation: 2.5.3
  5679.      * function will automatically find out where the Unicode string ends.
  5680.      *
  5681.      * @param string $subData 
  5682.      * @return array 
  5683.      */
  5684.     private function _readUnicodeStringShort($subData)
  5685.     {
  5686.         $value '';
  5687.  
  5688.         // offset: 0: size: 1; length of the string (character count)
  5689.         $characterCount ord($subData[0]);
  5690.  
  5691.         $string $this->_readUnicodeString(substr($subData1)$characterCount);
  5692.  
  5693.         // add 1 for the string length
  5694.         $string['size'+= 1;
  5695.  
  5696.         return $string;
  5697.     }
  5698.  
  5699.     /**
  5700.      * Extracts an Excel Unicode long string (16-bit string length)
  5701.      * OpenOffice documentation: 2.5.3
  5702.      * this function is under construction, needs to support rich text, and Asian phonetic settings
  5703.      *
  5704.      * @param string $subData 
  5705.      * @return array 
  5706.      */
  5707.     private function _readUnicodeStringLong($subData)
  5708.     {
  5709.         $value '';
  5710.  
  5711.         // offset: 0: size: 2; length of the string (character count)
  5712.         $characterCount $this->_GetInt2d($subData0);
  5713.  
  5714.         $string $this->_readUnicodeString(substr($subData2)$characterCount);
  5715.  
  5716.         // add 2 for the string length
  5717.         $string['size'+= 2;
  5718.  
  5719.         return $string;
  5720.     }
  5721.  
  5722.     /**
  5723.      * Read Unicode string with no string length field, but with known character count
  5724.      * this function is under construction, needs to support rich text, and Asian phonetic settings
  5725.      * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3
  5726.      *
  5727.      * @param string $subData 
  5728.      * @param int $characterCount 
  5729.      * @return array 
  5730.      */
  5731.     private function _readUnicodeString($subData$characterCount)
  5732.     {
  5733.         $value '';
  5734.  
  5735.         // offset: 0: size: 1; option flags
  5736.  
  5737.             // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
  5738.             $isCompressed !((0x01 ord($subData[0])) >> 0);
  5739.  
  5740.             // bit: 2; mask: 0x04; Asian phonetic settings
  5741.             $hasAsian (0x04ord($subData[0]>> 2;
  5742.  
  5743.             // bit: 3; mask: 0x08; Rich-Text settings
  5744.             $hasRichText (0x08ord($subData[0]>> 3;
  5745.  
  5746.         // offset: 1: size: var; character array
  5747.         // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
  5748.         // needs to be fixed
  5749.         $value $this->_encodeUTF16(substr($subData1$isCompressed $characterCount $characterCount)$isCompressed);
  5750.  
  5751.         return array(
  5752.             'value' => $value,
  5753.             'size' => $isCompressed $characterCount $characterCount// the size in bytes including the option flags
  5754.         );
  5755.     }
  5756.  
  5757.     /**
  5758.      * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
  5759.      * Example:  hello"world  -->  "hello""world"
  5760.      *
  5761.      * @param string $value UTF-8 encoded string
  5762.      * @return string 
  5763.      */
  5764.     private function _UTF8toExcelDoubleQuoted($value)
  5765.     {
  5766.         return '"' str_replace('"''""'$value'"';
  5767.     }
  5768.  
  5769.     /**
  5770.      * Reads first 8 bytes of a string and return IEEE 754 float
  5771.      *
  5772.      * @param string $data Binary string that is at least 8 bytes long
  5773.      * @return float 
  5774.      */
  5775.     private function _extractNumber($data)
  5776.     {
  5777.         $rknumhigh $this->_GetInt4d($data4);
  5778.         $rknumlow $this->_GetInt4d($data0);
  5779.         $sign ($rknumhigh 0x80000000>> 31;
  5780.         $exp ($rknumhigh 0x7ff00000>> 20;
  5781.         $mantissa (0x100000 ($rknumhigh 0x000fffff));
  5782.         $mantissalow1 ($rknumlow 0x80000000>> 31;
  5783.         $mantissalow2 ($rknumlow 0x7fffffff);
  5784.         $value $mantissa pow(20 ($exp 1023)));
  5785.  
  5786.         if ($mantissalow1 != 0{
  5787.             $value += pow ((21 ($exp 1023)));
  5788.         }
  5789.  
  5790.         $value += $mantissalow2 pow ((52 ($exp 1023)));
  5791.         if ($sign{
  5792.             $value = -$value;
  5793.         }
  5794.  
  5795.         return $value;
  5796.     }
  5797.  
  5798.     private function _GetIEEE754($rknum)
  5799.     {
  5800.         if (($rknum 0x02!= 0{
  5801.             $value $rknum >> 2;
  5802.         }
  5803.         else {
  5804.             // changes by mmp, info on IEEE754 encoding from
  5805.             // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
  5806.             // The RK format calls for using only the most significant 30 bits
  5807.             // of the 64 bit floating point value. The other 34 bits are assumed
  5808.             // to be 0 so we use the upper 30 bits of $rknum as follows...
  5809.             $sign ($rknum 0x80000000>> 31;
  5810.             $exp ($rknum 0x7ff00000>> 20;
  5811.             $mantissa (0x100000 ($rknum 0x000ffffc));
  5812.             $value $mantissa pow(20($exp 1023)));
  5813.             if ($sign{
  5814.                 $value = -$value;
  5815.             }
  5816.             //end of changes by mmp
  5817.         }
  5818.         if (($rknum 0x01!= 0{
  5819.             $value /= 100;
  5820.         }
  5821.         return $value;
  5822.     }
  5823.  
  5824.     /**
  5825.      * Get UTF-8 string from (compressed or uncompressed) UTF-16 string
  5826.      *
  5827.      * @param string $string 
  5828.      * @param bool $compressed 
  5829.      * @return string 
  5830.      */
  5831.     private function _encodeUTF16($string$compressed '')
  5832.     {
  5833.         if ($compressed{
  5834.             $string $this->_uncompressByteString($string);
  5835.          }
  5836.  
  5837.         $result PHPExcel_Shared_String::ConvertEncoding($string'UTF-8''UTF-16LE');
  5838.  
  5839.         return $result;
  5840.     }
  5841.  
  5842.     /**
  5843.      * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
  5844.      *
  5845.      * @param string $string 
  5846.      * @return string 
  5847.      */
  5848.     private function _uncompressByteString($string)
  5849.     {
  5850.         $uncompressedString '';
  5851.         for ($i 0$i strlen($string)++$i{
  5852.             $uncompressedString .= $string[$i"\0";
  5853.         }
  5854.  
  5855.         return $uncompressedString;
  5856.     }
  5857.  
  5858.     /**
  5859.      * Convert string to UTF-8. Only used for BIFF5.
  5860.      *
  5861.      * @param string $string 
  5862.      * @return string 
  5863.      */
  5864.     private function _decodeCodepage($string)
  5865.     {
  5866.         $result PHPExcel_Shared_String::ConvertEncoding($string'UTF-8'$this->_codepage);
  5867.         return $result;
  5868.     }
  5869.  
  5870.     /**
  5871.      * Read 16-bit unsigned integer
  5872.      *
  5873.      * @param string $data 
  5874.      * @param int $pos 
  5875.      * @return int 
  5876.      */
  5877.     private function _GetInt2d($data$pos)
  5878.     {
  5879.         return ord($data[$pos](ord($data[$pos 1]<< 8);
  5880.     }
  5881.  
  5882.     /**
  5883.      * Read 32-bit signed integer
  5884.      *
  5885.      * @param string $data 
  5886.      * @param int $pos 
  5887.      * @return int 
  5888.      */
  5889.     private function _GetInt4d($data$pos)
  5890.     {
  5891.         //return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) |
  5892.         //    (ord($data[$pos + 2]) << 16) | (ord($data[$pos + 3]) << 24);
  5893.  
  5894.         // FIX: represent numbers correctly on 64-bit system
  5895.         // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
  5896.         $_or_24 ord($data[$pos 3]);
  5897.         if ($_or_24 >= 128{
  5898.             // negative number
  5899.             $_ord_24 = -abs((256 $_or_24<< 24);
  5900.         else {
  5901.             $_ord_24 ($_or_24 127<< 24;
  5902.         }
  5903.         return ord($data[$pos](ord($data[$pos 1]<< 8(ord($data[$pos 2]<< 16$_ord_24;
  5904.     }
  5905.  
  5906.     /**
  5907.      * Read color
  5908.      *
  5909.      * @param int $color Indexed color
  5910.      * @return array RGB color value, example: array('rgb' => 'FF0000')
  5911.      */
  5912.     private function _readColor($color)
  5913.     {
  5914.         if ($color <= 0x07 || $color >= 0x40{
  5915.             // special built-in color
  5916.             $color $this->_mapBuiltInColor($color);
  5917.         else if (isset($this->_palette&& isset($this->_palette[$color 8])) {
  5918.             // palette color, color index 0x08 maps to pallete index 0
  5919.             $color $this->_palette[$color 8];
  5920.         else {
  5921.             // default color table
  5922.             if ($this->_version == self::XLS_BIFF8{
  5923.                 $color $this->_mapColor($color);
  5924.             else {
  5925.                 // BIFF5
  5926.                 $color $this->_mapColorBIFF5($color);
  5927.             }
  5928.         }
  5929.  
  5930.         return $color;
  5931.     }
  5932.  
  5933.  
  5934.     /**
  5935.      * Map border style
  5936.      * OpenOffice documentation: 2.5.11
  5937.      *
  5938.      * @param int $index 
  5939.      * @return string 
  5940.      */
  5941.     private function _mapBorderStyle($index)
  5942.     {
  5943.         switch ($index{
  5944.             case 0x00return PHPExcel_Style_Border::BORDER_NONE;
  5945.             case 0x01return PHPExcel_Style_Border::BORDER_THIN;
  5946.             case 0x02return PHPExcel_Style_Border::BORDER_MEDIUM;
  5947.             case 0x03return PHPExcel_Style_Border::BORDER_DASHED;
  5948.             case 0x04return PHPExcel_Style_Border::BORDER_DOTTED;
  5949.             case 0x05return PHPExcel_Style_Border::BORDER_THICK;
  5950.             case 0x06return PHPExcel_Style_Border::BORDER_DOUBLE;
  5951.             case 0x07return PHPExcel_Style_Border::BORDER_HAIR;
  5952.             case 0x08return PHPExcel_Style_Border::BORDER_MEDIUMDASHED;
  5953.             case 0x09return PHPExcel_Style_Border::BORDER_DASHDOT;
  5954.             case 0x0Areturn PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT;
  5955.             case 0x0Breturn PHPExcel_Style_Border::BORDER_DASHDOTDOT;
  5956.             case 0x0Creturn PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT;
  5957.             case 0x0Dreturn PHPExcel_Style_Border::BORDER_SLANTDASHDOT;
  5958.             default:   return PHPExcel_Style_Border::BORDER_NONE;
  5959.         }
  5960.     }
  5961.  
  5962.     /**
  5963.      * Get fill pattern from index
  5964.      * OpenOffice documentation: 2.5.12
  5965.      *
  5966.      * @param int $index 
  5967.      * @return string 
  5968.      */
  5969.     private function _mapFillPattern($index)
  5970.     {
  5971.         switch ($index{
  5972.             case 0x00return PHPExcel_Style_Fill::FILL_NONE;
  5973.             case 0x01return PHPExcel_Style_Fill::FILL_SOLID;
  5974.             case 0x02return PHPExcel_Style_Fill::FILL_PATTERN_MEDIUMGRAY;
  5975.             case 0x03return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRAY;
  5976.             case 0x04return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRAY;
  5977.             case 0x05return PHPExcel_Style_Fill::FILL_PATTERN_DARKHORIZONTAL;
  5978.             case 0x06return PHPExcel_Style_Fill::FILL_PATTERN_DARKVERTICAL;
  5979.             case 0x07return PHPExcel_Style_Fill::FILL_PATTERN_DARKDOWN;
  5980.             case 0x08return PHPExcel_Style_Fill::FILL_PATTERN_DARKUP;
  5981.             case 0x09return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRID;
  5982.             case 0x0Areturn PHPExcel_Style_Fill::FILL_PATTERN_DARKTRELLIS;
  5983.             case 0x0Breturn PHPExcel_Style_Fill::FILL_PATTERN_LIGHTHORIZONTAL;
  5984.             case 0x0Creturn PHPExcel_Style_Fill::FILL_PATTERN_LIGHTVERTICAL;
  5985.             case 0x0Dreturn PHPExcel_Style_Fill::FILL_PATTERN_LIGHTDOWN;
  5986.             case 0x0Ereturn PHPExcel_Style_Fill::FILL_PATTERN_LIGHTUP;
  5987.             case 0x0Freturn PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRID;
  5988.             case 0x10return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTTRELLIS;
  5989.             case 0x11return PHPExcel_Style_Fill::FILL_PATTERN_GRAY125;
  5990.             case 0x12return PHPExcel_Style_Fill::FILL_PATTERN_GRAY0625;
  5991.             default:   return PHPExcel_Style_Fill::FILL_NONE;
  5992.         }
  5993.     }
  5994.  
  5995.     /**
  5996.      * Map error code, e.g. '#N/A'
  5997.      *
  5998.      * @param int $subData 
  5999.      * @return string 
  6000.      */
  6001.     private function _mapErrorCode($subData)
  6002.     {
  6003.         switch ($subData{
  6004.             case 0x00return '#NULL!';        break;
  6005.             case 0x07return '#DIV/0!';    break;
  6006.             case 0x0Freturn '#VALUE!';    break;
  6007.             case 0x17return '#REF!';        break;
  6008.             case 0x1Dreturn '#NAME?';        break;
  6009.             case 0x24return '#NUM!';        break;
  6010.             case 0x2Areturn '#N/A';        break;
  6011.             defaultreturn false;
  6012.         }
  6013.     }
  6014.  
  6015.     /**
  6016.      * Map built-in color to RGB value
  6017.      *
  6018.      * @param int $color Indexed color
  6019.      * @return array 
  6020.      */
  6021.     private function _mapBuiltInColor($color)
  6022.     {
  6023.         switch ($color{
  6024.             case 0x00return array('rgb' => '000000');
  6025.             case 0x01return array('rgb' => 'FFFFFF');
  6026.             case 0x02return array('rgb' => 'FF0000');
  6027.             case 0x03return array('rgb' => '00FF00');
  6028.             case 0x04return array('rgb' => '0000FF');
  6029.             case 0x05return array('rgb' => 'FFFF00');
  6030.             case 0x06return array('rgb' => 'FF00FF');
  6031.             case 0x07return array('rgb' => '00FFFF');
  6032.             case 0x40return array('rgb' => '000000')// system window text color
  6033.             case 0x41return array('rgb' => 'FFFFFF')// system window background color
  6034.             default:   return array('rgb' => '000000');
  6035.         }
  6036.     }
  6037.  
  6038.     /**
  6039.      * Map color array from BIFF5 built-in color index
  6040.      *
  6041.      * @param int $subData 
  6042.      * @return array 
  6043.      */
  6044.     private function _mapColorBIFF5($subData)
  6045.     {
  6046.         switch ($subData{
  6047.             case 0x08return array('rgb' => '000000');
  6048.             case 0x09return array('rgb' => 'FFFFFF');
  6049.             case 0x0Areturn array('rgb' => 'FF0000');
  6050.             case 0x0Breturn array('rgb' => '00FF00');
  6051.             case 0x0Creturn array('rgb' => '0000FF');
  6052.             case 0x0Dreturn array('rgb' => 'FFFF00');
  6053.             case 0x0Ereturn array('rgb' => 'FF00FF');
  6054.             case 0x0Freturn array('rgb' => '00FFFF');
  6055.             case 0x10return array('rgb' => '800000');
  6056.             case 0x11return array('rgb' => '008000');
  6057.             case 0x12return array('rgb' => '000080');
  6058.             case 0x13return array('rgb' => '808000');
  6059.             case 0x14return array('rgb' => '800080');
  6060.             case 0x15return array('rgb' => '008080');
  6061.             case 0x16return array('rgb' => 'C0C0C0');
  6062.             case 0x17return array('rgb' => '808080');
  6063.             case 0x18return array('rgb' => '8080FF');
  6064.             case 0x19return array('rgb' => '802060');
  6065.             case 0x1Areturn array('rgb' => 'FFFFC0');
  6066.             case 0x1Breturn array('rgb' => 'A0E0F0');
  6067.             case 0x1Creturn array('rgb' => '600080');
  6068.             case 0x1Dreturn array('rgb' => 'FF8080');
  6069.             case 0x1Ereturn array('rgb' => '0080C0');
  6070.             case 0x1Freturn array('rgb' => 'C0C0FF');
  6071.             case 0x20return array('rgb' => '000080');
  6072.             case 0x21return array('rgb' => 'FF00FF');
  6073.             case 0x22return array('rgb' => 'FFFF00');
  6074.             case 0x23return array('rgb' => '00FFFF');
  6075.             case 0x24return array('rgb' => '800080');
  6076.             case 0x25return array('rgb' => '800000');
  6077.             case 0x26return array('rgb' => '008080');
  6078.             case 0x27return array('rgb' => '0000FF');
  6079.             case 0x28return array('rgb' => '00CFFF');
  6080.             case 0x29return array('rgb' => '69FFFF');
  6081.             case 0x2Areturn array('rgb' => 'E0FFE0');
  6082.             case 0x2Breturn array('rgb' => 'FFFF80');
  6083.             case 0x2Creturn array('rgb' => 'A6CAF0');
  6084.             case 0x2Dreturn array('rgb' => 'DD9CB3');
  6085.             case 0x2Ereturn array('rgb' => 'B38FEE');
  6086.             case 0x2Freturn array('rgb' => 'E3E3E3');
  6087.             case 0x30return array('rgb' => '2A6FF9');
  6088.             case 0x31return array('rgb' => '3FB8CD');
  6089.             case 0x32return array('rgb' => '488436');
  6090.             case 0x33return array('rgb' => '958C41');
  6091.             case 0x34return array('rgb' => '8E5E42');
  6092.             case 0x35return array('rgb' => 'A0627A');
  6093.             case 0x36return array('rgb' => '624FAC');
  6094.             case 0x37return array('rgb' => '969696');
  6095.             case 0x38return array('rgb' => '1D2FBE');
  6096.             case 0x39return array('rgb' => '286676');
  6097.             case 0x3Areturn array('rgb' => '004500');
  6098.             case 0x3Breturn array('rgb' => '453E01');
  6099.             case 0x3Creturn array('rgb' => '6A2813');
  6100.             case 0x3Dreturn array('rgb' => '85396A');
  6101.             case 0x3Ereturn array('rgb' => '4A3285');
  6102.             case 0x3Freturn array('rgb' => '424242');
  6103.             default:   return array('rgb' => '000000');
  6104.         }
  6105.     }
  6106.  
  6107.     /**
  6108.      * Map color array from BIFF8 built-in color index
  6109.      *
  6110.      * @param int $subData 
  6111.      * @return array 
  6112.      */
  6113.     private function _mapColor($subData)
  6114.     {
  6115.         switch ($subData{
  6116.             case 0x08return array('rgb' => '000000');
  6117.             case 0x09return array('rgb' => 'FFFFFF');
  6118.             case 0x0Areturn array('rgb' => 'FF0000');
  6119.             case 0x0Breturn array('rgb' => '00FF00');
  6120.             case 0x0Creturn array('rgb' => '0000FF');
  6121.             case 0x0Dreturn array('rgb' => 'FFFF00');
  6122.             case 0x0Ereturn array('rgb' => 'FF00FF');
  6123.             case 0x0Freturn array('rgb' => '00FFFF');
  6124.             case 0x10return array('rgb' => '800000');
  6125.             case 0x11return array('rgb' => '008000');
  6126.             case 0x12return array('rgb' => '000080');
  6127.             case 0x13return array('rgb' => '808000');
  6128.             case 0x14return array('rgb' => '800080');
  6129.             case 0x15return array('rgb' => '008080');
  6130.             case 0x16return array('rgb' => 'C0C0C0');
  6131.             case 0x17return array('rgb' => '808080');
  6132.             case 0x18return array('rgb' => '9999FF');
  6133.             case 0x19return array('rgb' => '993366');
  6134.             case 0x1Areturn array('rgb' => 'FFFFCC');
  6135.             case 0x1Breturn array('rgb' => 'CCFFFF');
  6136.             case 0x1Creturn array('rgb' => '660066');
  6137.             case 0x1Dreturn array('rgb' => 'FF8080');
  6138.             case 0x1Ereturn array('rgb' => '0066CC');
  6139.             case 0x1Freturn array('rgb' => 'CCCCFF');
  6140.             case 0x20return array('rgb' => '000080');
  6141.             case 0x21return array('rgb' => 'FF00FF');
  6142.             case 0x22return array('rgb' => 'FFFF00');
  6143.             case 0x23return array('rgb' => '00FFFF');
  6144.             case 0x24return array('rgb' => '800080');
  6145.             case 0x25return array('rgb' => '800000');
  6146.             case 0x26return array('rgb' => '008080');
  6147.             case 0x27return array('rgb' => '0000FF');
  6148.             case 0x28return array('rgb' => '00CCFF');
  6149.             case 0x29return array('rgb' => 'CCFFFF');
  6150.             case 0x2Areturn array('rgb' => 'CCFFCC');
  6151.             case 0x2Breturn array('rgb' => 'FFFF99');
  6152.             case 0x2Creturn array('rgb' => '99CCFF');
  6153.             case 0x2Dreturn array('rgb' => 'FF99CC');
  6154.             case 0x2Ereturn array('rgb' => 'CC99FF');
  6155.             case 0x2Freturn array('rgb' => 'FFCC99');
  6156.             case 0x30return array('rgb' => '3366FF');
  6157.             case 0x31return array('rgb' => '33CCCC');
  6158.             case 0x32return array('rgb' => '99CC00');
  6159.             case 0x33return array('rgb' => 'FFCC00');
  6160.             case 0x34return array('rgb' => 'FF9900');
  6161.             case 0x35return array('rgb' => 'FF6600');
  6162.             case 0x36return array('rgb' => '666699');
  6163.             case 0x37return array('rgb' => '969696');
  6164.             case 0x38return array('rgb' => '003366');
  6165.             case 0x39return array('rgb' => '339966');
  6166.             case 0x3Areturn array('rgb' => '003300');
  6167.             case 0x3Breturn array('rgb' => '333300');
  6168.             case 0x3Creturn array('rgb' => '993300');
  6169.             case 0x3Dreturn array('rgb' => '993366');
  6170.             case 0x3Ereturn array('rgb' => '333399');
  6171.             case 0x3Freturn array('rgb' => '333333');
  6172.             default:   return array('rgb' => '000000');
  6173.         }
  6174.     }
  6175.  
  6176. }

Documentation generated on Tue, 01 Jun 2010 17:03:32 +0200 by phpDocumentor 1.4.3