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

Documentation generated on Mon, 10 Aug 2009 08:03:52 +0200 by phpDocumentor 1.4.1