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

Source for file Excel5.php

Documentation is available at Excel5.php

  1. <?php
  2. /**
  3.  * PHPExcel
  4.  *
  5.  * Copyright (c) 2006 - 2008 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 tshhe 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
  23.  * @copyright  Copyright (c) 2006 - 2008 PHPExcel (http://www.codeplex.com/PHPExcel)
  24.  * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
  25.  * @version    1.6.4, 2008-10-27
  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 */
  61. require_once 'PHPExcel.php';
  62.  
  63. /** PHPExcel_Reader_IReader */
  64. require_once 'PHPExcel/Reader/IReader.php';
  65.  
  66. /** PHPExcel_Shared_OLERead */
  67. require_once 'PHPExcel/Shared/OLERead.php';
  68.  
  69. /** PHPExcel_Cell */
  70. require_once 'PHPExcel/Cell.php';
  71.  
  72. /** PHPExcel_Reader_IReadFilter */
  73. require_once 'PHPExcel/Reader/IReadFilter.php';
  74.  
  75. /** PHPExcel_Reader_DefaultReadFilter */
  76. require_once 'PHPExcel/Reader/DefaultReadFilter.php';
  77.  
  78.  
  79. /**
  80.  * PHPExcel_Reader_Excel5
  81.  *
  82.  * This class uses {@link http://sourceforge.net/projects/phpexcelreader/parseXL}
  83.  *
  84.  * @category   PHPExcel
  85.  * @package    PHPExcel_Reader
  86.  * @copyright  Copyright (c) 2006 - 2008 PHPExcel (http://www.codeplex.com/PHPExcel)
  87.  */
  88. class PHPExcel_Reader_Excel5 implements PHPExcel_Reader_IReader
  89. {
  90.     // ParseXL definitions
  91.     const XLS_BIFF8                        0x0600;
  92.     const XLS_BIFF7                        0x0500;
  93.     const XLS_WorkbookGlobals            0x0005;
  94.     const XLS_Worksheet                    0x0010;
  95.  
  96.     // record identifiers
  97.     const XLS_Type_FORMULA                0x0006;
  98.     const XLS_Type_EOF                    0x000a;
  99.     const XLS_Type_PROTECT                0x0012;
  100.     const XLS_Type_PASSWORD                0x0013;
  101.     const XLS_Type_HEADER                0x0014;
  102.     const XLS_Type_FOOTER                0x0015;
  103.     const XLS_Type_EXTSHEET                0x0017;
  104.     const XLS_Type_NAME                    0x0018;
  105.     const XLS_Type_VERTICALPAGEBREAKS    0x001a;
  106.     const XLS_Type_HORIZONTALPAGEBREAKS    0x001b;
  107.     const XLS_Type_NOTE                    0x001c;
  108.     const XLS_Type_NINETEENFOUR            0x0022;
  109.     const XLS_Type_LEFTMARGIN            0x0026;
  110.     const XLS_Type_RIGHTMARGIN            0x0027;
  111.     const XLS_Type_TOPMARGIN            0x0028;
  112.     const XLS_Type_BOTTOMMARGIN            0x0029;
  113.     const XLS_Type_FILEPASS                0x002f;
  114.     const XLS_Type_FONT                    0x0031;
  115.     const XLS_Type_CONTINUE                0x003c;
  116.     const XLS_Type_CODEPAGE                0x0042;
  117.     const XLS_Type_DEFCOLWIDTH             0x0055;
  118.     const XLS_Type_COLINFO                0x007d;
  119.     const XLS_Type_IMDATA                0x007f;
  120.     const XLS_Type_SHEETPR                0x0081;
  121.     const XLS_Type_HCENTER                0x0083;
  122.     const XLS_Type_VCENTER                0x0084;
  123.     const XLS_Type_BOUNDSHEET            0x0085;
  124.     const XLS_Type_PAGESETUP            0x00a1;
  125.     const XLS_Type_MULRK                0x00bd;
  126.     const XLS_Type_MULBLANK                0x00be;
  127.     const XLS_Type_DBCELL                0x00d7;
  128.     const XLS_Type_XF                    0x00e0;
  129.     const XLS_Type_MERGEDCELLS            0x00e5;
  130.     const XLS_Type_SST                    0x00fc;
  131.     const XLS_Type_LABELSST                0x00fd;
  132.     const XLS_Type_EXTSST                0x00ff;
  133.     const XLS_Type_EXTERNALBOOK            0x01ae;
  134.     const XLS_Type_TXO                    0x01b6;
  135.     const XLS_Type_HYPERLINK            0x01b8;
  136.     const XLS_Type_DIMENSION            0x0200;
  137.     const XLS_Type_BLANK                0x0201;
  138.     const XLS_Type_NUMBER                0x0203;
  139.     const XLS_Type_LABEL                0x0204;
  140.     const XLS_Type_BOOLERR                0x0205;
  141.     const XLS_Type_STRING                0x0207;
  142.     const XLS_Type_ROW                    0x0208;
  143.     const XLS_Type_INDEX                0x020b;
  144.     const XLS_Type_ARRAY                0x0221;
  145.     const XLS_Type_DEFAULTROWHEIGHT     0x0225;
  146.     const XLS_Type_RK                    0x027e;
  147.     const XLS_Type_FORMAT                0x041e;
  148.     const XLS_Type_BOF                    0x0809;
  149.     const XLS_Type_UNKNOWN                0xffff;
  150.  
  151.     /**
  152.      * Read data only?
  153.      *
  154.      * @var boolean 
  155.      */
  156.     private $_readDataOnly = false;
  157.  
  158.     /**
  159.      * Restict which sheets should be loaded?
  160.      *
  161.      * @var array 
  162.      */
  163.     private $_loadSheetsOnly = null;
  164.  
  165.     /**
  166.      * PHPExcel_Reader_IReadFilter instance
  167.      *
  168.      * @var PHPExcel_Reader_IReadFilter 
  169.      */
  170.     private $_readFilter = null;
  171.  
  172.     /**
  173.      * OLE reader
  174.      *
  175.      * @var PHPExcel_Shared_OLERead 
  176.      */
  177.     private $_ole;
  178.  
  179.     /**
  180.      * Stream data that is read. Includes workbook globals substream as well as sheet substreams
  181.      *
  182.      * @var string 
  183.      */
  184.     private $_data;
  185.  
  186.     /**
  187.      * Current position in stream
  188.      *
  189.      * @var integer 
  190.      */
  191.     private $_pos;
  192.  
  193.     /**
  194.      * Workbook to be returned by the reader.
  195.      *
  196.      * @var PHPExcel 
  197.      */
  198.     private $_excel;
  199.  
  200.     /**
  201.      * Worksheet that is currently being built by the reader.
  202.      *
  203.      * @var PHPExcel_Worksheet 
  204.      */
  205.     private $_sheet;
  206.  
  207.     /**
  208.      * BIFF version
  209.      *
  210.      * @var int 
  211.      */
  212.     private $_version;
  213.  
  214.     /**
  215.      * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
  216.      * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'
  217.      *
  218.      * @var string 
  219.      */
  220.     private $_codepage;
  221.  
  222.     /**
  223.      * Shared fonts
  224.      *
  225.      * @var array 
  226.      */
  227.     private $_fonts = array();
  228.  
  229.     /**
  230.      * Shared formats
  231.      *
  232.      * @var array 
  233.      */
  234.     private $_formats = array();
  235.  
  236.     /**
  237.      * Shared styles
  238.      *
  239.      * @var array 
  240.      */
  241.     private $_xf = array();
  242.  
  243.     /**
  244.      * Worksheets
  245.      *
  246.      * @var array 
  247.      */
  248.     private $_boundsheets = array();
  249.  
  250.     /**
  251.      * External books
  252.      *
  253.      * @var array 
  254.      */
  255.     private $_externalBooks = array();
  256.  
  257.     /**
  258.      * REF structures. Only applies to BIFF8.
  259.      *
  260.      * @var array 
  261.      */
  262.     private $_ref = array();
  263.  
  264.     /**
  265.      * Defined names
  266.      *
  267.      * @var array 
  268.      */
  269.     private $_definedname = array();
  270.  
  271.     /**
  272.      * Shared strings. Only applies to BIFF8.
  273.      *
  274.      * @var array 
  275.      */
  276.     private $_sst = array();
  277.  
  278.  
  279.     // to be removed
  280.     // dvc: added for external sheets references
  281.     private $_extshref = array();
  282.  
  283.     // dvc: added list of names and their sheet associated indexes
  284.     //private $_namedcells = array();
  285.  
  286.     /**
  287.      * Read data only?
  288.      *
  289.      * @return boolean 
  290.      */
  291.     public function getReadDataOnly()
  292.     {
  293.         return $this->_readDataOnly;
  294.     }
  295.  
  296.     /**
  297.      * Set read data only
  298.      *
  299.      * @param boolean $pValue 
  300.      */
  301.     public function setReadDataOnly($pValue false)
  302.     {
  303.         $this->_readDataOnly = $pValue;
  304.     }
  305.  
  306.     /**
  307.      * Get which sheets to load
  308.      *
  309.      * @return mixed 
  310.      */
  311.     public function getLoadSheetsOnly()
  312.     {
  313.         return $this->_loadSheetsOnly;
  314.     }
  315.  
  316.     /**
  317.      * Set which sheets to load
  318.      *
  319.      * @param mixed $value 
  320.      */
  321.     public function setLoadSheetsOnly($value null)
  322.     {
  323.         $this->_loadSheetsOnly = is_array($value?
  324.             $value array($value);
  325.     }
  326.  
  327.     /**
  328.      * Set all sheets to load
  329.      */
  330.     public function setLoadAllSheets()
  331.     {
  332.         $this->_loadSheetsOnly = null;
  333.     }
  334.  
  335.     /**
  336.      * Read filter
  337.      *
  338.      * @return PHPExcel_Reader_IReadFilter 
  339.      */
  340.     public function getReadFilter({
  341.         return $this->_readFilter;
  342.     }
  343.  
  344.     /**
  345.      * Set read filter
  346.      *
  347.      * @param PHPExcel_Reader_IReadFilter $pValue 
  348.      */
  349.     public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue{
  350.         $this->_readFilter = $pValue;
  351.     }
  352.  
  353.     /**
  354.      * Create a new PHPExcel_Reader_Excel5 instance
  355.      */
  356.     public function __construct({
  357.         $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
  358.     }
  359.  
  360.     /**
  361.      * Loads PHPExcel from file
  362.      *
  363.      * @param     string         $pFilename 
  364.      * @throws     Exception
  365.      */
  366.     public function load($pFilename)
  367.     {
  368.         // Check if file exists
  369.         if (!file_exists($pFilename)) {
  370.             throw new Exception("Could not open " $pFilename " for reading! File does not exist.");
  371.         }
  372.  
  373.         // Initialisations
  374.         $this->_excel = new PHPExcel;
  375.         $this->_excel->removeSheetByIndex(0);
  376.  
  377.         // Use ParseXL for the hard work.
  378.         $this->_ole = new PHPExcel_Shared_OLERead();
  379.  
  380.         $this->_encoderFunction function_exists('mb_convert_encoding'?
  381.             'mb_convert_encoding' 'iconv';
  382.  
  383.         // get excel data
  384.         $res $this->_ole->read($pFilename);
  385.  
  386.         // oops, something goes wrong (Darko Miljanovic)
  387.         if($res === false// check error code
  388.             if($this->_ole->error == 1// bad file
  389.                 throw new Exception('The filename ' $pFilename ' is not readable');
  390.             elseif($this->_ole->error == 2{
  391.                 throw new Exception('The filename ' $pFilename ' is not recognised as an Excel file');
  392.             }
  393.             // check other error codes here (eg bad fileformat, etc...)
  394.         }
  395.  
  396.         $this->_data = $this->_ole->getWorkBook();
  397.         $this->_pos = 0;
  398.  
  399.         // Parse workbook
  400.         while (true{
  401.             $code $this->_GetInt2d($this->_data$this->_pos);
  402.  
  403.             switch ($code{
  404.  
  405.             case self::XLS_Type_BOF:
  406.                 $pos $this->_pos;
  407.                 $length $this->_GetInt2d($this->_data$pos 2);
  408.                 $recordData substr($this->_data$pos 4$length);
  409.                 // offset: 0; size: 2; BIFF version
  410.                 $version $this->_GetInt2d($this->_data$pos 4);
  411.                 $this->_version = $version;
  412.                 if (($version != self::XLS_BIFF8&& ($version != self::XLS_BIFF7)) {
  413.                     return false;
  414.                 }
  415.                 // offset: 2; size: 2; type of stream
  416.                 $substreamType $this->_GetInt2d($this->_data$pos 6);
  417.                 if ($substreamType != self::XLS_WorkbookGlobals{
  418.                     return false;
  419.                 }
  420.                 $this->_pos += $length;
  421.                 break;
  422.  
  423.             /**
  424.              * SHEETPROTECTION
  425.              *
  426.              * This record is part of the File Protection Block. It
  427.              * contains information about the read/write password of the
  428.              * file. All record contents following this record will be
  429.              * encrypted.
  430.              *
  431.              * --    "OpenOffice.org's Documentation of the Microsoft
  432.              *         Excel File Format"
  433.              */
  434.             case self::XLS_Type_FILEPASS:
  435.                 $pos $this->_pos;
  436.                 $length $this->_GetInt2d($this->_data$pos 2);
  437.                 $recordData substr($this->_data$pos 4$length);
  438.                 $this->_pos += $length;
  439.                 throw new Exception('Cannot read encrypted file');
  440.                 break;
  441.  
  442.             /**
  443.              * CODEPAGE
  444.              *
  445.              * This record stores the text encoding used to write byte
  446.              * strings, stored as MS Windows code page identifier.
  447.              *
  448.              * --    "OpenOffice.org's Documentation of the Microsoft
  449.              *         Excel File Format"
  450.              */
  451.             case self::XLS_Type_CODEPAGE:
  452.                 $this->_readCodepage();
  453.                 break;
  454.  
  455.             /**
  456.              * DATEMODE
  457.              *
  458.              * This record specifies the base date for displaying date
  459.              * values. All dates are stored as count of days past this
  460.              * base date. In BIFF2-BIFF4 this record is part of the
  461.              * Calculation Settings Block. In BIFF5-BIFF8 it is
  462.              * stored in the Workbook Globals Substream.
  463.              *
  464.              * --    "OpenOffice.org's Documentation of the Microsoft
  465.              *         Excel File Format"
  466.              */
  467.             case self::XLS_Type_NINETEENFOUR:
  468.                 $pos $this->_pos;
  469.                 $length $this->_GetInt2d($this->_data$pos 2);
  470.                 $recordData substr($this->_data$pos 4$length);
  471.                 $this->_nineteenFour (ord($this->_data[$pos 4]== 1);
  472.  
  473.                 /*
  474.                 if (ord($this->_data[$pos + 4]) == 1) {
  475.                     PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_MAC_1904);
  476.                 } else {
  477.                     PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900);
  478.                 }
  479.                 */
  480.                 $this->_pos += $length;
  481.                 break;
  482.  
  483.             case self::XLS_Type_FONT:
  484.                 $this->_readFont();
  485.                 break;
  486.  
  487.             /**
  488.              * FORMAT
  489.              *
  490.              * This record contains information about a number format.
  491.              * All FORMAT records occur together in a sequential list.
  492.              *
  493.              * In BIFF2-BIFF4 other records referencing a FORMAT record
  494.              * contain a zero-based index into this list. From BIFF5 on
  495.              * the FORMAT record contains the index itself that will be
  496.              * used by other records.
  497.              *
  498.              * --    "OpenOffice.org's Documentation of the Microsoft
  499.              *         Excel File Format"
  500.              */
  501.             case self::XLS_Type_FORMAT:
  502.                 $this->_readFormat();
  503.                 break;
  504.  
  505.             /**
  506.              * XF - Extended Format
  507.              *
  508.              * This record contains formatting information for cells,
  509.              * rows, columns or styles.
  510.              *
  511.              * --    "OpenOffice.org's Documentation of the Microsoft
  512.              *         Excel File Format"
  513.              */
  514.             case self::XLS_Type_XF:
  515.                 $this->_readXf();
  516.                 break;
  517.  
  518.             /**
  519.              * SHEET
  520.              *
  521.              * This record is  located in the  Workbook Globals
  522.              * Substream  and represents a sheet inside the workbook.
  523.              * One SHEET record is written for each sheet. It stores the
  524.              * sheet name and a stream offset to the BOF record of the
  525.              * respective Sheet Substream within the Workbook Stream.
  526.              *
  527.              * --    "OpenOffice.org's Documentation of the Microsoft
  528.              *         Excel File Format"
  529.              */
  530.             case self::XLS_Type_BOUNDSHEET:
  531.                 $pos $this->_pos;
  532.                 $length $this->_GetInt2d($this->_data$pos 2);
  533.                 $recordData substr($this->_data$pos 4$length);
  534.                 // offset: 0; size: 4; absolute stream position of the BOF record of the sheet
  535.                 $rec_offset $this->_GetInt4d($this->_data$pos 4);
  536.                 // offset: 4; size: 1; sheet state
  537.                 $rec_typeFlag ord($this->_data[$pos 8]);
  538.                 // offset: 5; size: 1; sheet type
  539.                 $rec_visibilityFlag ord($this->_data[$pos 9]);
  540.                 // offset: 6; size: var; sheet name
  541.                 if ($version == self::XLS_BIFF8{
  542.                     $string $this->_readUnicodeStringShort(substr($recordData6));
  543.                     $rec_name $string['value'];
  544.                 elseif ($version == self::XLS_BIFF7{
  545.                     $string $this->_readByteStringShort(substr($recordData6));
  546.                     $rec_name $string['value'];
  547.                 }
  548.                 $this->_boundsheets[array(
  549.                     'name' => $rec_name,
  550.                     'offset' => $rec_offset
  551.                 );
  552.                 $this->_pos += $length;
  553.                 break;
  554.  
  555.             case self::XLS_Type_EXTERNALBOOK:
  556.                 $pos $this->_pos;
  557.                 $length $this->_GetInt2d($this->_data$pos 2);
  558.                 $recordData substr($this->_data$pos 4$length);
  559.  
  560.                 $offset 0;
  561.                 // there are 4 types of records
  562.                 if (strlen($recordData4{
  563.                     // external reference
  564.                     // offset: 0; size: 2; number of sheet names ($nm)
  565.                     $nm $this->_GetInt2d($recordData0);
  566.                     $offset += 2;
  567.  
  568.                     // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
  569.                     $encodedUrlString $this->_readUnicodeStringLong(substr($recordData2));
  570.                     $offset += $encodedUrlString['size'];
  571.  
  572.                     // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
  573.                     $externalSheetNames array();
  574.                     for ($i 0$i $nm++$i{
  575.                         $externalSheetNameString $this->_readUnicodeStringLong(substr($recordData$offset));
  576.                         $externalSheetNames[$externalSheetNameString['value'];
  577.                         $offset += $externalSheetNameString['size'];
  578.                     }
  579.  
  580.                     // store the record data
  581.                     $this->_externalBooks[array(
  582.                         'type' => 'external',
  583.                         'encodedUrl' => $encodedUrlString['value'],
  584.                         'externalSheetNames' => $externalSheetNames,
  585.                     );
  586.  
  587.                 elseif (substr($recordData22== pack('CC'0x010x04)) {
  588.                     // internal reference
  589.                     // offset: 0; size: 2; number of sheet in this document
  590.                     // offset: 2; size: 2; 0x01 0x04
  591.                     $this->_externalBooks[array(
  592.                         'type' => 'internal',
  593.                     );
  594.                 elseif (substr($recordData04== pack('VCC'0x00010x010x3A)) {
  595.                     // add-in function
  596.                     // offset: 0; size: 2; 0x0001
  597.                     $this->_externalBooks[array(
  598.                         'type' => 'addInFunction',
  599.                     );
  600.                 elseif (substr($recordData02== pack('V'0x0000)) {
  601.                     // DDE links, OLE links
  602.                     // offset: 0; size: 2; 0x0000
  603.                     // offset: 2; size: var; encoded source document name
  604.                     $this->_externalBooks[array(
  605.                         'type' => 'DDEorOLE',
  606.                     );
  607.                 }
  608.  
  609.                 $this->_pos += $length;
  610.                 break;
  611.  
  612.             case self::XLS_Type_EXTSHEET:
  613.                 $pos $this->_pos;
  614.                 $length $this->_GetInt2d($this->_data$pos 2);
  615.                 $recordData substr($this->_data$pos 4$length);
  616.                 // external sheet references provided for named cells
  617.                 if ($version == self::XLS_BIFF8{
  618.                     $xpos $pos 4;
  619.                     $xcnt $this->_GetInt2d($this->_data$xpos);
  620.                     for ($x 0$x $xcnt++$x{
  621.                         $this->_extshref[$x$this->_GetInt2d($this->_data$xpos $x);
  622.                     }
  623.                 }
  624.  
  625.                 // this if statement is going to replace the above one later
  626.                 if ($version == self::XLS_BIFF8{
  627.                     // offset: 0; size: 2; number of following ref structures
  628.                     $nm $this->_GetInt2d($recordData0);
  629.                     for ($i 0$i $nm++$i{
  630.                         $this->_ref[array(
  631.                             // offset: 2 + 6 * $i; index to EXTERNALBOOK record
  632.                             'externalBookIndex' => $this->_GetInt2d($recordData$i),
  633.                             // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
  634.                             'firstSheetIndex' => $this->_GetInt2d($recordData$i),
  635.                             // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
  636.                             'lastSheetIndex' => $this->_GetInt2d($recordData$i),
  637.                         );
  638.                     }
  639.                 }
  640.                 $this->_pos += $length;
  641.                 break;
  642.  
  643.             /**
  644.              * DEFINEDNAME
  645.              *
  646.              * This record is part of a Link Table. It contains the name
  647.              * and the token array of an internal defined name. Token
  648.              * arrays of defined names contain tokens with aberrant
  649.              * token classes.
  650.              *
  651.              * --    "OpenOffice.org's Documentation of the Microsoft
  652.              *         Excel File Format"
  653.              */
  654.             case self::XLS_Type_NAME:
  655.                 $pos $this->_pos;
  656.                 $length $this->_GetInt2d($this->_data$pos 2);
  657.                 $recordData substr($this->_data$pos 4$length);
  658.                 if ($this->_version == self::XLS_BIFF8{
  659.                     // retrieves named cells
  660.                     $npos $pos 4;
  661.                     // offset: 0; size: 2; option flags
  662.                     $opts $this->_GetInt2d($this->_data$npos);
  663.                         // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
  664.                         $isBuiltInName (0x0020 $opts>> 5;
  665.                     // offset: 2; size: 1; keyboard shortcut
  666.                     // offset: 3; size: 1; length of the name (character count)
  667.                     $nlen ord($this->_data[$npos 3]);
  668.                     // offset: 4; size: 2; size of the formula data
  669.                     $flen $this->_GetInt2d($this->_data$npos 4);
  670.  
  671.                     // offset: 14; size: var; Name (Unicode string without length field)
  672.                     //$nstr = substr($this->_data, $npos + 15, $nlen);
  673.                     $string $this->_readUnicodeString(substr($recordData14)$nlen);
  674.  
  675.                     // offset: var; size: $flen; formula data
  676.                     $offset 14 $string['size'];
  677.                     $formulaStructure pack('v'$flensubstr($recordData$offset$flen);
  678.  
  679.                     /*
  680.                     $fpos = $npos + 14 + 1 + $nlen;
  681.                     $ftoken = ord($this->_data[$fpos]);
  682.                     if ($ftoken == 0x3A && $opts == 0 && $flen == 7) {
  683.                         // then we have BIFF8
  684.  
  685.                         // index to REF entry in EXTERNSHEET record
  686.                         $xref = $this->_GetInt2d($this->_data, $fpos + 1);
  687.  
  688.                         // encoded cell address
  689.                         $frow = $this->_GetInt2d($this->_data, $fpos + 3);
  690.                         $fcol = ord($this->_data[$fpos + 5]);
  691.                         if (array_key_exists($xref,$this->_extshref)) {
  692.                             $fsheet = $this->_extshref[$xref];
  693.                             var_dump($fsheet);
  694.                         } else {
  695.                             $fsheet = '';
  696.                         }
  697.                         $this->_namedcells[$nstr] = array(
  698.                             'sheet' => $fsheet,
  699.                             'row' => $frow,
  700.                             'column' => $fcol
  701.                         );
  702.                     }
  703.                     */
  704.  
  705.                     try {
  706.                         $formula $this->_getFormulaFromStructure($formulaStructure);
  707.                     catch (Exception $e{
  708.                         $formula '';
  709.                     }
  710.  
  711.                     $this->_definedname[array(
  712.                         'isBuiltInName' => $isBuiltInName,
  713.                         'name' => $string['value'],
  714.                         'formula' => $formula,
  715.                     );
  716.                 }
  717.  
  718.                 $this->_pos += $length;
  719.                 break;
  720.  
  721.             /**
  722.              * SST - Shared String Table
  723.              *
  724.              * This record contains a list of all strings used anywhere
  725.              * in the workbook. Each string occurs only once. The
  726.              * workbook uses indexes into the list to reference the
  727.              * strings.
  728.              *
  729.              * --    "OpenOffice.org's Documentation of the Microsoft
  730.              *         Excel File Format"
  731.              **/
  732.             case self::XLS_Type_SST:
  733.                 $pos $this->_pos;
  734.                 $length $this->_GetInt2d($this->_data$pos 2);
  735.                 $recordData substr($this->_data$pos 4$length);
  736.                 $spos $pos 4;
  737.                 // last position in record data
  738.                 $limitpos $spos $length;
  739.                 // offset: 0; size: 4; total number of strings in the workbook
  740.                 // offset: 4; size: 4; number of following strings ($nm)
  741.                 $nm $this->_GetInt4d($this->_data$spos 4);
  742.                 $spos += 8;
  743.                 // loop through the Unicode strings (16-bit length)
  744.                 for ($i 0$i $nm++$i{
  745.                     if ($spos == $limitpos{
  746.                         // then we have reached end of SST record data
  747.                         $opcode $this->_GetInt2d($this->_data$spos);
  748.                         $conlength $this->_GetInt2d($this->_data$spos 2);
  749.                         if ($opcode != self::XLS_Type_CONTINUE{
  750.                             throw new Exception('Excel file is corrupt. Didn\'t find CONTINUE record while reading shared strings.');
  751.                         }
  752.                         $spos += 4;
  753.                         $limitpos $spos $conlength;
  754.                         $this->_pos += $conlength;
  755.                     }
  756.                     // Read in the number of characters in the Unicode string
  757.                     $numChars $this->_GetInt2d($this->_data$spos);
  758.                     $spos += 2;
  759.                     // option flags
  760.                     $optionFlags ord($this->_data[$spos]);
  761.                     ++$spos;
  762.                     // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
  763.                     $asciiEncoding (($optionFlags 0x01== 0;
  764.                     // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
  765.                     $extendedString (($optionFlags 0x04!= 0)// Asian phonetic
  766.                     // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
  767.                     $richString (($optionFlags 0x08!= 0);
  768.                     if ($richString// Read in the crun
  769.                         // number of Rich-Text formatting runs
  770.                         $formattingRuns $this->_GetInt2d($this->_data$spos);
  771.                         $spos += 2;
  772.                     }
  773.                     if ($extendedString{
  774.                         // size of Asian phonetic setting
  775.                         $extendedRunLength $this->_GetInt4d($this->_data$spos);
  776.                         $spos += 4;
  777.                     }
  778.                     // read in the characters
  779.                     $len ($asciiEncoding$numChars $numChars 2;
  780.                     if ($spos $len $limitpos{
  781.                         $retstr substr($this->_data$spos$len);
  782.                         $spos += $len;
  783.                     else {
  784.                         // found countinue record
  785.                         $retstr substr($this->_data$spos$limitpos $spos);
  786.                         $bytesRead $limitpos $spos;
  787.                         // remaining characters in Unicode string
  788.                         $charsLeft $numChars (($asciiEncoding$bytesRead ($bytesRead 2));
  789.                         $spos $limitpos;
  790.                         // keep reading the characters
  791.                         while ($charsLeft 0{
  792.                             // record data
  793.                             $opcode $this->_GetInt2d($this->_data$spos);
  794.  
  795.                             // length of continue record data
  796.                             $conlength $this->_GetInt2d($this->_data$spos 2);
  797.                             if ($opcode != self::XLS_Type_CONTINUE{
  798.                                 throw new Exception('Excel file is corrupt. Didn\'t find CONTINUE record while reading shared strings.');
  799.                             }
  800.                             $spos += 4;
  801.                             $limitpos $spos $conlength;
  802.  
  803.                             // option flags are repeated when Unicode string is split by a continue record
  804.                             // OpenOffice.org documentation 5.21
  805.                             $option ord($this->_data[$spos]);
  806.                             ++$spos;
  807.  
  808.                             if ($asciiEncoding && ($option == 0)) {
  809.                                 // 1st fragment compressed
  810.                                 // this fragment compressed
  811.                                 $len min($charsLeft$limitpos $spos);
  812.                                 $retstr .= substr($this->_data$spos$len);
  813.                                 $charsLeft -= $len;
  814.                                 $asciiEncoding true;
  815.  
  816.                             elseif (!$asciiEncoding && ($option != 0)) {
  817.                                 // 1st fragment uncompressed
  818.                                 // this fragment uncompressed
  819.                                 $len min($charsLeft 2$limitpos $spos);
  820.                                 $retstr .= substr($this->_data$spos$len);
  821.                                 $charsLeft -= $len/2;
  822.                                 $asciiEncoding false;
  823.  
  824.                             elseif (!$asciiEncoding && ($option == 0)) {
  825.                                 // 1st fragment uncompressed
  826.                                 // this fragment compressed
  827.                                 $len min($charsLeft$limitpos $spos);
  828.                                 for ($j 0$j $len++$j{
  829.                                     $retstr .= $this->_data[$spos $j].chr(0);
  830.                                 }
  831.                                 $charsLeft -= $len;
  832.                                 $asciiEncoding false;
  833.                             else {
  834.                                 // 1st fragment compressed
  835.                                 // this fragment uncompressed
  836.                                 $newstr '';
  837.                                 for ($j 0$j strlen($retstr)++$j{
  838.                                     $newstr .= $retstr[$j].chr(0);
  839.                                 }
  840.                                 $retstr $newstr;
  841.                                 $len min($charsLeft 2$limitpos $spos);
  842.                                 $retstr .= substr($this->_data$spos$len);
  843.                                 $charsLeft -= $len/2;
  844.                                 $asciiEncoding false;
  845.                             }
  846.                             $spos += $len;
  847.                             $this->_pos += $conlength;
  848.                         }
  849.                     }
  850.                     $retstr $this->_encodeUTF16($retstr$asciiEncoding);
  851.  
  852.                     $fmtRuns array();
  853.                     if ($richString{
  854.                         // list of formatting runs
  855.                         for ($j 0$j $formattingRuns++$j{
  856.                             // first formatted character; zero-based
  857.                             $charPos $this->_GetInt2d($this->_data$spos $j 4);
  858.                             // index to font record
  859.                             $fontIndex $this->_GetInt2d($this->_data$spos $j 4);
  860.                             $fmtRuns[array(
  861.                                 'charPos' => $charPos,
  862.                                 'fontIndex' => $fontIndex,
  863.                             );
  864.                         }
  865.                         $spos += $formattingRuns;
  866.                     }
  867.                     if ($extendedString{
  868.                         // For Asian phonetic settings, we skip the extended string data
  869.                         $spos += $extendedRunLength;
  870.                     }
  871.                     $this->_sst[array(
  872.                         'value' => $retstr,
  873.                         'fmtRuns' => $fmtRuns,
  874.                     );
  875.                 }
  876.                 $this->_pos += $length;
  877.                 break;
  878.  
  879.             case self::XLS_Type_EOF:
  880.                 $pos $this->_pos;
  881.                 $length $this->_GetInt2d($this->_data$pos 2);
  882.                 $recordData substr($this->_data$pos 4$length);
  883.                 $this->_pos += $length;
  884.                 break 2;
  885.  
  886.             default:
  887.                 $pos $this->_pos;
  888.                 $length $this->_GetInt2d($this->_data$pos 2);
  889.                 $recordData substr($this->_data$pos 4$length);
  890.                 $this->_pos += $length;
  891.                 break;
  892.  
  893.             }
  894.  
  895.         }
  896.  
  897.         // Parse the individual sheets
  898.         foreach ($this->_boundsheets as $key => $val{
  899.  
  900.             // check if sheet should be skipped
  901.             if (isset($this->_loadSheetsOnly&& !in_array($val['name']$this->_loadSheetsOnly)) {
  902.                 continue;
  903.             }
  904.  
  905.             // add sheet to PHPExcel object
  906.             $this->_sheet = $this->_excel->createSheet();
  907.             $this->_sheet->setTitle($val['name']);
  908.  
  909.             $this->_sn $key;
  910.             $spos $val['offset'];
  911.             $this->_pos = $val['offset'];
  912.  
  913.             while (true{
  914.                 $code $this->_GetInt2d($this->_data$this->_pos);
  915.  
  916.                 switch ($code{
  917.  
  918.                 case self::XLS_Type_BOF:
  919.                     $spos $this->_pos;
  920.                     $length $this->_GetInt2d($this->_data$spos 2);
  921.                     $recordData substr($this->_data$spos 4$length);
  922.                     $spos += 4;
  923.                     // do not use this version information for anything
  924.                     // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
  925.                     $substreamType ord($this->_data[$spos 2]ord($this->_data[$spos 3]<< 8;
  926.                     if ($substreamType != self::XLS_Worksheet{
  927.                         return -2;
  928.                     }
  929.                     $this->_pos += $length;
  930.                     break;
  931.  
  932.                 case self::XLS_Type_DEFAULTROWHEIGHT:
  933.                     $spos $this->_pos;
  934.                     $length $this->_GetInt2d($this->_data$spos 2);
  935.                     $recordData substr($this->_data$spos 4$length);
  936.                     $spos += 4;
  937.                     // offset: 0; size: 2; option flags
  938.                     // offset: 2; size: 2; default height for unused rows, (twips 1/20 point)
  939.                     $height $this->_GetInt2d($recordData2);
  940.                     $this->_sheet->getDefaultRowDimension()->setRowHeight($height 20);
  941.  
  942.                     $this->_pos += $length;
  943.                     break;
  944.  
  945.                 case self::XLS_Type_SHEETPR:
  946.                     $spos $this->_pos;
  947.                     $length $this->_GetInt2d($this->_data$spos 2);
  948.                     $recordData substr($this->_data$spos 4$length);
  949.                     $spos += 4;
  950.                     // offset: 0; size: 2
  951.                     // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
  952.                     $isSummaryBelow (0x0040 $this->_GetInt2d($recordData0)) >> 6;
  953.                     $this->_sheet->setShowSummaryBelow($isSummaryBelow);
  954.                     // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
  955.                     $isSummaryRight (0x0080 $this->_GetInt2d($recordData0)) >> 7;
  956.                     $this->_sheet->setShowSummaryRight($isSummaryRight);
  957.  
  958.                     $this->_pos += $length;
  959.                     break;
  960.  
  961.                 case self::XLS_Type_HORIZONTALPAGEBREAKS:
  962.                     $this->_readHorizontalPageBreaks();
  963.                     break;
  964.  
  965.                 case self::XLS_Type_VERTICALPAGEBREAKS:
  966.                     $this->_readVerticalPageBreaks();
  967.                     break;
  968.  
  969.                 case self::XLS_Type_HEADER:
  970.                     $spos $this->_pos;
  971.                     $length $this->_GetInt2d($this->_data$spos 2);
  972.                     $recordData substr($this->_data$spos 4$length);
  973.                     $spos += 4;
  974.                     if (!$this->_readDataOnly{
  975.                         // offset: 0; size: var
  976.                         // realized that $recordData can be empty even when record exists
  977.                         if ($recordData{
  978.                             $string $this->_readUnicodeStringLong($recordData);
  979.                             $this->_sheet->getHeaderFooter()->setOddHeader($string['value']);
  980.                             $this->_sheet->getHeaderFooter()->setEvenHeader($string['value']);
  981.                         }
  982.                     }
  983.  
  984.                     $this->_pos += $length;
  985.                     break;
  986.  
  987.                 case self::XLS_Type_FOOTER:
  988.                     $spos $this->_pos;
  989.                     $length $this->_GetInt2d($this->_data$spos 2);
  990.                     $recordData substr($this->_data$spos 4$length);
  991.                     $spos += 4;
  992.                     if (!$this->_readDataOnly{
  993.                         // offset: 0; size: var
  994.                         // realized that $recordData can be empty even when record exists
  995.                         if ($recordData{
  996.                             $string $this->_readUnicodeStringLong($recordData);
  997.                             $this->_sheet->getHeaderFooter()->setOddFooter($string['value']);
  998.                             $this->_sheet->getHeaderFooter()->setEvenFooter($string['value']);
  999.                         }
  1000.                     }
  1001.  
  1002.                     $this->_pos += $length;
  1003.                     break;
  1004.  
  1005.                 case self::XLS_Type_HCENTER:
  1006.                     $this->_readHcenter();
  1007.                     break;
  1008.  
  1009.                 case self::XLS_Type_VCENTER:
  1010.                     $this->_readVcenter();
  1011.                     break;
  1012.  
  1013.                 case self::XLS_Type_LEFTMARGIN:
  1014.                     $spos $this->_pos;
  1015.                     $length $this->_GetInt2d($this->_data$spos 2);
  1016.                     $recordData substr($this->_data$spos 4$length);
  1017.                     $spos += 4;
  1018.                     if (!$this->_readDataOnly{
  1019.                         // offset: 0; size: 8
  1020.                         $this->_sheet->getPageMargins()->setLeft($this->_extractNumber($recordData));
  1021.                     }
  1022.  
  1023.                     $this->_pos += $length;
  1024.                     break;
  1025.  
  1026.                 case self::XLS_Type_RIGHTMARGIN:
  1027.                     $spos $this->_pos;
  1028.                     $length $this->_GetInt2d($this->_data$spos 2);
  1029.                     $recordData substr($this->_data$spos 4$length);
  1030.                     $spos += 4;
  1031.                     if (!$this->_readDataOnly{
  1032.                         // offset: 0; size: 8
  1033.                         $this->_sheet->getPageMargins()->setRight($this->_extractNumber($recordData));
  1034.                     }
  1035.  
  1036.                     $this->_pos += $length;
  1037.                     break;
  1038.  
  1039.                 case self::XLS_Type_TOPMARGIN:
  1040.                     $spos $this->_pos;
  1041.                     $length $this->_GetInt2d($this->_data$spos 2);
  1042.                     $recordData substr($this->_data$spos 4$length);
  1043.                     $spos += 4;
  1044.                     if (!$this->_readDataOnly{
  1045.                         // offset: 0; size: 8
  1046.                         $this->_sheet->getPageMargins()->setTop($this->_extractNumber($recordData));
  1047.                     }
  1048.  
  1049.                     $this->_pos += $length;
  1050.                     break;
  1051.  
  1052.                 case self::XLS_Type_BOTTOMMARGIN:
  1053.                     $spos $this->_pos;
  1054.                     $length $this->_GetInt2d($this->_data$spos 2);
  1055.                     $recordData substr($this->_data$spos 4$length);
  1056.                     $spos += 4;
  1057.                     if (!$this->_readDataOnly{
  1058.                         // offset: 0; size: 8
  1059.                         $this->_sheet->getPageMargins()->setBottom($this->_extractNumber($recordData));
  1060.                     }
  1061.  
  1062.                     $this->_pos += $length;
  1063.                     break;
  1064.  
  1065.                 case self::XLS_Type_PAGESETUP:
  1066.                     $spos $this->_pos;
  1067.                     $length $this->_GetInt2d($this->_data$spos 2);
  1068.                     $recordData substr($this->_data$spos 4$length);
  1069.                     $spos += 4;
  1070.                     if (!$this->_readDataOnly{
  1071.                         // offset: 0; size: 2; paper size
  1072.                         $paperSize $this->_GetInt2d($recordData0);
  1073.                         // offset: 2; size: 2; scaling factor
  1074.                         $scale $this->_GetInt2d($recordData2);
  1075.                         // offset: 10; size: 2; option flags
  1076.                             // bit: 1; mask: 0x0002; 0=landscape, 1=portrait
  1077.                             $isPortrait (0x0002 $this->_GetInt2d($recordData10)) >> 1;
  1078.                             // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
  1079.                             // when this bit is set, do not use flags for those properties
  1080.                             $isNotInit (0x0004 $this->_GetInt2d($recordData10)) >> 2;
  1081.  
  1082.                         if (!$isNotInit{
  1083.                             $this->_sheet->getPageSetup()->setPaperSize($paperSize);
  1084.                             switch ($isPortrait{
  1085.                             case 0$this->_sheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE)break;
  1086.                             case 1$this->_sheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_PORTRAIT)break;
  1087.                             }
  1088.                             $this->_sheet->getPageSetup()->setScale($scale);
  1089.                         }
  1090.                     }
  1091.  
  1092.                     $this->_pos += $length;
  1093.                     break;
  1094.  
  1095.                 /**
  1096.                  * PROTECT - Sheet protection (BIFF2 through BIFF8)
  1097.                  *   if this record is omitted, then it also means no sheet protection
  1098.                  */
  1099.                 case self::XLS_Type_PROTECT:
  1100.                     $spos $this->_pos;
  1101.                     $length $this->_GetInt2d($this->_data$spos 2);
  1102.                     $recordData substr($this->_data$spos 4$length);
  1103.                     $spos += 4;
  1104.                     if (!$this->_readDataOnly{
  1105.                         // offset: 0; size: 2;
  1106.                         // bit 0, mask 0x01; sheet protection
  1107.                         $isSheetProtected (0x01 $this->_GetInt2d($recordData0)) >> 0;
  1108.                         switch ($isSheetProtected{
  1109.                             case 0break;
  1110.                             case 1$this->_sheet->getProtection()->setSheet(true)break;
  1111.                         }
  1112.                     }
  1113.  
  1114.                     $this->_pos += $length;
  1115.                     break;
  1116.  
  1117.                 /**
  1118.                  * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8)
  1119.                  */
  1120.                 case self::XLS_Type_PASSWORD:
  1121.                     $spos $this->_pos;
  1122.                     $length $this->_GetInt2d($this->_data$spos 2);
  1123.                     $recordData substr($this->_data$spos 4$length);
  1124.                     $spos += 4;
  1125.                     if (!$this->_readDataOnly{
  1126.                         // offset: 0; size: 2; 16-bit hash value of password
  1127.                         $password strtoupper(dechex($this->_GetInt2d($recordData0)))// the hashed password
  1128.                         $this->_sheet->getProtection()->setPassword($passwordtrue);
  1129.                     }
  1130.  
  1131.                     $this->_pos += $length;
  1132.                     break;
  1133.  
  1134.                 case self::XLS_Type_DEFCOLWIDTH:
  1135.                     $spos $this->_pos;
  1136.                     $length $this->_GetInt2d($this->_data$spos 2);
  1137.                     $recordData substr($this->_data$spos 4$length);
  1138.                     $spos += 4;
  1139.                     // offset: 0; size: 2; row index
  1140.                     $width $this->_GetInt2d($recordData0);
  1141.                     $this->_sheet->getDefaultColumnDimension()->setWidth($width);
  1142.  
  1143.                     $this->_pos += $length;
  1144.                     break;
  1145.  
  1146.                 /**
  1147.                  * COLINFO - Column information
  1148.                  */
  1149.                 case self::XLS_Type_COLINFO:
  1150.                     $spos $this->_pos;
  1151.                     $length $this->_GetInt2d($this->_data$spos 2);
  1152.                     $recordData substr($this->_data$spos 4$length);
  1153.                     $spos += 4;
  1154.                     if (!$this->_readDataOnly{
  1155.                         // offset: 0; size: 2; index to first column in range
  1156.                         $fc $this->_GetInt2d($recordData0)// first column index
  1157.                         // offset: 2; size: 2; index to last column in range
  1158.                         $lc $this->_GetInt2d($recordData2)// first column index
  1159.                         // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
  1160.                         $width $this->_GetInt2d($recordData4);
  1161.  
  1162.                         // offset: 6; size: 2; index to XF record for default column formatting
  1163.  
  1164.                         // offset: 8; size: 2; option flags
  1165.                             // bit: 0; mask: 0x0001; 1= columns are hidden
  1166.                             $isHidden (0x0001 $this->_GetInt2d($recordData8)) >> 0;
  1167.                             // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
  1168.                             $level (0x0700 $this->_GetInt2d($recordData8)) >> 8;
  1169.                             // bit: 12; mask: 0x1000; 1 = collapsed
  1170.                             $isCollapsed (0x1000 $this->_GetInt2d($recordData8)) >> 12;
  1171.  
  1172.                         // offset: 10; size: 2; not used
  1173.  
  1174.                         for ($i $fc$i <= $lc++$i{
  1175.                             $this->_sheet->getColumnDimensionByColumn($i)->setWidth($width 256);
  1176.                             $this->_sheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
  1177.                             $this->_sheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
  1178.                             $this->_sheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
  1179.                         }
  1180.                     }
  1181.  
  1182.                     $this->_pos += $length;
  1183.                     break;
  1184.  
  1185.                 /**
  1186.                  * DIMENSION
  1187.                  *
  1188.                  * This record contains the range address of the used area
  1189.                  * in the current sheet.
  1190.                  *
  1191.                  * --    "OpenOffice.org's Documentation of the Microsoft
  1192.                  *         Excel File Format"
  1193.                  */
  1194.                 case self::XLS_Type_DIMENSION:
  1195.                     $spos $this->_pos;
  1196.                     $length $this->_GetInt2d($this->_data$spos 2);
  1197.                     $recordData substr($this->_data$spos 4$length);
  1198.                     $spos += 4;
  1199.                     $this->_pos += $length;
  1200.                     break;
  1201.  
  1202.                 /**
  1203.                  * ROW
  1204.                  *
  1205.                  * This record contains the properties of a single row in a
  1206.                  * sheet. Rows and cells in a sheet are divided into blocks
  1207.                  * of 32 rows.
  1208.                  *
  1209.                  * --    "OpenOffice.org's Documentation of the Microsoft
  1210.                  *         Excel File Format"
  1211.                  */
  1212.                 case self::XLS_Type_ROW:
  1213.                     $spos $this->_pos;
  1214.                     $length $this->_GetInt2d($this->_data$spos 2);
  1215.                     $recordData substr($this->_data$spos 4$length);
  1216.                     $spos += 4;
  1217.                     if (!$this->_readDataOnly{
  1218.                         // offset: 0; size: 2; index of this row
  1219.                         $r $this->_GetInt2d($recordData0);
  1220.                         // offset: 2; size: 2; index to column of the first cell which is described by a cell record
  1221.                         // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
  1222.                         // offset: 6; size: 2;
  1223.                             // bit: 14-0; mask: 0x7FF; height of the row, in twips = 1/20 of a point
  1224.                             $height (0x7FF $this->_GetInt2d($recordData6)) >> 0;
  1225.                             // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
  1226.                             $useDefaultHeight (0x8000 $this->_GetInt2d($recordData6)) >> 15;
  1227.  
  1228.                             if (!$useDefaultHeight{
  1229.                                 $this->_sheet->getRowDimension($r 1)->setRowHeight($height 20);
  1230.                             }
  1231.                         // offset: 8; size: 2; not used
  1232.                         // offset: 10; size: 2; not used in BIFF5-BIFF8
  1233.                         // offset: 12; size: 4; option flags and default row formatting
  1234.                             // bit: 2-0: mask: 0x00000007; outline level of the row
  1235.                             $level (0x00000007 $this->_GetInt4d($recordData12)) >> 0;
  1236.                             $this->_sheet->getRowDimension($r 1)->setOutlineLevel($level);
  1237.                             // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
  1238.                             $isCollapsed (0x00000010 $this->_GetInt4d($recordData12)) >> 4;
  1239.                             $this->_sheet->getRowDimension($r 1)->setCollapsed($isCollapsed);
  1240.                             // bit: 5; mask: 0x00000020; 1 = row is hidden
  1241.                             $isHidden (0x00000020 $this->_GetInt4d($recordData12)) >> 5;
  1242.                             $this->_sheet->getRowDimension($r 1)->setVisible(!$isHidden);
  1243.                     }
  1244.                     $this->_pos += $length;
  1245.                     break;
  1246.  
  1247.                 /**
  1248.                  * DBCELL
  1249.                  *
  1250.                  * This record is written once in a Row Block. It contains
  1251.                  * relative offsets to calculate the stream position of the
  1252.                  * first cell record for each row. The offset list in this
  1253.                  * record contains as many offsets as ROW records are
  1254.                  * present in the Row Block.
  1255.                  *
  1256.                  * --    "OpenOffice.org's Documentation of the Microsoft
  1257.                  *         Excel File Format"
  1258.                  */
  1259.                 case self::XLS_Type_DBCELL:
  1260.                     $spos $this->_pos;
  1261.                     $length $this->_GetInt2d($this->_data$spos 2);
  1262.                     $recordData substr($this->_data$spos 4$length);
  1263.                     $spos += 4;
  1264.                     $this->_pos += $length;
  1265.                     break;
  1266.  
  1267.                 case self::XLS_Type_RK:
  1268.                     $this->_readRk();
  1269.                     break;
  1270.  
  1271.                 case self::XLS_Type_LABELSST:
  1272.                     $this->_readLabelSst();
  1273.                     break;
  1274.  
  1275.                 case self::XLS_Type_MULRK:
  1276.                     $this->_readMulRk();
  1277.                     break;
  1278.  
  1279.                 case self::XLS_Type_NUMBER:
  1280.                     $this->_readNumber();
  1281.                     break;
  1282.  
  1283.                 case self::XLS_Type_FORMULA:
  1284.                     $this->_readFormula();
  1285.                     break;
  1286.  
  1287.                 case self::XLS_Type_BOOLERR:
  1288.                     $this->_readBoolErr();
  1289.                     break;
  1290.  
  1291.                 case self::XLS_Type_MULBLANK:
  1292.                     $this->_readMulBlank();
  1293.                     break;
  1294.  
  1295.                 case self::XLS_Type_LABEL:
  1296.                     $this->_readLabel();
  1297.                     break;
  1298.  
  1299.                 case self::XLS_Type_BLANK:
  1300.                     $this->_readBlank();
  1301.                     break;
  1302.  
  1303.                 /**
  1304.                  * MERGEDCELLS
  1305.                  *
  1306.                  * This record contains the addresses of merged cell ranges
  1307.                  * in the current sheet.
  1308.                  *
  1309.                  * --    "OpenOffice.org's Documentation of the Microsoft
  1310.                  *         Excel File Format"
  1311.                  */
  1312.                 case self::XLS_Type_MERGEDCELLS:
  1313.                     $spos $this->_pos;
  1314.                     $length $this->_GetInt2d($this->_data$spos 2);
  1315.                     $recordData substr($this->_data$spos 4$length);
  1316.                     $spos += 4;
  1317.                     if ($version == self::XLS_BIFF8 && !$this->_readDataOnly{
  1318.                         $cellRangeAddressList $this->_readBIFF8CellRangeAddressList($recordData);
  1319.                         foreach ($cellRangeAddressList['cellRangeAddresses'as $cellRangeAddress{
  1320.                             $this->_sheet->mergeCells($cellRangeAddress);
  1321.                         }
  1322.                     }
  1323.                     $this->_pos += $length;
  1324.                     break;
  1325.  
  1326.                 case self::XLS_Type_HYPERLINK:
  1327.                     $spos $this->_pos;
  1328.                     $length $this->_GetInt2d($this->_data$spos 2);
  1329.                     $recordData substr($this->_data$spos 4$length);
  1330.                     $spos += 4;
  1331.                     if (!$this->_readDataOnly{
  1332.                         // offset: 0; size: 8; cell range address of all cells containing this hyperlink
  1333.                         $cellRange $this->_readBIFF8CellRangeAddressFixed($recordData08);
  1334.                         // offset: 8, size: 16; GUID of StdLink
  1335.                         // offset: 24, size: 4; unknown value
  1336.                         // offset: 28, size: 4; option flags
  1337.                             // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
  1338.                             $isFileLinkOrUrl (0x00000001 $this->_GetInt2d($recordData28)) >> 0;
  1339.                             // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
  1340.                             $isAbsPathOrUrl (0x00000001 $this->_GetInt2d($recordData28)) >> 1;
  1341.                             // bit: 2 (and 4); mask: 0x00000014; 0 = no description
  1342.                             $hasDesc (0x00000014 $this->_GetInt2d($recordData28)) >> 2;
  1343.                             // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
  1344.                             $hasText (0x00000008 $this->_GetInt2d($recordData28)) >> 3;
  1345.                             // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
  1346.                             $hasFrame (0x00000080 $this->_GetInt2d($recordData28)) >> 7;
  1347.                             // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
  1348.                             $isUNC (0x00000100 $this->_GetInt2d($recordData28)) >> 8;
  1349.  
  1350.                         $offset 32;
  1351.                         if ($hasDesc{
  1352.                             // offset: 32; size: var; character count of description text
  1353.                             $dl $this->_GetInt4d($recordData32);
  1354.                             // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
  1355.                             $desc $this->_encodeUTF16(substr($recordData36($dl 1))false);
  1356.                             $offset += $dl;
  1357.                         }
  1358.                         if ($hasFrame{
  1359.                             $fl $this->_GetInt4d($recordData$offset);
  1360.                             $offset += $fl;
  1361.                         }
  1362.  
  1363.                         // detect type of hyperlink (there are 4 types)
  1364.                         $hyperlinkType null;
  1365.  
  1366.                         if ($isUNC{
  1367.                             $hyperlinkType 'UNC';
  1368.                         else if (!$isFileLinkOrUrl{
  1369.                             $hyperlinkType 'workbook';
  1370.                         else if (ord($recordData[$offset]== 0x03{
  1371.                             $hyperlinkType 'local';
  1372.                         else if (ord($recordData[$offset]== 0xE0{
  1373.                             $hyperlinkType 'URL';
  1374.                         }
  1375.  
  1376.                         switch ($hyperlinkType{
  1377.  
  1378.                         case 'URL':
  1379.                             // offset: var; size: 16; GUID of URL Moniker
  1380.                             $offset += 16;
  1381.                             // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
  1382.                             $us $this->_GetInt4d($recordData$offset);
  1383.                             $offset += 4;
  1384.                             // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
  1385.                             $url $this->_encodeUTF16(substr($recordData$offset$us 1)false);
  1386.                             $url .= $hasText '#' '';
  1387.                             $offset += $us;
  1388.                             break;
  1389.                         case 'workbook':
  1390.                             // section 5.58.5: Hyperlink to the Current Workbook
  1391.                             // e.g. Sheet2!B1:C2, stored in text mark field
  1392.                             $url 'sheet://';
  1393.                             break;
  1394.                         case 'local':
  1395.                             // section 5.58.2: Hyperlink containing a URL
  1396.                             // e.g. http://example.org/index.php
  1397.                             // todo: implement
  1398.                         case 'UNC':
  1399.                             // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
  1400.                             // todo: implement
  1401.                         default:
  1402.                             $this->_pos += $length;
  1403.                             break 2;
  1404.  
  1405.                         }
  1406.  
  1407.                         if ($hasText{
  1408.                             // offset: var; size: 4; character count of text mark including trailing zero word
  1409.                             $tl $this->_GetInt4d($recordData$offset);
  1410.                             $offset += 4;
  1411.                             // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
  1412.                             $text $this->_encodeUTF16(substr($recordData$offset($tl 1))false);
  1413.                             $url .= $text;
  1414.                         }
  1415.  
  1416.                         // apply the hyperlink to all the relevant cells
  1417.                         foreach (PHPExcel_Cell::extractAllCellReferencesInRange($cellRangeas $coordinate{
  1418.                             $this->_sheet->getCell($coordinate)->getHyperLink()->setUrl($url);
  1419.                         }
  1420.                     }
  1421.                     $this->_pos += $length;
  1422.                     break;
  1423.  
  1424.                 //case self::XLS_Type_IMDATA:
  1425.                 //    $this->_readImData();
  1426.                 //    break;
  1427.  
  1428.                 case self::XLS_Type_EOF:
  1429.                     $spos $this->_pos;
  1430.                     $length $this->_GetInt2d($this->_data$spos 2);
  1431.                     $recordData substr($this->_data$spos 4$length);
  1432.                     $spos += 4;
  1433.                     $this->_pos += $length;
  1434.                     break 2;
  1435.  
  1436.                 default:
  1437.                     $spos $this->_pos;
  1438.                     $length $this->_GetInt2d($this->_data$spos 2);
  1439.                     $recordData substr($this->_data$spos 4$length);
  1440.                     $spos += 4;
  1441.                     $this->_pos += $length;
  1442.                     break;
  1443.  
  1444.                 }
  1445.  
  1446.             }
  1447.         }
  1448.  
  1449.         // add the named ranges (defined names)
  1450.         foreach ($this->_definedname as $definedName{
  1451.             if ($definedName['isBuiltInName']{
  1452.                 switch ($definedName['name']{
  1453.  
  1454.                 case pack('C'0x06):
  1455.                     // print area
  1456.                     //    in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
  1457.  
  1458.                     $ranges explode(','$definedName['formula'])// FIXME: what if sheetname contains comma?
  1459.  
  1460.                     foreach ($ranges as $range{
  1461.                         // $range should look like this one of these
  1462.                         //        Foo!$C$7:$J$66
  1463.                         //        Bar!$A$1:$IV$2
  1464.  
  1465.                         $explodes explode('!'$range);
  1466.  
  1467.                         if (count($explodes== 2{
  1468.                             if ($docSheet $this->_excel->getSheetByName($explodes[0])) {
  1469.                                 $extractedRange $explodes[1];
  1470.                                 $extractedRange str_replace('$'''$extractedRange);
  1471.                                 $docSheet->getPageSetup()->setPrintArea($extractedRange);
  1472.                             }
  1473.                         }
  1474.                     }
  1475.                     break;
  1476.  
  1477.                 case pack('C'0x07):
  1478.                     // print titles (repeating rows)
  1479.                     // Assuming BIFF8, there are 3 cases
  1480.                     // 1. repeating rows
  1481.                     //        formula looks like this: Sheet!$A$1:$IV$2
  1482.                     //        rows 1-2 repeat
  1483.                     // 2. repeating columns
  1484.                     //        formula looks like this: Sheet!$A$1:$B$65536
  1485.                     //        columns A-B repeat
  1486.                     // 3. both repeating rows and repeating columns
  1487.                     //        formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
  1488.  
  1489.                     $ranges explode(','$definedName['formula'])// FIXME: what if sheetname contains comma?
  1490.  
  1491.                     foreach ($ranges as $range{
  1492.                         // $range should look like this one of these
  1493.                         //        Sheet!$A$1:$B$65536
  1494.                         //        Sheet!$A$1:$IV$2
  1495.  
  1496.                         $explodes explode('!'$range);
  1497.  
  1498.                         if (count($explodes== 2{
  1499.                             if ($docSheet $this->_excel->getSheetByName($explodes[0])) {
  1500.  
  1501.                                 $extractedRange $explodes[1];
  1502.                                 $extractedRange str_replace('$'''$extractedRange);
  1503.  
  1504.                                 $coordinateStrings explode(':'$extractedRange);
  1505.                                 if (count($coordinateStrings== 2{
  1506.                                     list($firstColumn$firstRowPHPExcel_Cell::coordinateFromString($coordinateStrings[0]);
  1507.                                     list($lastColumn$lastRowPHPExcel_Cell::coordinateFromString($coordinateStrings[1]);
  1508.  
  1509.                                     if ($firstColumn == 'A' and $lastColumn == 'IV'{
  1510.                                         // then we have repeating rows
  1511.                                         $docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow$lastRow));
  1512.                                     elseif ($firstRow == and $lastRow == 65536{
  1513.                                         // then we have repeating columns
  1514.                                         $docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn$lastColumn));
  1515.                                     }
  1516.                                 }
  1517.                             }
  1518.                         }
  1519.                     }
  1520.                     break;
  1521.  
  1522.                 }
  1523.             else {
  1524.                 // Extract range
  1525.                 $explodes explode('!'$definedName['formula']);
  1526.  
  1527.                 if (count($explodes== 2{
  1528.                     if ($docSheet $this->_excel->getSheetByName($explodes[0])) {
  1529.                         $extractedRange $explodes[1];
  1530.                         $extractedRange str_replace('$'''$extractedRange);
  1531.  
  1532.                         $this->_excel->addNamedRangenew PHPExcel_NamedRange((string)$definedName['name']$docSheet$extractedRangetrue) );
  1533.                     }
  1534.                 }
  1535.             }
  1536.         }
  1537.  
  1538.         return $this->_excel;
  1539.     }
  1540.  
  1541.     /**
  1542.      * Read CODEPAGE record
  1543.      */
  1544.     private function _readCodepage()
  1545.     {
  1546.         $pos $this->_pos;
  1547.         $length $this->_GetInt2d($this->_data$pos 2);
  1548.         $recordData substr($this->_data$pos 4$length);
  1549.         $codepage $this->_GetInt2d($this->_data$pos 4);
  1550.  
  1551.         switch($codepage{
  1552.  
  1553.         case 367// ASCII
  1554.             $this->_codepage ="ASCII";
  1555.             break;
  1556.         case 437//OEM US
  1557.             $this->_codepage ="CP437";
  1558.             break;
  1559.         case 720//OEM Arabic
  1560.             // currently not supported by libiconv
  1561.             $this->_codepage = "";
  1562.             break;
  1563.         case 737//OEM Greek
  1564.             $this->_codepage ="CP737";
  1565.             break;
  1566.         case 775//OEM Baltic
  1567.             $this->_codepage ="CP775";
  1568.             break;
  1569.         case 850//OEM Latin I
  1570.             $this->_codepage ="CP850";
  1571.             break;
  1572.         case 852//OEM Latin II (Central European)
  1573.             $this->_codepage ="CP852";
  1574.             break;
  1575.         case 855//OEM Cyrillic
  1576.             $this->_codepage ="CP855";
  1577.             break;
  1578.         case 857//OEM Turkish
  1579.             $this->_codepage ="CP857";
  1580.             break;
  1581.         case 858//OEM Multilingual Latin I with Euro
  1582.             $this->_codepage ="CP858";
  1583.             break;
  1584.         case 860//OEM Portugese
  1585.             $this->_codepage ="CP860";
  1586.             break;
  1587.         case 861//OEM Icelandic
  1588.             $this->_codepage ="CP861";
  1589.             break;
  1590.         case 862//OEM Hebrew
  1591.             $this->_codepage ="CP862";
  1592.             break;
  1593.         case 863//OEM Canadian (French)
  1594.             $this->_codepage ="CP863";
  1595.             break;
  1596.         case 864//OEM Arabic
  1597.             $this->_codepage ="CP864";
  1598.             break;
  1599.         case 865//OEM Nordic
  1600.             $this->_codepage ="CP865";
  1601.             break;
  1602.         case 866//OEM Cyrillic (Russian)
  1603.             $this->_codepage ="CP866";
  1604.             break;
  1605.         case 869//OEM Greek (Modern)
  1606.             $this->_codepage ="CP869";
  1607.             break;
  1608.         case 874//ANSI Thai
  1609.             $this->_codepage ="CP874";
  1610.             break;
  1611.         case 932//ANSI Japanese Shift-JIS
  1612.             $this->_codepage ="CP932";
  1613.             break;
  1614.         case 936//ANSI Chinese Simplified GBK
  1615.             $this->_codepage ="CP936";
  1616.             break;
  1617.         case 949//ANSI Korean (Wansung)
  1618.             $this->_codepage ="CP949";
  1619.             break;
  1620.         case 950//ANSI Chinese Traditional BIG5
  1621.             $this->_codepage ="CP950";
  1622.             break;
  1623.         case 1200//UTF-16 (BIFF8)
  1624.             $this->_codepage ="UTF-16LE";
  1625.             break;
  1626.         case 1250:// ANSI Latin II (Central European)
  1627.             $this->_codepage ="CP1250";
  1628.             break;
  1629.         case 1251//ANSI Cyrillic
  1630.             $this->_codepage ="CP1251";
  1631.             break;
  1632.         case 1252//ANSI Latin I (BIFF4-BIFF7)
  1633.             $this->_codepage ="CP1252";
  1634.             break;
  1635.         case 1253//ANSI Greek
  1636.             $this->_codepage ="CP1253";
  1637.             break;
  1638.         case 1254//ANSI Turkish
  1639.             $this->_codepage ="CP1254";
  1640.             break;
  1641.         case 1255//ANSI Hebrew
  1642.             $this->_codepage ="CP1255";
  1643.             break;
  1644.         case 1256//ANSI Arabic
  1645.             $this->_codepage ="CP1256";
  1646.             break;
  1647.         case 1257//ANSI Baltic
  1648.             $this->_codepage ="CP1257";
  1649.             break;
  1650.         case 1258//ANSI Vietnamese
  1651.             $this->_codepage ="CP1258";
  1652.             break;
  1653.         case 1361//ANSI Korean (Johab)
  1654.             $this->_codepage ="CP1361";
  1655.             break;
  1656.         case 10000//Apple Roman
  1657.             // currently not supported by libiconv
  1658.             $this->_codepage = "";
  1659.             break;
  1660.         case 32768//Apple Roman
  1661.             // currently not supported by libiconv
  1662.             $this->_codepage = "";
  1663.             break;
  1664.         case 32769//ANSI Latin I (BIFF2-BIFF3)
  1665.             // currently not supported by libiconv
  1666.             $this->_codepage = "";
  1667.             break;
  1668.  
  1669.         }
  1670.         $this->_pos += $length;
  1671.     }
  1672.  
  1673.     /**
  1674.      * Read a FONT record
  1675.      */
  1676.     private function _readFont()
  1677.     {
  1678.         $pos $this->_pos;
  1679.         $length $this->_GetInt2d($this->_data$pos 2);
  1680.         $recordData substr($this->_data$pos 4$length);
  1681.         if (!$this->_readDataOnly{
  1682.             $font array();
  1683.             // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
  1684.             $size $this->_GetInt2d($recordData0);
  1685.             $font['size'$size 20;
  1686.  
  1687.             // offset: 2; size: 2; option flags
  1688.                 // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
  1689.                 // bit: 1; mask 0x0002; italic
  1690.                 $isItalic (0x0002 $this->_GetInt2d($recordData2)) >> 1;
  1691.                 if ($isItalic$font['italic'true;
  1692.  
  1693.                 // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
  1694.                 // bit: 3; mask 0x0008; strike
  1695.                 $isStrike (0x0008 $this->_GetInt2d($recordData2)) >> 3;
  1696.                 if ($isStrike$font['strike'true;
  1697.  
  1698.             // offset: 4; size: 2; colour index
  1699.             if ($color $this->_mapColor($this->_GetInt2d($recordData4))) {
  1700.                 $font['color'$color;
  1701.             }
  1702.  
  1703.             // offset: 6; size: 2; font weight
  1704.             $weight $this->_GetInt2d($recordData6);
  1705.             switch ($weight{
  1706.                 case 0x02BC$font['bold'true;
  1707.             }
  1708.  
  1709.             // offset: 8; size: 2; escapement type
  1710.             $escapement $this->_GetInt2d($recordData8);
  1711.             switch ($escapement{
  1712.                 case 0x0001$font['superScript'truebreak;
  1713.                 case 0x0002$font['subScript'truebreak;
  1714.             }
  1715.  
  1716.             // offset: 10; size: 1; underline type
  1717.             $underlineType ord($recordData[10]);
  1718.             switch ($underlineType{
  1719.                 case 0x00break// no underline
  1720.                 case 0x01$font['underline'PHPExcel_Style_Font::UNDERLINE_SINGLEbreak;
  1721.                 case 0x02$font['underline'PHPExcel_Style_Font::UNDERLINE_DOUBLEbreak;
  1722.                 case 0x21$font['underline'PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTINGbreak;
  1723.                 case 0x22$font['underline'PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTINGbreak;
  1724.             }
  1725.  
  1726.             // offset: 11; size: 1; font family
  1727.             // offset: 12; size: 1; character set
  1728.             // offset: 13; size: 1; not used
  1729.             // offset: 14; size: var; font name
  1730.             if ($this->_version == self::XLS_BIFF8{
  1731.                 $string $this->_readUnicodeStringShort(substr($recordData14));
  1732.             else {
  1733.                 $string $this->_readByteStringShort(substr($recordData14));
  1734.             }
  1735.             $font['name'$string['value'];
  1736.  
  1737.             $this->_fonts[$font;
  1738.         }
  1739.         $this->_pos += $length;
  1740.     }
  1741.  
  1742.     /**
  1743.      * Read a FORMAT record
  1744.      */
  1745.     private function _readFormat()
  1746.     {
  1747.         $pos $this->_pos;
  1748.         $length $this->_GetInt2d($this->_data$pos 2);
  1749.         $recordData substr($this->_data$pos 4$length);
  1750.         if (!$this->_readDataOnly{
  1751.             $indexCode $this->_GetInt2d($recordData0);
  1752.             
  1753.             if ($this->_version == self::XLS_BIFF8{
  1754.                 $string $this->_readUnicodeStringLong(substr($recordData2));
  1755.             else {
  1756.                 // BIFF7
  1757.                 $string $this->_readByteStringShort(substr($recordData2));
  1758.             }
  1759.             
  1760.             $formatString $string['value'];
  1761.             $this->_formats[$indexCode$formatString;
  1762.         }
  1763.         $this->_pos += $length;
  1764.     }
  1765.  
  1766.     /**
  1767.      * Read an XF record
  1768.      */
  1769.     private function _readXf()
  1770.     {
  1771.         $pos $this->_pos;
  1772.         $length $this->_GetInt2d($this->_data$pos 2);
  1773.         $recordData substr($this->_data$pos 4$length);
  1774.  
  1775.         $style array();
  1776.         if (!$this->_readDataOnly{
  1777.             // offset:  0; size: 2; Index to FONT record
  1778.             if ($this->_GetInt2d($recordData04{
  1779.                 $fontIndex $this->_GetInt2d($recordData0);
  1780.             else {
  1781.                 // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
  1782.                 // check the OpenOffice documentation of the FONT record
  1783.                 $fontIndex $this->_GetInt2d($recordData01;
  1784.             }
  1785.             $style['font'$this->_fonts[$fontIndex];
  1786.  
  1787.             // offset:  2; size: 2; Index to FORMAT record
  1788.             $numberFormatIndex $this->_GetInt2d($recordData2);
  1789.             if (isset($this->_formats[$numberFormatIndex])) {
  1790.                 // then we have user-defined format code
  1791.                 $numberformat array('code' => $this->_formats[$numberFormatIndex]);
  1792.             elseif ($code PHPExcel_Style_NumberFormat::builtInFormatCode($numberFormatIndex)) {
  1793.                 // then we have built-in format code
  1794.                 $numberformat array('code' => $code);
  1795.             else {
  1796.                 // we set the general format code
  1797.                 $numberformat array('code' => 'General');
  1798.             }
  1799.             $style['numberformat'$numberformat;
  1800.  
  1801.             $style['protection'array();
  1802.             // offset:  4; size: 2; XF type, cell protection, and parent style XF
  1803.             // bit 2-0; mask 0x0007; XF_TYPE_PROT
  1804.             $xfTypeProt $this->_GetInt2d($recordData4);
  1805.             // bit 0; mask 0x01; 1 = cell is locked
  1806.             $isLocked (0x01 $xfTypeProt>> 0;
  1807.             $style['protection']['locked'$isLocked ?
  1808.                 PHPExcel_Style_Protection::PROTECTION_INHERIT PHPExcel_Style_Protection::PROTECTION_UNPROTECTED;
  1809.             // bit 1; mask 0x02; 1 = Formula is hidden
  1810.             $isHidden (0x02 $xfTypeProt>> 1;
  1811.             $style['protection']['hidden'$isHidden ?
  1812.                 PHPExcel_Style_Protection::PROTECTION_PROTECTED PHPExcel_Style_Protection::PROTECTION_UNPROTECTED;
  1813.             // bit 2;
  1814.  
  1815.             if ($this->_version == self::XLS_BIFF8{
  1816.                 // offset:  6; size: 1; Alignment and text break
  1817.                 $style['alignment'array();
  1818.                     // bit 2-0, mask 0x07; horizontal alignment
  1819.                     $horAlign (0x07 ord($recordData[6])) >> 0;
  1820.                     switch ($horAlign{
  1821.                         case 0$style['alignment']['horizontal'PHPExcel_Style_Alignment::HORIZONTAL_GENERALbreak;
  1822.                         case 1$style['alignment']['horizontal'PHPExcel_Style_Alignment::HORIZONTAL_LEFTbreak;
  1823.                         case 2$style['alignment']['horizontal'PHPExcel_Style_Alignment::HORIZONTAL_CENTERbreak;
  1824.                         case 3$style['alignment']['horizontal'PHPExcel_Style_Alignment::HORIZONTAL_RIGHTbreak;
  1825.                         case 5$style['alignment']['horizontal'PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFYbreak;
  1826.                     }
  1827.                     // bit 3, mask 0x08; wrap text
  1828.                     $wrapText (0x08 ord($recordData[6])) >> 3;
  1829.                     switch ($wrapText{
  1830.                         case 0$style['alignment']['wrap'falsebreak;
  1831.                         case 1$style['alignment']['wrap'truebreak;
  1832.                     }
  1833.                     // bit 6-4, mask 0x70; vertical alignment
  1834.                     $vertAlign (0x70 ord($recordData[6])) >> 4;
  1835.                     switch ($vertAlign{
  1836.                         case 0$style['alignment']['vertical'PHPExcel_Style_Alignment::VERTICAL_TOPbreak;
  1837.                         case 1$style['alignment']['vertical'PHPExcel_Style_Alignment::VERTICAL_CENTERbreak;
  1838.                         case 2$style['alignment']['vertical'PHPExcel_Style_Alignment::VERTICAL_BOTTOMbreak;
  1839.                         case 3$style['alignment']['vertical'PHPExcel_Style_Alignment::VERTICAL_JUSTIFYbreak;
  1840.                     }
  1841.  
  1842.                 // offset:  7; size: 1; XF_ROTATION: Text rotation angle
  1843.                     $angle ord($recordData[7]);
  1844.                     $rotation 0;
  1845.                     if ($angle <= 90{
  1846.                         $rotation $angle;
  1847.                     else if ($angle <= 180{
  1848.                         $rotation 90 $angle;
  1849.                     else if ($angle == 255{
  1850.                         $rotation = -165;
  1851.                     }
  1852.                     $style['alignment']['rotation'$rotation;
  1853.  
  1854.                 // offset:  8; size: 1; Indentation, shrink to cell size, and text direction
  1855.                     // bit: 3-0; mask: 0x0F; indent level
  1856.                     $indent (0x0F ord($recordData[8])) >> 0;
  1857.                     $style['alignment']['indent'$indent;
  1858.                     
  1859.                     // bit: 4; mask: 0x10; 1 = shrink content to fit into cell
  1860.                     $shrinkToFit (0x10 ord($recordData[8])) >> 4;
  1861.                     switch ($shrinkToFit{
  1862.                         case 0$style['alignment']['shrinkToFit'falsebreak;
  1863.                         case 1$style['alignment']['shrinkToFit'truebreak;
  1864.                     }
  1865.  
  1866.                 // offset:  9; size: 1; Flags used for attribute groups
  1867.  
  1868.                 $style['borders'array(
  1869.                     'left' => array(),
  1870.                     'right' => array(),
  1871.                     'top' => array(),
  1872.                     'bottom' => array(),
  1873.                 );
  1874.                 // offset: 10; size: 4; Cell border lines and background area
  1875.                     // bit: 3-0; mask: 0x0000000F; left style
  1876.                     if ($bordersLeftStyle $this->_mapBorderStyle((0x0000000F $this->_GetInt4d($recordData10)) >> 0)) {
  1877.                         $style['borders']['left']['style'$bordersLeftStyle;
  1878.                     }
  1879.                     // bit: 7-4; mask: 0x000000F0; right style
  1880.                     if ($bordersRightStyle $this->_mapBorderStyle((0x000000F0 $this->_GetInt4d($recordData10)) >> 4)) {
  1881.                         $style['borders']['right']['style'$bordersRightStyle;
  1882.                     }
  1883.                     // bit: 11-8; mask: 0x00000F00; top style
  1884.                     if ($bordersTopStyle $this->_mapBorderStyle((0x00000F00 $this->_GetInt4d($recordData10)) >> 8)) {
  1885.                         $style['borders']['top']['style'$bordersTopStyle;
  1886.                     }
  1887.                     // bit: 15-12; mask: 0x0000F000; bottom style
  1888.                     if ($bordersBottomStyle $this->_mapBorderStyle((0x0000F000 $this->_GetInt4d($recordData10)) >> 12)) {
  1889.                         $style['borders']['bottom']['style'$bordersBottomStyle;
  1890.                     }
  1891.                     // bit: 22-16; mask: 0x007F0000; left color
  1892.                     if ($bordersLeftColor $this->_mapColor((0x007F0000 $this->_GetInt4d($recordData10)) >> 16)) {
  1893.                         $style['borders']['left']['color'$bordersLeftColor;
  1894.                     }
  1895.                     // bit: 29-23; mask: 0x3F800000; right color
  1896.                     if ($bordersRightColor $this->_mapColor((0x3F800000 $this->_GetInt4d($recordData10)) >> 23)) {
  1897.                         $style['borders']['right']['color'$bordersRightColor;
  1898.                     }
  1899.  
  1900.                 $style['fill'array();
  1901.                 // offset: 14; size: 4;
  1902.                     // bit: 6-0; mask: 0x0000007F; top color
  1903.                     if ($bordersTopColor $this->_mapColor((0x0000007F $this->_GetInt4d($recordData14)) >> 0)) {
  1904.                         $style['borders']['top']['color'$bordersTopColor;
  1905.                     }
  1906.                     // bit: 13-7; mask: 0x00003F80; top color
  1907.                     if ($bordersBottomColor $this->_mapColor((0x00003F80 $this->_GetInt4d($recordData14)) >> 7)) {
  1908.                         $style['borders']['bottom']['color'$bordersBottomColor;
  1909.                     }
  1910.                     // bit: 31-26; mask: 0xFC000000 fill pattern
  1911.                     if ($fillType $this->_mapFillPattern((0xFC000000 $this->_GetInt4d($recordData14)) >> 26)) {
  1912.                         $style['fill']['type'$fillType;
  1913.                     }
  1914.                 // offset: 18; size: 2; pattern and background colour
  1915.                     // bit: 6-0; mask: 0x007F; color index for pattern color
  1916.                     if ($rgb $this->_mapColor((0x007F $this->_GetInt2d($recordData18)) >> 0)) {
  1917.                         $style['fill']['startcolor'$rgb;
  1918.                     }
  1919.                     // bit: 13-7; mask: 0x3F80; color index for pattern background
  1920.                     if ($rgb $this->_mapColor((0x3F80 $this->_GetInt2d($recordData18)) >> 7)) {
  1921.                         $style['fill']['endcolor'$rgb;
  1922.                     }
  1923.             else {
  1924.                 // BIFF7
  1925.                 
  1926.             }
  1927.             $this->_xf[$style;
  1928.         }
  1929.         $this->_pos += $length;
  1930.     }
  1931.  
  1932.     /**
  1933.      * Read HORIZONTALPAGEBREAKS record
  1934.      */
  1935.     private function _readHorizontalPageBreaks()
  1936.     {
  1937.         $spos $this->_pos;
  1938.         $length $this->_GetInt2d($this->_data$spos 2);
  1939.  
  1940.         if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly{
  1941.             $recordData substr($this->_data$spos 4$length);
  1942.             // offset: 0; size: 2; number of the following row index structures
  1943.             $nm $this->_GetInt2d($recordData0);
  1944.             // offset: 2; size: 6 * $nm; list of $nm row index structures
  1945.             for ($i 0$i $nm++$i{
  1946.                 $r $this->_GetInt2d($recordData$i);
  1947.                 $cf $this->_GetInt2d($recordData$i 2);
  1948.                 $cl $this->_GetInt2d($recordData$i 4);
  1949.                 // not sure why two column indexes are necessary?
  1950.                 $this->_sheet->setBreakByColumnAndRow($cf$rPHPExcel_Worksheet::BREAK_ROW);
  1951.             }
  1952.         }
  1953.  
  1954.         $this->_pos += $length;
  1955.     }
  1956.  
  1957.     /**
  1958.      * Read VERTICALPAGEBREAKS record
  1959.      */
  1960.     private function _readVerticalPageBreaks()
  1961.     {
  1962.         $spos $this->_pos;
  1963.         $length $this->_GetInt2d($this->_data$spos 2);
  1964.  
  1965.         if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly{
  1966.             $recordData substr($this->_data$spos 4$length);
  1967.             // offset: 0; size: 2; number of the following column index structures
  1968.             $nm $this->_GetInt2d($recordData0);
  1969.             // offset: 2; size: 6 * $nm; list of $nm row index structures
  1970.             for ($i 0$i $nm++$i{
  1971.                 $c $this->_GetInt2d($recordData$i);
  1972.                 $rf $this->_GetInt2d($recordData$i 2);
  1973.                 $rl $this->_GetInt2d($recordData$i 4);
  1974.                 // not sure why two row indexes are necessary?
  1975.                 $this->_sheet->setBreakByColumnAndRow($c$rfPHPExcel_Worksheet::BREAK_COLUMN);
  1976.             }
  1977.         }
  1978.  
  1979.         $this->_pos += $length;
  1980.     }
  1981.  
  1982.     /**
  1983.      * Read HCENTER record
  1984.      */
  1985.     private function _readHcenter()
  1986.     {
  1987.         $pos $this->_pos;
  1988.         $length $this->_GetInt2d($this->_data$pos 2);
  1989.  
  1990.         if (!$this->_readDataOnly{
  1991.             $recordData substr($this->_data$pos 4$length);
  1992.  
  1993.             // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
  1994.             $isHorizontalCentered = (bool) $this->_GetInt2d($recordData0);
  1995.  
  1996.             $this->_sheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
  1997.         }
  1998.  
  1999.         $this->_pos += $length;
  2000.     }
  2001.  
  2002.     /**
  2003.      * Read VCENTER record
  2004.      */
  2005.     private function _readVcenter()
  2006.     {
  2007.         $pos $this->_pos;
  2008.         $length $this->_GetInt2d($this->_data$pos 2);
  2009.  
  2010.         if (!$this->_readDataOnly{
  2011.             $recordData substr($this->_data$pos 4$length);
  2012.  
  2013.             // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
  2014.             $isVerticalCentered = (bool) $this->_GetInt2d($recordData0);
  2015.  
  2016.             $this->_sheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
  2017.         }
  2018.  
  2019.         $this->_pos += $length;
  2020.     }
  2021.  
  2022.     /**
  2023.      * Read RK record
  2024.      * This record represents a cell that contains an RK value
  2025.      * (encoded integer or floating-point value). If a
  2026.      * floating-point value cannot be encoded to an RK value,
  2027.      * a NUMBER record will be written. This record replaces the
  2028.      * record INTEGER written in BIFF2.
  2029.      *
  2030.      * --    "OpenOffice.org's Documentation of the Microsoft
  2031.      *         Excel File Format"
  2032.      */
  2033.     private function _readRk()
  2034.     {
  2035.         $pos $this->_pos;
  2036.         $length $this->_GetInt2d($this->_data$pos 2);
  2037.         $recordData substr($this->_data$pos 4$length);
  2038.         $pos += 4;
  2039.  
  2040.         // offset: 0; size: 2; index to row
  2041.         $row $this->_GetInt2d($this->_data$pos);
  2042.  
  2043.         // offset: 2; size: 2; index to column
  2044.         $column $this->_GetInt2d($this->_data$pos 2);
  2045.         $columnString PHPExcel_Cell::stringFromColumnIndex($column);
  2046.  
  2047.         // Read cell?
  2048.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_sheet->getTitle()) ) {
  2049.             // offset: 4; size: 2; index to XF record
  2050.             $xfindex $this->_GetInt2d($recordData4);
  2051.  
  2052.             // offset: 6; size: 4; RK value
  2053.             $rknum $this->_GetInt4d($this->_data$pos 6);
  2054.             $numValue $this->_GetIEEE754($rknum);
  2055.  
  2056.             // add style information
  2057.             if (!$this->_readDataOnly{
  2058.                 $this->_sheet->getStyleByColumnAndRow($column$row 1)->applyFromArray($this->_xf[$xfindex]);
  2059.  
  2060.                 if (PHPExcel_Shared_Date::isDateTimeFormatCode($this->_xf[$xfindex]['numberformat']['code'])) {
  2061.                     $numValue PHPExcel_Shared_Date::ExcelToPHP($numValue);
  2062.                 }
  2063.             }
  2064.  
  2065.             // add cell
  2066.             $this->_sheet->setCellValueExplicitByColumnAndRow($column$row 1$numValuePHPExcel_Cell_DataType::TYPE_NUMERIC);
  2067.         }
  2068.  
  2069.         // move stream pointer to next record
  2070.         $this->_pos += $length;
  2071.     }
  2072.  
  2073.     /**
  2074.      * Read LABELSST record
  2075.      * This record represents a cell that contains a string. It
  2076.      * replaces the LABEL record and RSTRING record used in
  2077.      * BIFF2-BIFF5.
  2078.      *
  2079.      * --    "OpenOffice.org's Documentation of the Microsoft
  2080.      *         Excel File Format"
  2081.      */
  2082.     private function _readLabelSst()
  2083.     {
  2084.         $pos $this->_pos;
  2085.         $length $this->_GetInt2d($this->_data$pos 2);
  2086.         $recordData substr($this->_data$pos 4$length);
  2087.         $pos += 4;
  2088.  
  2089.         // offset: 0; size: 2; index to row
  2090.         $row $this->_GetInt2d($this->_data$pos);
  2091.  
  2092.         // offset: 2; size: 2; index to column
  2093.         $column $this->_GetInt2d($this->_data$pos 2);
  2094.         $columnString PHPExcel_Cell::stringFromColumnIndex($column);
  2095.  
  2096.         // Read cell?
  2097.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_sheet->getTitle()) ) {
  2098.             // offset: 4; size: 2; index to XF record
  2099.             $xfindex $this->_GetInt2d($this->_data$pos 4);
  2100.  
  2101.             // offset: 6; size: 4; index to SST record
  2102.             $index $this->_GetInt4d($this->_data$pos 6);
  2103.  
  2104.             // add cell
  2105.             if (($fmtRuns $this->_sst[$index]['fmtRuns']&& !$this->_readDataOnly{
  2106.                 // then we should treat as rich text
  2107.                 $richText new PHPExcel_RichText($this->_sheet->getCellByColumnAndRow($column$row 1));
  2108.                 $charPos 0;
  2109.                 for ($i 0$i <= count($this->_sst[$index]['fmtRuns'])++$i{
  2110.                     if (isset($fmtRuns[$i])) {
  2111.                         $text mb_substr($this->_sst[$index]['value']$charPos$fmtRuns[$i]['charPos'$charPos'UTF-8');
  2112.                         $charPos $fmtRuns[$i]['charPos'];
  2113.                     else {
  2114.                         $text mb_substr($this->_sst[$index]['value']$charPosmb_strlen($this->_sst[$index]['value'])'UTF-8');
  2115.                     }
  2116.  
  2117.                     if (mb_strlen($text0{
  2118.                         if ($i == 0// first text run, no style
  2119.                             $richText->createText($text);
  2120.                         else {
  2121.                             $textRun $richText->createTextRun($text);
  2122.                             if (isset($fmtRuns[$i 1])) {
  2123.                                 if ($fmtRuns[$i 1]['fontIndex'4{
  2124.                                     $fontIndex $fmtRuns[$i 1]['fontIndex'];
  2125.                                 else {
  2126.                                     // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
  2127.                                     // check the OpenOffice documentation of the FONT record
  2128.                                     $fontIndex $fmtRuns[$i 1]['fontIndex'1;
  2129.                                 }
  2130.                                 $textRun->getFont()->applyFromArray($this->_fonts[$fontIndex]);
  2131.                             }
  2132.                         }
  2133.                     }
  2134.                 }
  2135.             else {
  2136.                 $this->_sheet->setCellValueExplicitByColumnAndRow($column$row 1$this->_sst[$index]['value']PHPExcel_Cell_DataType::TYPE_STRING);
  2137.             }
  2138.  
  2139.             // add style information
  2140.             if (!$this->_readDataOnly{
  2141.                 $this->_sheet->getStyleByColumnAndRow($column$row 1)->applyFromArray($this->_xf[$xfindex]);
  2142.             }
  2143.         }
  2144.  
  2145.         // move stream pointer to next record
  2146.         $this->_pos += $length;
  2147.     }
  2148.  
  2149.     /**
  2150.      * Read MULRK record
  2151.      * This record represents a cell range containing RK value
  2152.      * cells. All cells are located in the same row.
  2153.      *
  2154.      * --    "OpenOffice.org's Documentation of the Microsoft
  2155.      *         Excel File Format"
  2156.      */
  2157.     private function _readMulRk()
  2158.     {
  2159.         $pos $this->_pos;
  2160.         $length $this->_GetInt2d($this->_data$pos 2);
  2161.         $recordData substr($this->_data$pos 4$length);
  2162.         $pos += 4;
  2163.  
  2164.         // offset: 0; size: 2; index to row
  2165.         $row $this->_GetInt2d($this->_data$pos);
  2166.         
  2167.         // offset: 2; size: 2; index to first column
  2168.         $colFirst $this->_GetInt2d($this->_data$pos 2);
  2169.         
  2170.         // offset: var; size: 2; index to last column
  2171.         $colLast $this->_GetInt2d($this->_data$pos $length 2);
  2172.         $columns $colLast $colFirst 1;
  2173.  
  2174.         $tmppos $pos 4;
  2175.         for ($i 0$i $columns++$i{
  2176.             $columnString PHPExcel_Cell::stringFromColumnIndex($colFirst $i);
  2177.  
  2178.             // Read cell?
  2179.             if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_sheet->getTitle()) ) {
  2180.                 // offset: 0; size: 2; index to XF record
  2181.                 $xfindex $this->_GetInt2d($recordData$i);
  2182.  
  2183.                 // offset: 2; size: 4; RK value
  2184.                 $numValue $this->_GetIEEE754($this->_GetInt4d($this->_data$tmppos 2));
  2185.                 if (!$this->_readDataOnly{
  2186.                     // add style
  2187.                     $this->_sheet->getStyleByColumnAndRow($colFirst $i$row 1)->applyFromArray($this->_xf[$xfindex]);
  2188.  
  2189.                     if (PHPExcel_Shared_Date::isDateTimeFormatCode($this->_xf[$xfindex]['numberformat']['code'])) {
  2190.                         $numValue PHPExcel_Shared_Date::ExcelToPHP($numValue);
  2191.                     }
  2192.                 }
  2193.  
  2194.                 // add cell value
  2195.                 $this->_sheet->setCellValueExplicitByColumnAndRow($colFirst $i$row 1$numValuePHPExcel_Cell_DataType::TYPE_NUMERIC);
  2196.             }
  2197.  
  2198.             $tmppos += 6;
  2199.         }
  2200.  
  2201.         // move stream pointer to next record
  2202.         $this->_pos += $length;
  2203.     }
  2204.  
  2205.     /**
  2206.      * Read NUMBER record
  2207.      * This record represents a cell that contains a
  2208.      * floating-point value.
  2209.      *
  2210.      * --    "OpenOffice.org's Documentation of the Microsoft
  2211.      *         Excel File Format"
  2212.      */
  2213.     private function _readNumber()
  2214.     {
  2215.         $pos $this->_pos;
  2216.         $length $this->_GetInt2d($this->_data$pos 2);
  2217.         $recordData substr($this->_data$pos 4$length);
  2218.         $pos += 4;
  2219.  
  2220.         // offset: 0; size: 2; index to row
  2221.         $row $this->_GetInt2d($this->_data$pos);
  2222.  
  2223.         // offset: 2; size 2; index to column
  2224.         $column $this->_GetInt2d($this->_data$pos 2);
  2225.         $columnString PHPExcel_Cell::stringFromColumnIndex($column);
  2226.  
  2227.         // Read cell?
  2228.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_sheet->getTitle()) ) {
  2229.             // offset 4; size: 2; index to XF record
  2230.             $xfindex $this->_GetInt2d($recordData4);
  2231.  
  2232.             $numValue $this->_createNumber($pos);
  2233.  
  2234.             // add cell style
  2235.             if (!$this->_readDataOnly{
  2236.                 $this->_sheet->getStyleByColumnAndRow($column$row 1)->applyFromArray($this->_xf[$xfindex]);
  2237.                 if (PHPExcel_Shared_Date::isDateTimeFormatCode($this->_xf[$xfindex]['numberformat']['code'])) {
  2238.                     $numValue PHPExcel_Shared_Date::ExcelToPHP($numValue);
  2239.                 }
  2240.             }
  2241.  
  2242.             // add cell value
  2243.             $this->_sheet->setCellValueExplicitByColumnAndRow($column$row 1$numValuePHPExcel_Cell_DataType::TYPE_NUMERIC);
  2244.         }
  2245.  
  2246.         // move stream pointer to next record
  2247.         $this->_pos += $length;
  2248.     }
  2249.  
  2250.     /**
  2251.      * Read FORMULA record
  2252.      * This record contains the token array and the result of a
  2253.      * formula cell.
  2254.      *
  2255.      * --    "OpenOffice.org's Documentation of the Microsoft
  2256.      *         Excel File Format"
  2257.      */
  2258.     private function _readFormula()
  2259.     {
  2260.         $pos $this->_pos;
  2261.         $length $this->_GetInt2d($this->_data$pos 2);
  2262.         $recordData substr($this->_data$pos 4$length);
  2263.         $pos += 4;
  2264.  
  2265.         // offset: 0; size: 2; row index
  2266.         $row $this->_GetInt2d($this->_data$pos);
  2267.  
  2268.         // offset: 2; size: 2; col index
  2269.         $column $this->_GetInt2d($this->_data$pos 2);
  2270.         $columnString PHPExcel_Cell::stringFromColumnIndex($column);
  2271.  
  2272.         // Read cell?
  2273.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_sheet->getTitle()) ) {
  2274.             // offset: 4; size: 2; XF index
  2275.             $xfindex $this->_GetInt2d($this->_data$pos 4);
  2276.  
  2277.             // offset: 6; size: 8; result of the formula
  2278.             if ((ord($this->_data[$pos 6]== 0&&
  2279.             (ord($this->_data[$pos 12]== 255&&
  2280.             (ord($this->_data[$pos 13]== 255)) {
  2281.                 //String formula. Result follows in appended STRING record
  2282.                 $dataType PHPExcel_Cell_DataType::TYPE_STRING;
  2283.                 $soff $pos $length;
  2284.                 $scode $this->_GetInt2d($this->_data$soff);
  2285.                 $slength $this->_GetInt2d($this->_data$soff 2);
  2286.                 $sdata substr($this->_data$soff 4$slength);
  2287.                 if ($this->_version == self::XLS_BIFF8{
  2288.                     $string $this->_readUnicodeStringLong($sdata);
  2289.                     $value $string['value'];
  2290.                 else {
  2291.                     $string $this->_readByteStringLong($sdata);
  2292.                     $value $string['value'];
  2293.                 }
  2294.             elseif ((ord($this->_data[$pos 6]== 1&&
  2295.             (ord($this->_data[$pos 12]== 255&&
  2296.             (ord($this->_data[$pos 13]== 255)) {
  2297.                 //Boolean formula. Result is in +2; 0=false,1=true
  2298.                 $dataType PHPExcel_Cell_DataType::TYPE_BOOL;
  2299.                 $value = (bool) ord($this->_data[$pos 8]);
  2300.             elseif ((ord($this->_data[$pos 6]== 2&&
  2301.             (ord($this->_data[$pos 12]== 255&&
  2302.             (ord($this->_data[$pos 13]== 255)) {
  2303.                 //Error formula. Error code is in +2
  2304.                 $dataType PHPExcel_Cell_DataType::TYPE_ERROR;
  2305.                 $value $this->_mapErrorCode(ord($this->_data[$pos 8]));
  2306.             elseif ((ord($this->_data[$pos 6]== 3&&
  2307.             (ord($this->_data[$pos 12]== 255&&
  2308.             (ord($this->_data[$pos 13]== 255)) {
  2309.                 //Formula result is a null string
  2310.                 $dataType PHPExcel_Cell_DataType::TYPE_NULL;
  2311.                 $value '';
  2312.             else {
  2313.                 // forumla result is a number, first 14 bytes like _NUMBER record
  2314.                 $dataType PHPExcel_Cell_DataType::TYPE_NUMERIC;
  2315.                 $value $this->_createNumber($pos);
  2316.             }
  2317.  
  2318.             // add cell style
  2319.             if (!$this->_readDataOnly{
  2320.                 $this->_sheet->getStyleByColumnAndRow($column$row 1)->applyFromArray($this->_xf[$xfindex]);
  2321.                 if (PHPExcel_Shared_Date::isDateTimeFormatCode($this->_xf[$xfindex]['numberformat']['code'])) {
  2322.                     $value PHPExcel_Shared_Date::ExcelToPHP($value);
  2323.                 }
  2324.             }
  2325.  
  2326.             // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
  2327.             // offset: 16: size: 4; not used
  2328.             // offset: 20: size: variable; formula structure
  2329.             $formulaStructure substr($recordData20);
  2330.  
  2331.             // add cell value
  2332.             try {
  2333.                 if ($this->_version != self::XLS_BIFF8{
  2334.                     throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
  2335.                 }
  2336.                 $formula $this->_getFormulaFromStructure($formulaStructure)// get human language
  2337.                 $this->_sheet->getCellByColumnAndRow($column$row 1)->setValueExplicit('=' $formulaPHPExcel_Cell_DataType::TYPE_FORMULA);
  2338.             catch (Exception $e{
  2339.                 $this->_sheet->setCellValueExplicitByColumnAndRow($column$row 1$value$dataType);
  2340.             }
  2341.         }
  2342.  
  2343.         // move stream pointer to next record
  2344.         $this->_pos += $length;
  2345.     }
  2346.  
  2347.     /**
  2348.      * Read BOOLERR record
  2349.      * This record represents a Boolean value or error value
  2350.      * cell.
  2351.      *
  2352.      * --    "OpenOffice.org's Documentation of the Microsoft
  2353.      *         Excel File Format"
  2354.      */
  2355.     private function _readBoolErr()
  2356.     {
  2357.         $pos $this->_pos;
  2358.         $length $this->_GetInt2d($this->_data$pos 2);
  2359.         $recordData substr($this->_data$pos 4$length);
  2360.         $pos += 4;
  2361.  
  2362.         // offset: 0; size: 2; row index
  2363.         $row $this->_GetInt2d($this->_data$pos);
  2364.  
  2365.         // offset: 2; size: 2; column index
  2366.         $column $this->_GetInt2d($this->_data$pos 2);
  2367.         $columnString PHPExcel_Cell::stringFromColumnIndex($column);
  2368.  
  2369.         // Read cell?
  2370.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_sheet->getTitle()) ) {
  2371.             // offset: 4; size: 2; index to XF record
  2372.             $xfindex $this->_GetInt2d($recordData4);
  2373.  
  2374.             // offset: 6; size: 1; the boolean value or error value
  2375.             $boolErr ord($recordData[6]);
  2376.  
  2377.             // offset: 7; size: 1; 0=boolean; 1=error
  2378.             $isError ord($recordData[7]);
  2379.  
  2380.             switch ($isError{
  2381.  
  2382.             case 0// boolean
  2383.                 $value = (bool) $boolErr;
  2384.  
  2385.                 // add cell value
  2386.                 $this->_sheet->getCellByColumnAndRow($column$row 1)->setValueExplicit($valuePHPExcel_Cell_DataType::TYPE_BOOL);
  2387.                 break;
  2388.  
  2389.             case 1// error type
  2390.                 $value $this->_mapErrorCode($boolErr);
  2391.  
  2392.                 // add cell value
  2393.                 $this->_sheet->getCellByColumnAndRow($column$row 1)->setValueExplicit($valuePHPExcel_Cell_DataType::TYPE_ERROR);
  2394.                 break;
  2395.             }
  2396.  
  2397.             // add cell style
  2398.             if (!$this->_readDataOnly{
  2399.                 $this->_sheet->getStyleByColumnAndRow($column$row 1)->applyFromArray($this->_xf[$xfindex]);
  2400.             }
  2401.         }
  2402.  
  2403.         // move stream pointer to next record
  2404.         $this->_pos += $length;
  2405.     }
  2406.  
  2407.     /**
  2408.      * Read MULBLANK record
  2409.      * This record represents a cell range of empty cells. All
  2410.      * cells are located in the same row
  2411.      *
  2412.      * --    "OpenOffice.org's Documentation of the Microsoft
  2413.      *         Excel File Format"
  2414.      */
  2415.     private function _readMulBlank()
  2416.     {
  2417.         $pos $this->_pos;
  2418.         $length $this->_GetInt2d($this->_data$pos 2);
  2419.         $recordData substr($this->_data$pos 4$length);
  2420.         $pos += 4;
  2421.  
  2422.         // offset: 0; size: 2; index to row
  2423.         $row $this->_GetInt2d($recordData0);
  2424.  
  2425.         // offset: 2; size: 2; index to first column
  2426.         $fc $this->_GetInt2d($recordData2);
  2427.  
  2428.         // offset: 4; size: 2 x nc; list of indexes to XF records
  2429.         // add style information
  2430.         if (!$this->_readDataOnly{
  2431.             for ($i 0$i $length 3++$i{
  2432.                 $columnString PHPExcel_Cell::stringFromColumnIndex($fc $i);
  2433.  
  2434.                 // Read cell?
  2435.                 if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_sheet->getTitle()) ) {
  2436.                     $xfindex $this->_GetInt2d($recordData$i);
  2437.                     $this->_sheet->getStyleByColumnAndRow($fc $i$row 1)->applyFromArray($this->_xf[$xfindex]);
  2438.                 }
  2439.             }
  2440.         }
  2441.  
  2442.         // offset: 6; size 2; index to last column (not needed)
  2443.  
  2444.         // move stream pointer to next record
  2445.         $this->_pos += $length;
  2446.     }
  2447.  
  2448.     /**
  2449.      * Read LABEL record
  2450.      * This record represents a cell that contains a string. In
  2451.      * BIFF8 it is usually replaced by the LABELSST record.
  2452.      * Excel still uses this record, if it copies unformatted
  2453.      * text cells to the clipboard.
  2454.      *
  2455.      * --    "OpenOffice.org's Documentation of the Microsoft
  2456.      *         Excel File Format"
  2457.      */
  2458.     private function _readLabel()
  2459.     {
  2460.         $pos $this->_pos;
  2461.         $length $this->_GetInt2d($this->_data$pos 2);
  2462.         $recordData substr($this->_data$pos 4$length);
  2463.         $pos += 4;
  2464.  
  2465.         // offset: 0; size: 2; index to row
  2466.         $row $this->_GetInt2d($this->_data$pos);
  2467.  
  2468.         // offset: 2; size: 2; index to column
  2469.         $column $this->_GetInt2d($this->_data$pos 2);
  2470.         $columnString PHPExcel_Cell::stringFromColumnIndex($column);
  2471.  
  2472.         // Read cell?
  2473.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_sheet->getTitle()) ) {
  2474.             // offset: 4; size: 2; XF index
  2475.             $xfindex $this->_GetInt2d($recordData4);
  2476.  
  2477.             // add cell value
  2478.             // todo: what if string is very long? continue record
  2479.             if ($this->_version == self::XLS_BIFF8{
  2480.                 $string $this->_readUnicodeStringLong(substr($recordData6));
  2481.                 $value $string['value'];
  2482.             else {
  2483.                 $string $this->_readByteStringLong(substr($recordData6));
  2484.                 $value $string['value'];
  2485.             }
  2486.             $this->_sheet->setCellValueExplicitByColumnAndRow($column$row 1$valuePHPExcel_Cell_DataType::TYPE_STRING);
  2487.  
  2488.             // add cell style
  2489.             if (!$this->_readDataOnly{
  2490.                 $this->_sheet->getStyleByColumnAndRow($column$row 1)->applyFromArray($this->_xf[$xfindex]);
  2491.             }
  2492.         }
  2493.  
  2494.         // move stream pointer to next record
  2495.         $this->_pos += $length;
  2496.     }
  2497.  
  2498.     /**
  2499.      * Read BLANK record
  2500.      */
  2501.     private function _readBlank()
  2502.     {
  2503.         $pos $this->_pos;
  2504.         $length $this->_GetInt2d($this->_data$pos 2);
  2505.         $recordData substr($this->_data$pos 4$length);
  2506.         $pos += 4;
  2507.  
  2508.         // offset: 0; size: 2; row index
  2509.         $row $this->_GetInt2d($recordData0);
  2510.  
  2511.         // offset: 2; size: 2; col index
  2512.         $col $this->_GetInt2d($recordData2);
  2513.         $columnString PHPExcel_Cell::stringFromColumnIndex($col);
  2514.  
  2515.         // Read cell?
  2516.         if !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString$row 1$this->_sheet->getTitle()) ) {
  2517.             // offset: 4; size: 2; XF index
  2518.             $xfindex $this->_GetInt2d($recordData4);
  2519.  
  2520.             // add style information
  2521.             if (!$this->_readDataOnly{
  2522.                 $this->_sheet->getStyleByColumnAndRow($col$row 1)->applyFromArray($this->_xf[$xfindex]);
  2523.             }
  2524.         }
  2525.  
  2526.         // move stream pointer to next record
  2527.         $this->_pos += $length;
  2528.     }
  2529.  
  2530.     /**
  2531.      * Read IMDATA record
  2532.      */
  2533.     private function _readImData()
  2534.     {
  2535.  
  2536.         $spos $this->_pos;
  2537.         $length $this->_GetInt2d($this->_data$spos 2);
  2538.         // get spliced record data
  2539.         $recordData $this->_getSplicedRecordData();
  2540.  
  2541.         // UNDER CONSTRUCTION
  2542.  
  2543.         // offset: 0; size: 2; image format
  2544.         $cf $this->_GetInt2d($recordData0);
  2545.         // offset: 2; size: 2; environment from which the file was written
  2546.         $env $this->_GetInt2d($recordData2);
  2547.         // offset: 4; size: 4; length of the image data
  2548.         $lcb $this->_GetInt4d($recordData4);
  2549.         // offset: 8; size: var; image data
  2550.         $iData substr($recordData8);
  2551.  
  2552.         switch ($cf{
  2553.  
  2554.         case 0x09// Windows bitmap format
  2555.             // BITMAPCOREINFO
  2556.             // 1. BITMAPCOREHEADER
  2557.             // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure
  2558.             $bcSize $this->_GetInt4d($iData0);
  2559.             var_dump($bcSize);
  2560.             // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels
  2561.             $bcWidth $this->_GetInt2d($iData4);
  2562.             var_dump($bcWidth);
  2563.             // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels.
  2564.             $bcHeight $this->_GetInt2d($iData6);
  2565.             var_dump($bcHeight);
  2566.             $ih imagecreatetruecolor($bcWidth$bcHeight);
  2567.             // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1
  2568.             // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24
  2569.             $bcBitCount $this->_GetInt2d($iData10);
  2570.             var_dump($bcBitCount);
  2571.  
  2572.             $rgbString substr($iData12);
  2573.             $rgbTriples array();
  2574.             while (strlen($rgbString0{
  2575.                 $rgbTriples[unpack('Cb/Cg/Cr'$rgbString);
  2576.                 $rgbString substr($rgbString3);
  2577.             }
  2578.             $x 0;
  2579.             $y 0;
  2580.             foreach ($rgbTriples as $i => $rgbTriple{
  2581.                 $color imagecolorallocate($ih$rgbTriple['r']$rgbTriple['g']$rgbTriple['b']);
  2582.                 imagesetpixel($ih$x$bcHeight $y$color);
  2583.                 $x ($x 1$bcWidth;
  2584.                 $y $y floor(($x 1$bcWidth);
  2585.             }
  2586.             //imagepng($ih, 'image.png');
  2587.  
  2588.             $drawing new PHPExcel_Worksheet_Drawing();
  2589.             $drawing->setPath($filename);
  2590.             $drawing->setWorksheet($this->_sheet);
  2591.  
  2592.             break;
  2593.  
  2594.         case 0x02// Windows metafile or Macintosh PICT format
  2595.         case 0x0e// native format
  2596.         default;
  2597.             break;
  2598.  
  2599.         }
  2600.  
  2601.         // _getSplicedRecordData() takes care of moving current position in data stream
  2602.     }
  2603.  
  2604.     /**
  2605.      * Reads a record from current position in data stream and continues reading data as long as CONTINUE
  2606.      * records are found. Splices the record data pieces and returns the combined string as if record data
  2607.      * is in one piece.
  2608.      * Moves to next current position in data stream to start of next record different from a CONtINUE record
  2609.      *
  2610.      * @return string 
  2611.      */
  2612.     private function _getSplicedRecordData()
  2613.     {
  2614.         $data '';
  2615.  
  2616.         do {
  2617.             // offset: 0; size: 2; identifier
  2618.             $identifier $this->_GetInt2d($this->_data$this->_pos);
  2619.             // offset: 2; size: 2; length
  2620.             $length $this->_GetInt2d($this->_data$this->_pos + 2);
  2621.             $data .= substr($this->_data$this->_pos + 4$length);
  2622.  
  2623.             $this->_pos += $length;
  2624.             $nextIdentifier $this->_GetInt2d($this->_data$this->_pos);
  2625.         }
  2626.         while ($nextIdentifier == self::XLS_Type_CONTINUE);
  2627.  
  2628.         return $data;
  2629.  
  2630.     }
  2631.  
  2632.     /**
  2633.      * Convert formula structure into human readable Excel formula like 'A3+A5*5'
  2634.      *
  2635.      * @param string $formulaStructure The complete binary data for the formula
  2636.      * @return string Human readable formula
  2637.      */
  2638.     private function _getFormulaFromStructure($formulaStructure)
  2639.     {
  2640.         // offset: 0; size: 2; size of the following formula data
  2641.         $sz $this->_GetInt2d($formulaStructure0);
  2642.  
  2643.         // offset: 2; size: sz
  2644.         $formulaData substr($formulaStructure2$sz);
  2645.  
  2646.         // for debug: dump the formula data
  2647.         //echo '<xmp>';
  2648.         //echo 'size: ' . $sz . "\n";
  2649.         //echo 'the entire formula data: ';
  2650.         //$this->_dump($formulaData);
  2651.         //echo "\n----\n";
  2652.  
  2653.         // offset: 2 + sz; size: variable (optional)
  2654.         if (strlen($formulaStructure$sz{
  2655.             $additionalData substr($formulaStructure$sz);
  2656.  
  2657.             // for debug: dump the additional data
  2658.             //echo 'the entire additional data: ';
  2659.             //$this->_dump($additionalData);
  2660.             //echo "\n----\n";
  2661.  
  2662.         else {
  2663.             $additionalData '';
  2664.         }
  2665.  
  2666.         return $this->_getFormulaFromData($formulaData$additionalData);
  2667.     }
  2668.  
  2669.     /**
  2670.      * Take formula data and additional data for formula and return human readable formula
  2671.      *
  2672.      * @param string $formulaData The binary data for the formula itself
  2673.      * @param string $additionalData Additional binary data going with the formula
  2674.      * @return string Human readable formula
  2675.      */
  2676.     private function _getFormulaFromData($formulaData,  $additionalData '')
  2677.     {
  2678.         // start parsing the formula data
  2679.         $tokens array();
  2680.  
  2681.         while (strlen($formulaDataand $token $this->_getNextToken($formulaData)) {
  2682.             $tokens[$token;
  2683.             $formulaData substr($formulaData$token['size']);
  2684.  
  2685.             // for debug: dump the token
  2686.             //var_dump($token);
  2687.         }
  2688.  
  2689.         $formulaString $this->_createFormulaFromTokens($tokens$additionalData);
  2690.  
  2691.         return $formulaString;
  2692.     }
  2693.  
  2694.     /**
  2695.      * Take array of tokens together with additional data for formula and return human readable formula
  2696.      *
  2697.      * @param array $tokens 
  2698.      * @param array $additionalData Additional binary data going with the formula
  2699.      * @return string Human readable formula
  2700.      */
  2701.     private function _createFormulaFromTokens($tokens$additionalData)
  2702.     {
  2703.         $formulaStrings array();
  2704.         foreach ($tokens as $token{
  2705.             // initialize spaces
  2706.             $space0 = isset($space0$space0 ''// spaces before next token, not tParen
  2707.             $space1 = isset($space1$space1 ''// carriage returns before next token, not tParen
  2708.             $space2 = isset($space2$space2 ''// spaces before opening parenthesis
  2709.             $space3 = isset($space3$space3 ''// carriage returns before opening parenthesis
  2710.             $space4 = isset($space4$space4 ''// spaces before closing parenthesis
  2711.             $space5 = isset($space5$space5 ''// carriage returns before closing parenthesis
  2712.  
  2713.             switch ($token['name']{
  2714.             case 'tAdd'// addition
  2715.             case 'tConcat'// addition
  2716.             case 'tDiv'// division
  2717.             case 'tEQ'// equaltiy
  2718.             case 'tGE'// greater than or equal
  2719.             case 'tGT'// greater than
  2720.             case 'tIsect'// intersection
  2721.             case 'tLE'// less than or equal
  2722.             case 'tList'// less than or equal
  2723.             case 'tLT'// less than
  2724.             case 'tMul'// multiplication
  2725.             case 'tNE'// multiplication
  2726.             case 'tPower'// power
  2727.             case 'tRange'// range
  2728.             case 'tSub'// subtraction
  2729.                 $op2 array_pop($formulaStrings);
  2730.                 $op1 array_pop($formulaStrings);
  2731.                 $formulaStrings["$op1$space1$space0{$token['data']}$op2";
  2732.                 unset($space0$space1);
  2733.                 break;
  2734.             case 'tUplus'// unary plus
  2735.             case 'tUminus'// unary minus
  2736.                 $op array_pop($formulaStrings);
  2737.                 $formulaStrings["$space1$space0{$token['data']}$op";
  2738.                 unset($space0$space1);
  2739.                 break;
  2740.             case 'tPercent'// percent sign
  2741.                 $op array_pop($formulaStrings);
  2742.                 $formulaStrings["$op$space1$space0{$token['data']}";
  2743.                 unset($space0$space1);
  2744.                 break;
  2745.             case 'tAttrVolatile'// indicates volatile function
  2746.             case 'tAttrIf':
  2747.             case 'tAttrSkip':
  2748.             case 'tAttrChoose':
  2749.                 // token is only important for Excel formula evaluator
  2750.                 // do nothing
  2751.                 break;
  2752.             case 'tAttrSpace'// space / carriage return
  2753.                 // space will be used when next token arrives, do not alter formulaString stack
  2754.                 switch ($token['data']['spacetype']{
  2755.                 case 'type0':
  2756.                     $space0 str_repeat(' '$token['data']['spacecount']);
  2757.                     break;
  2758.                 case 'type1':
  2759.                     $space1 str_repeat("\n"$token['data']['spacecount']);
  2760.                     break;
  2761.                 case 'type2':
  2762.                     $space2 str_repeat(' '$token['data']['spacecount']);
  2763.                     break;
  2764.                 case 'type3':
  2765.                     $space3 str_repeat("\n"$token['data']['spacecount']);
  2766.                     break;
  2767.                 case 'type4':
  2768.                     $space4 str_repeat(' '$token['data']['spacecount']);
  2769.                     break;
  2770.                 case 'type5':
  2771.                     $space5 str_repeat("\n"$token['data']['spacecount']);
  2772.                     break;
  2773.                 }
  2774.                 break;
  2775.             case 'tAttrSum'// SUM function with one parameter
  2776.                 $op array_pop($formulaStrings);
  2777.                 $formulaStrings["{$space1}{$space0}SUM($op)";
  2778.                 unset($space0$space1);
  2779.                 break;
  2780.             case 'tFunc'// function with fixed number of arguments
  2781.             case 'tFuncV'// function with variable number of arguments
  2782.                 $ops array()// array of operators
  2783.                 for ($i 0$i $token['data']['args']++$i{
  2784.                     $ops[array_pop($formulaStrings);
  2785.                 }
  2786.                 $ops array_reverse($ops);
  2787.                 $formulaStrings["$space1$space0{$token['data']['function']}(implode(','$ops")";
  2788.                 unset($space0$space1);
  2789.                 break;
  2790.             case 'tParen'// parenthesis
  2791.                 $expression array_pop($formulaStrings);
  2792.                 $formulaStrings["$space3$space2($expression$space5$space4)";
  2793.                 unset($space2$space3$space4$space5);
  2794.                 break;
  2795.             case 'tArray'// array constant
  2796.                 $constantArray $this->_readBIFF8ConstantArray($additionalData);
  2797.                 $formulaStrings[$space1 $space0 $constantArray['value'];
  2798.                 $additionalData substr($additionalData$constantArray['size'])// bite of chunk of additional data
  2799.                 unset($space0$space1);
  2800.                 break;
  2801.             case 'tMemArea':
  2802.                 // bite off chunk of additional data
  2803.                 $cellRangeAddressList $this->_readBIFF8CellRangeAddressList($additionalData);
  2804.                 $additionalData substr($additionalData$cellRangeAddressList['size']);
  2805.                 $formulaStrings["$space1$space0{$token['data']}";
  2806.                 unset($space0$space1);
  2807.                 break;
  2808.             case 'tArea'// cell range address
  2809.             case 'tBool'// boolean
  2810.             case 'tErr'// error code
  2811.             case 'tInt'// integer
  2812.             case 'tMemErr':
  2813.             case 'tMemFunc':
  2814.             case 'tMissArg':
  2815.             case 'tName':
  2816.             case 'tNum'// number
  2817.             case 'tRef'// single cell reference
  2818.             case 'tRef3d'// 3d cell reference
  2819.             case 'tArea3d'// 3d cell range reference
  2820.             case 'tStr'// string
  2821.                 $formulaStrings["$space1$space0{$token['data']}";
  2822.                 unset($space0$space1);
  2823.                 break;
  2824.             }
  2825.         }
  2826.         $formulaString $formulaStrings[0];
  2827.  
  2828.         // for debug: dump the human readable formula
  2829.         //echo '----' . "\n";
  2830.         //echo 'Formula: ' . $formulaString;
  2831.  
  2832.         return $formulaString;
  2833.     }
  2834.  
  2835.     /**
  2836.      * Fetch next token from binary formula data
  2837.      *
  2838.      * @param string Formula data
  2839.      * @throws Exception
  2840.      */
  2841.     private function _getNextToken($formulaData)
  2842.     {
  2843.         // offset: 0; size: 1; token id
  2844.         $id ord($formulaData[0])// token id
  2845.         $name false// initialize token name
  2846.  
  2847.         switch ($id{
  2848.         case 0x03$name 'tAdd';        $size 1;    $data '+';    break;
  2849.         case 0x04$name 'tSub';        $size 1;    $data '-';    break;
  2850.         case 0x05$name 'tMul';        $size 1;    $data '*';    break;
  2851.         case 0x06$name 'tDiv';        $size 1;    $data '/';    break;
  2852.         case 0x07$name 'tPower';    $size 1;    $data '^';    break;
  2853.         case 0x08$name 'tConcat';    $size 1;    $data '&';    break;
  2854.         case 0x09$name 'tLT';        $size 1;    $data '<';    break;
  2855.         case 0x0A$name 'tLE';        $size 1;    $data '<=';    break;
  2856.         case 0x0B$name 'tEQ';        $size 1;    $data '=';    break;
  2857.         case 0x0C$name 'tGE';        $size 1;    $data '>=';    break;
  2858.         case 0x0D$name 'tGT';        $size 1;    $data '>';    break;
  2859.         case 0x0E$name 'tNE';        $size 1;    $data '<>';    break;
  2860.         case 0x0F$name 'tIsect';    $size 1;    $data ' ';    break;
  2861.         case 0x10$name 'tList';        $size 1;    $data ',';    break;
  2862.         case 0x11$name 'tRange';    $size 1;    $data ':';    break;
  2863.         case 0x12$name 'tUplus';    $size 1;    $data '+';    break;
  2864.         case 0x13$name 'tUminus';    $size 1;    $data '-';    break;
  2865.         case 0x14$name 'tPercent';    $size 1;    $data '%';    break;
  2866.         case 0x15// parenthesis
  2867.             $name  'tParen';
  2868.             $size  1;
  2869.             $data null;
  2870.             break;
  2871.         case 0x16// missing argument
  2872.             $name 'tMissArg';
  2873.             $size 1;
  2874.             $data '';
  2875.             break;
  2876.         case 0x17// string
  2877.             $name 'tStr';
  2878.             // offset: 1; size: var; Unicode string, 8-bit string length
  2879.             $string $this->_readUnicodeStringShort(substr($formulaData1));
  2880.             $size $string['size'];
  2881.             $data $this->_UTF8toExcelDoubleQuoted($string['value']);
  2882.             break;
  2883.         case 0x19// Special attribute
  2884.             // offset: 1; size: 1; attribute type flags:
  2885.             switch (ord($formulaData[1])) {
  2886.             case 0x01:
  2887.                 $name 'tAttrVolatile';
  2888.                 $size 4;
  2889.                 $data null;
  2890.                 break;
  2891.             case 0x02:
  2892.                 $name 'tAttrIf';
  2893.                 $size 4;
  2894.                 $data null;
  2895.                 break;
  2896.             case 0x04:
  2897.                 $name 'tAttrChoose';
  2898.                 // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
  2899.                 $nc $this->_GetInt2d($formulaData2);
  2900.                 // offset: 4; size: 2 * $nc
  2901.                 // offset: 4 + 2 * $nc; size: 2
  2902.                 $size $nc 6;
  2903.                 $data null;
  2904.                 break;
  2905.             case 0x08:
  2906.                 $name 'tAttrSkip';
  2907.                 $size 4;
  2908.                 $data null;
  2909.                 break;
  2910.             case 0x10:
  2911.                 $name 'tAttrSum';
  2912.                 $size 4;
  2913.                 $data null;
  2914.                 break;
  2915.             case 0x40:
  2916.             case 0x41:
  2917.                 $name 'tAttrSpace';
  2918.                 $size 4;
  2919.                 // offset: 2; size: 2; space type and position
  2920.                 switch (ord($formulaData[2])) {
  2921.                 case 0x00:
  2922.                     $spacetype 'type0';
  2923.                     break;
  2924.                 case 0x01:
  2925.                     $spacetype 'type1';
  2926.                     break;
  2927.                 case 0x02:
  2928.                     $spacetype 'type2';
  2929.                     break;
  2930.                 case 0x03:
  2931.                     $spacetype 'type3';
  2932.                     break;
  2933.                 case 0x04:
  2934.                     $spacetype 'type4';
  2935.                     break;
  2936.                 case 0x05:
  2937.                     $spacetype 'type5';
  2938.                     break;
  2939.                 default:
  2940.                     throw new Exception('Unrecognized space type in tAttrSpace token');
  2941.                     break;
  2942.                 }
  2943.                 // offset: 3; size: 1; number of inserted spaces/carriage returns
  2944.                 $spacecount ord($formulaData[3]);
  2945.  
  2946.                 $data array('spacetype' => $spacetype'spacecount' => $spacecount);
  2947.                 break;
  2948.             default:
  2949.                 throw new Exception('Unrecognized attribute flag in tAttr token');
  2950.                 break;
  2951.             }
  2952.             break;
  2953.         case 0x1C// error code
  2954.             // offset: 1; size: 1; error code
  2955.             $name 'tErr';
  2956.             $size 2;
  2957.             $data $this->_mapErrorCode(ord($formulaData[1]));
  2958.             break;
  2959.         case 0x1D// boolean
  2960.             // offset: 1; size: 1; 0 = false, 1 = true;
  2961.             $name 'tBool';
  2962.             $size 2;
  2963.             $data ord($formulaData[1]'TRUE' 'FALSE';
  2964.             break;
  2965.         case 0x1E// integer
  2966.             // offset: 1; size: 2; unsigned 16-bit integer
  2967.             $name 'tInt';
  2968.             $size 3;
  2969.             $data $this->_GetInt2d($formulaData1);
  2970.             break;
  2971.         case 0x1F// number
  2972.             // offset: 1; size: 8;
  2973.             $name 'tNum';
  2974.             $size 9;
  2975.             $data $this->_extractNumber(substr($formulaData1));
  2976.             break;
  2977.         case 0x40// array constant
  2978.         case 0x60// array constant
  2979.             // offset: 1; size: 7; not used
  2980.             $name 'tArray';
  2981.             $size 8;
  2982.             $data null;
  2983.             break;
  2984.         case 0x41// function with fixed number of arguments
  2985.             $name 'tFunc';
  2986.             $size 3;
  2987.             // offset: 1; size: 2; index to built-in sheet function
  2988.             switch ($this->_GetInt2d($formulaData1)) {
  2989.             case   2$function 'ISNA';             $args 1;     break;
  2990.             case   3$function 'ISERROR';         $args 1;     break;
  2991.             case  10$function 'NA';             $args 0;     break;
  2992.             case  15$function 'SIN';             $args 1;     break;
  2993.             case  16$function 'COS';             $args 1;     break;
  2994.             case  17$function 'TAN';             $args 1;     break;
  2995.             case  18$function 'ATAN';             $args 1;     break;
  2996.             case  19$function 'PI';             $args 0;     break;
  2997.             case  20$function 'SQRT';             $args 1;     break;
  2998.             case  21$function 'EXP';             $args 1;     break;
  2999.             case  22$function 'LN';             $args 1;     break;
  3000.             case  23$function 'LOG10';             $args 1;     break;
  3001.             case  24$function 'ABS';             $args 1;     break;
  3002.             case  25$function 'INT';             $args 1;     break;
  3003.             case  26$function 'SIGN';             $args 1;     break;
  3004.             case  27$function 'ROUND';             $args 2;     break;
  3005.             case  30$function 'REPT';             $args 2;     break;
  3006.             case  31$function 'MID';             $args 3;     break;
  3007.             case  32$function 'LEN';             $args 1;     break;
  3008.             case  33$function 'VALUE';             $args 1;     break;
  3009.             case  34$function 'TRUE';             $args 0;     break;
  3010.             case  35$function 'FALSE';             $args 0;     break;
  3011.             case  38$function 'NOT';             $args 1;     break;
  3012.             case  39$function 'MOD';             $args 2;    break;
  3013.             case  40$function 'DCOUNT';         $args 3;    break;
  3014.             case  41$function 'DSUM';             $args 3;    break;
  3015.             case  42$function 'DAVERAGE';         $args 3;    break;
  3016.             case  43$function 'DMIN';             $args 3;    break;
  3017.             case  44$function 'DMAX';             $args 3;    break;
  3018.             case  45$function 'DSTDEV';         $args 3;    break;
  3019.             case  48$function 'TEXT';             $args 2;    break;
  3020.             case  61$function 'MIRR';             $args 3;    break;
  3021.             case  63$function 'RAND';             $args 0;    break;
  3022.             case  65$function 'DATE';             $args 3;    break;
  3023.             case  66$function 'TIME';             $args 3;    break;
  3024.             case  67$function 'DAY';             $args 1;    break;
  3025.             case  68$function 'MONTH';             $args 1;    break;
  3026.             case  69$function 'YEAR';             $args 1;    break;
  3027.             case  71$function 'HOUR';             $args 1;    break;
  3028.             case  72$function 'MINUTE';         $args 1;    break;
  3029.             case  73$function 'SECOND';         $args 1;    break;
  3030.             case  74$function 'NOW';             $args 0;    break;
  3031.             case  75$function 'AREAS';             $args 1;    break;
  3032.             case  76$function 'ROWS';             $args 1;    break;
  3033.             case  77$function 'COLUMNS';         $args 1;    break;
  3034.             case  83$function 'TRANSPOSE';         $args 1;    break;
  3035.             case  86$function 'TYPE';             $args 1;    break;
  3036.             case  97$function 'ATAN2';             $args 2;    break;
  3037.             case  98$function 'ASIN';             $args 1;    break;
  3038.             case  99$function 'ACOS';             $args 1;    break;
  3039.             case 105$function 'ISREF';             $args 1;    break;
  3040.             case 111$function 'CHAR';             $args 1;    break;
  3041.             case 112$function 'LOWER';             $args 1;    break;
  3042.             case 113$function 'UPPER';             $args 1;    break;
  3043.             case 114$function 'PROPER';         $args 1;    break;
  3044.             case 117$function 'EXACT';             $args 2;    break;
  3045.             case 118$function 'TRIM';             $args 1;    break;
  3046.             case 119$function 'REPLACE';         $args 4;    break;
  3047.             case 121$function 'CODE';             $args 1;    break;
  3048.             case 126$function 'ISERR';             $args 1;    break;
  3049.             case 127$function 'ISTEXT';         $args 1;    break;
  3050.             case 128$function 'ISNUMBER';         $args 1;    break;
  3051.             case 129$function 'ISBLANK';         $args 1;    break;
  3052.             case 130$function 'T';                 $args 1;    break;
  3053.             case 131$function 'N';                 $args 1;    break;
  3054.             case 140$function 'DATEVALUE';         $args 1;    break;
  3055.             case 141$function 'TIMEVALUE';         $args 1;    break;
  3056.             case 142$function 'SLN';             $args 3;    break;
  3057.             case 143$function 'SYD';             $args 4;    break;
  3058.             case 162$function 'CLEAN';             $args 1;    break;
  3059.             case 163$function 'MDETERM';         $args 1;    break;
  3060.             case 164$function 'MINVERSE';         $args 1;    break;
  3061.             case 165$function 'MMULT';             $args 2;    break;
  3062.             case 184$function 'FACT';             $args 1;    break;
  3063.             case 189$function 'DPRODUCT';         $args 3;    break;
  3064.             case 190$function 'ISNONTEXT';         $args 1;    break;
  3065.             case 195$function 'DSTDEVP';         $args 3;    break;
  3066.             case 196$function 'DVARP';             $args 3;    break;
  3067.             case 198$function 'ISLOGICAL';         $args 1;    break;
  3068.             case 199$function 'DCOUNTA';         $args 3;    break;
  3069.             case 207$function 'REPLACEB';         $args 4;    break;
  3070.             case 210$function 'MIDB';             $args 3;    break;
  3071.             case 211$function 'LENB';             $args 1;    break;
  3072.             case 212$function 'ROUNDUP';         $args 2;    break;
  3073.             case 213$function 'ROUNDDOWN';         $args 2;    break;
  3074.             case 214$function 'ASC';             $args 1;    break;
  3075.             case 215$function 'DBCS';             $args 1;    break;
  3076.             case 221$function 'TODAY';             $args 0;    break;
  3077.             case 229$function 'SINH';             $args 1;    break;
  3078.             case 230$function 'COSH';             $args 1;    break;
  3079.             case 231$function 'TANH';             $args 1;    break;
  3080.             case 232$function 'ASINH';             $args 1;    break;
  3081.             case 233$function 'ACOSH';             $args 1;    break;
  3082.             case 234$function 'ATANH';             $args 1;    break;
  3083.             case 235$function 'DGET';             $args 3;    break;
  3084.             case 244$function 'INFO';             $args 1;    break;
  3085.             case 252$function 'FREQUENCY';         $args 2;    break;
  3086.             case 261$function 'ERROR.TYPE';     $args 1;    break;
  3087.             case 271$function 'GAMMALN';         $args 1;    break;
  3088.             case 273$function 'BINOMDIST';         $args 4;    break;
  3089.             case 274$function 'CHIDIST';         $args 2;    break;
  3090.             case 275$function 'CHIINV';         $args 2;    break;
  3091.             case 276$function 'COMBIN';         $args 2;    break;
  3092.             case 277$function 'CONFIDENCE';     $args 3;    break;
  3093.             case 278$function 'CRITBINOM';         $args 3;    break;
  3094.             case 279$function 'EVEN';             $args 1;    break;
  3095.             case 280$function 'EXPONDIST';         $args 3;    break;
  3096.             case 281$function 'FDIST';             $args 3;    break;
  3097.             case 282$function 'FINV';             $args 3;    break;
  3098.             case 283$function 'FISHER';         $args 1;    break;
  3099.             case 284$function 'FISHERINV';         $args 1;    break;
  3100.             case 285$function 'FLOOR';             $args 2;    break;
  3101.             case 286$function 'GAMMADIST';         $args 4;    break;
  3102.             case 287$function 'GAMMAINV';         $args 3;    break;
  3103.             case 288$function 'CEILING';         $args 2;    break;
  3104.             case 289$function 'HYPGEOMDIST';    $args 4;    break;
  3105.             case 290$function 'LOGNORMDIST';    $args 3;    break;
  3106.             case 291$function 'LOGINV';            $args 3;    break;
  3107.             case 292$function 'NEGBINOMDIST';    $args 3;    break;
  3108.             case 293$function 'NORMDIST';        $args 4;    break;
  3109.             case 294$function 'NORMSDIST';        $args 1;    break;
  3110.             case 295$function 'NORMINV';        $args 3;    break;
  3111.             case 296$function 'NORMSINV';        $args 1;    break;
  3112.             case 297$function 'STANDARDIZE';    $args 3;    break;
  3113.             case 298$function 'ODD';            $args 1;    break;
  3114.             case 299$function 'PERMUT';            $args 2;    break;
  3115.             case 300$function 'POISSON';        $args 3;    break;
  3116.             case 301$function 'TDIST';            $args 3;    break;
  3117.             case 302$function 'WEIBULL';        $args 4;    break;
  3118.             case 303$function 'SUMXMY2';        $args 2;    break;
  3119.             case 304$function 'SUMX2MY2';        $args 2;    break;
  3120.             case 305$function 'SUMX2PY2';        $args 2;    break;
  3121.             case 306$function 'CHITEST';        $args 2;    break;
  3122.             case 307$function 'CORREL';            $args 2;    break;
  3123.             case 308$function 'COVAR';            $args 2;    break;
  3124.             case 309$function 'FORECAST';        $args 3;    break;
  3125.             case 310$function 'FTEST';            $args 2;    break;
  3126.             case 311$function 'INTERCEPT';        $args 2;    break;
  3127.             case 312$function 'PEARSON';        $args 2;    break;
  3128.             case 313$function 'RSQ';            $args 2;    break;
  3129.             case 314$function 'STEYX';            $args 2;    break;
  3130.             case 315$function 'SLOPE';            $args 2;    break;
  3131.             case 316$function 'TTEST';            $args 4;    break;
  3132.             case 325$function 'LARGE';            $args 2;    break;
  3133.             case 326$function 'SMALL';            $args 2;    break;
  3134.             case 327$function 'QUARTILE';        $args 2;    break;
  3135.             case 328$function 'PERCENTILE';        $args 2;    break;
  3136.             case 331$function 'TRIMMEAN';        $args 2;    break;
  3137.             case 332$function 'TINV';            $args 2;    break;
  3138.             case 337$function 'POWER';            $args 2;    break;
  3139.             case 342$function 'RADIANS';        $args 1;    break;
  3140.             case 343$function 'DEGREES';        $args 1;    break;
  3141.             case 346$function 'COUNTIF';        $args 2;    break;
  3142.             case 347$function 'COUNTBLANK';        $args 1;    break;
  3143.             case 350$function 'ISPMT';            $args 4;    break;
  3144.             case 351$function 'DATEDIF';        $args 3;    break;
  3145.             case 352$function 'DATESTRING';        $args 1;    break;
  3146.             case 353$function 'NUMBERSTRING';    $args 2;    break;
  3147.             case 360$function 'PHONETIC';        $args 1;    break;
  3148.             default:
  3149.                 throw new Exception('Unrecognized function in formula');
  3150.                 break;
  3151.             }
  3152.             $data array('function' => $function'args' => $args);
  3153.             break;
  3154.         case 0x42// function with variable number of arguments
  3155.         case 0x62// function with variable number of arguments
  3156.             $name 'tFuncV';
  3157.             $size 4;
  3158.             // offset: 1; size: 1; number of arguments
  3159.             $args ord($formulaData[1]);
  3160.             // offset: 2: size: 2; index to built-in sheet function
  3161.             switch ($this->_GetInt2d($formulaData2)) {
  3162.             case   0$function 'COUNT';            break;
  3163.             case   1$function 'IF';                break;
  3164.             case   4$function 'SUM';            break;
  3165.             case   5$function 'AVERAGE';        break;
  3166.             case   6$function 'MIN';            break;
  3167.             case   7$function 'MAX';            break;
  3168.             case   8$function 'ROW';            break;
  3169.             case   9$function 'COLUMN';            break;
  3170.             case  11$function 'NPV';            break;
  3171.             case  12$function 'STDEV';            break;
  3172.             case  13$function 'DOLLAR';            break;
  3173.             case  14$function 'FIXED';            break;
  3174.             case  28$function 'LOOKUP';            break;
  3175.             case  29$function 'INDEX';            break;
  3176.             case  36$function 'AND';            break;
  3177.             case  37$function 'OR';                break;
  3178.             case  46$function 'VAR';            break;
  3179.             case  49$function 'LINEST';            break;
  3180.             case  50$function 'TREND';            break;
  3181.             case  51$function 'LOGEST';            break;
  3182.             case  52$function 'GROWTH';            break;
  3183.             case  56$function 'PV';                break;
  3184.             case  57$function 'FV';                break;
  3185.             case  58$function 'NPER';            break;
  3186.             case  59$function 'PMT';            break;
  3187.             case  60$function 'RATE';            break;
  3188.             case  62$function 'IRR';            break;
  3189.             case  64$function 'MATCH';            break;
  3190.             case  70$function 'WEEKDAY';        break;
  3191.             case  78$function 'OFFSET';            break;
  3192.             case  82$function 'SEARCH';            break;
  3193.             case 100$function 'CHOOSE';            break;
  3194.             case 101$function 'HLOOKUP';        break;
  3195.             case 102$function 'VLOOKUP';        break;
  3196.             case 109$function 'LOG';            break;
  3197.             case 115$function 'LEFT';            break;
  3198.             case 116$function 'RIGHT';            break;
  3199.             case 120$function 'SUBSTITUTE';        break;
  3200.             case 124$function 'FIND';            break;
  3201.             case 125$function 'CELL';            break;
  3202.             case 144$function 'DDB';            break;
  3203.             case 148$function 'INDIRECT';        break;
  3204.             case 167$function 'IPMT';            break;
  3205.             case 168$function 'PPMT';            break;
  3206.             case 169$function 'COUNTA';            break;
  3207.             case 183$function 'PRODUCT';        break;
  3208.             case 193$function 'STDEVP';            break;
  3209.             case 194$function 'VARP';            break;
  3210.             case 197$function 'TRUNC';            break;
  3211.             case 204$function 'USDOLLAR';        break;
  3212.             case 205$function 'FINDB';            break;
  3213.             case 206$function 'SEARCHB';        break;
  3214.             case 208$function 'LEFTB';            break;
  3215.             case 209$function 'RIGHTB';            break;
  3216.             case 216$function 'RANK';            break;
  3217.             case 219$function 'ADDRESS';        break;
  3218.             case 220$function 'DAYS360';        break;
  3219.             case 222$function 'VDB';            break;
  3220.             case 227$function 'MEDIAN';            break;
  3221.             case 228$function 'SUMPRODUCT';        break;
  3222.             case 247$function 'DB';                break;
  3223.             case 269$function 'AVEDEV';            break;
  3224.             case 270$function 'BETADIST';        break;
  3225.             case 272$function 'BETAINV';        break;
  3226.             case 317$function 'PROB';            break;
  3227.             case 318$function 'DEVSQ';            break;
  3228.             case 319$function 'GEOMEAN';        break;
  3229.             case 320$function 'HARMEAN';        break;
  3230.             case 321$function 'SUMSQ';            break;
  3231.             case 322$function 'KURT';            break;
  3232.             case 323$function 'SKEW';            break;
  3233.             case 324$function 'ZTEST';            break;
  3234.             case 329$function 'PERCENTRANK';    break;
  3235.             case 330$function 'MODE';            break;
  3236.             case 336$function 'CONCATENATE';    break;
  3237.             case 344$function 'SUBTOTAL';        break;
  3238.             case 345$function 'SUMIF';            break;
  3239.             case 354$function 'ROMAN';            break;
  3240.             case 358$function 'GETPIVOTDATA';    break;
  3241.             case 359$function 'HYPERLINK';        break;
  3242.             case 361$function 'AVERAGEA';        break;
  3243.             case 362$function 'MAXA';            break;
  3244.             case 363$function 'MINA';            break;
  3245.             case 364$function 'STDEVPA';        break;
  3246.             case 365$function 'VARPA';            break;
  3247.             case 366$function 'STDEVA';            break;
  3248.             case 367$function 'VARA';            break;
  3249.             default:
  3250.                 throw new Exception('Unrecognized function in formula');
  3251.                 break;
  3252.             }
  3253.             $data array('function' => $function'args' => $args);
  3254.             break;
  3255.         case 0x23// index to defined name
  3256.         case 0x43:
  3257.             $name 'tName';
  3258.             $size 5;
  3259.             // offset: 1; size: 2; one-based index to definedname record
  3260.             $definedNameIndex $this->_GetInt2d($formulaData11;
  3261.             // offset: 2; size: 2; not used
  3262.             $data $this->_definedname[$definedNameIndex]['name'];
  3263.             break;
  3264.         case 0x24// single cell reference e.g. A5
  3265.         case 0x44:
  3266.         case 0x64:
  3267.             $name 'tRef';
  3268.             $size 5;
  3269.             $data $this->_readBIFF8CellAddress(substr($formulaData14));
  3270.             break;
  3271.         case 0x25// cell range reference to cells in the same sheet
  3272.         case 0x45:
  3273.         case 0x65:
  3274.             $name 'tArea';
  3275.             $size 9;
  3276.             $data $this->_readBIFF8CellRangeAddress(substr($formulaData18));
  3277.             break;
  3278.         case 0x26:
  3279.         case 0x46:
  3280.             $name 'tMemArea';
  3281.             // offset: 1; size: 4; not used
  3282.             // offset: 5; size: 2; size of the following subexpression
  3283.             $subSize $this->_GetInt2d($formulaData5);
  3284.             $size $subSize;
  3285.             $data $this->_getFormulaFromData(substr($formulaData7$subSize));
  3286.             break;
  3287.         case 0x47:
  3288.             $name 'tMemErr';
  3289.             // offset: 1; size: 4; not used
  3290.             // offset: 5; size: 2; size of the following subexpression
  3291.             $subSize $this->_GetInt2d($formulaData5);
  3292.             $size $subSize;
  3293.             $data $this->_getFormulaFromData(substr($formulaData7$subSize));
  3294.             break;
  3295.         case 0x29:
  3296.         case 0x49:
  3297.             $name 'tMemFunc';
  3298.             // offset: 1; size: 2; size of the following subexpression
  3299.             $subSize $this->_GetInt2d($formulaData1);
  3300.             $size $subSize;
  3301.             $data $this->_getFormulaFromData(substr($formulaData3$subSize));
  3302.             break;
  3303.         case 0x3A// 3d reference to cell
  3304.         case 0x5A:
  3305.             $name 'tRef3d';
  3306.             $size 7;
  3307.             // offset: 1; size: 2; index to REF entry
  3308.             $sheetRange $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData1));
  3309.             // offset: 3; size: 4; cell address
  3310.             $cellAddress $this->_readBIFF8CellAddress(substr($formulaData34));
  3311.             $data "$sheetRange!$cellAddress";
  3312.             break;
  3313.         case 0x3B// 3d reference to cell range
  3314.             $name 'tArea3d';
  3315.             $size 11;
  3316.             // offset: 1; size: 2; index to REF entry
  3317.             $sheetRange $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData1));
  3318.             // offset: 3; size: 8; cell address
  3319.             $cellRangeAddress $this->_readBIFF8CellRangeAddress(substr($formulaData38));
  3320.             $data "$sheetRange!$cellRangeAddress";
  3321.             break;
  3322.         // case 0x39: // don't know how to deal with
  3323.         default:
  3324.             throw new Exception('Unrecognized token ' sprintf('%02X'$id' in formula');
  3325.             break;
  3326.         }
  3327.  
  3328.         return array(
  3329.             'id' => $id,
  3330.             'name' => $name,
  3331.             'size' => $size,
  3332.             'data' => $data,
  3333.         );
  3334.     }
  3335.  
  3336.     /**
  3337.      * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
  3338.      * section 3.3.4
  3339.      */
  3340.     private function _readBIFF8CellAddress($cellAddressStructure)
  3341.     {
  3342.         // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
  3343.             $row $this->_GetInt2d($cellAddressStructure01;
  3344.  
  3345.         // offset: 2; size: 2; index to column or column offset + relative flags
  3346.             // bit: 7-0; mask 0x00FF; column index
  3347.             $column PHPExcel_Cell::stringFromColumnIndex(0x00FF $this->_GetInt2d($cellAddressStructure2));
  3348.             // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
  3349.             if (!(0x4000 $this->_GetInt2d($cellAddressStructure2))) {
  3350.                 $column '$' $column;
  3351.             }
  3352.             // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
  3353.             if (!(0x8000 $this->_GetInt2d($cellAddressStructure2))) {
  3354.                 $row '$' $row;
  3355.             }
  3356.  
  3357.         return $column $row;
  3358.     }
  3359.  
  3360.     /**
  3361.      * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
  3362.      * always fixed range
  3363.      * section 2.5.14
  3364.      */
  3365.     private function _readBIFF8CellRangeAddressFixed($subData)
  3366.     {
  3367.         // offset: 0; size: 2; index to first row
  3368.         $fr $this->_GetInt2d($subData01;
  3369.  
  3370.         // offset: 2; size: 2; index to last row
  3371.         $lr $this->_GetInt2d($subData21;
  3372.  
  3373.         // offset: 4; size: 2; index to first column
  3374.         $fc PHPExcel_Cell::stringFromColumnIndex($this->_GetInt2d($subData4));
  3375.  
  3376.         // offset: 6; size: 2; index to last column
  3377.         $lc PHPExcel_Cell::stringFromColumnIndex($this->_GetInt2d($subData6));
  3378.  
  3379.         if ($fr == $lr and $fc == $lc{
  3380.             return "$fc$fr";
  3381.         }
  3382.         return "$fc$fr:$lc$lr";
  3383.     }
  3384.  
  3385.     /**
  3386.      * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
  3387.      * there are flags indicating whether column/row index is relative
  3388.      * section 3.3.4
  3389.      */
  3390.     private function _readBIFF8CellRangeAddress($subData)
  3391.     {
  3392.         // todo: if cell range is just a single cell, should this funciton
  3393.         // not just return e.g. 'A1' and not 'A1:A1' ?
  3394.  
  3395.         // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767))
  3396.             $fr $this->_GetInt2d($subData01;
  3397.         // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767))
  3398.             $lr $this->_GetInt2d($subData21;
  3399.         // offset: 4; size: 2; index to first column or column offset + relative flags
  3400.             // bit: 7-0; mask 0x00FF; column index
  3401.             $fc PHPExcel_Cell::stringFromColumnIndex(0x00FF $this->_GetInt2d($subData4));
  3402.             // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
  3403.             if (!(0x4000 $this->_GetInt2d($subData4))) {
  3404.                 $fc '$' $fc;
  3405.             }
  3406.             // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
  3407.             if (!(0x8000 $this->_GetInt2d($subData4))) {
  3408.                 $fr '$' $fr;
  3409.             }
  3410.         // offset: 6; size: 2; index to last column or column offset + relative flags
  3411.             // bit: 7-0; mask 0x00FF; column index
  3412.             $lc PHPExcel_Cell::stringFromColumnIndex(0x00FF $this->_GetInt2d($subData6));
  3413.             // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
  3414.             if (!(0x4000 $this->_GetInt2d($subData6))) {
  3415.                 $lc '$' $lc;
  3416.             }
  3417.             // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
  3418.             if (!(0x8000 $this->_GetInt2d($subData6))) {
  3419.                 $lr '$' $lr;
  3420.             }
  3421.  
  3422.         return "$fc$fr:$lc$lr";
  3423.     }
  3424.  
  3425.     /**
  3426.      * Read BIFF8 cell range address list
  3427.      * section 2.5.15
  3428.      */
  3429.     private function _readBIFF8CellRangeAddressList($subData)
  3430.     {
  3431.         $cellRangeAddresses array();
  3432.  
  3433.         // offset: 0; size: 2; number of the following cell range addresses
  3434.         $nm $this->_GetInt2d($subData0);
  3435.  
  3436.         $offset 2;
  3437.         // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
  3438.         for ($i 0$i $nm++$i{
  3439.             $cellRangeAddresses[$this->_readBIFF8CellRangeAddressFixed(substr($subData$offset8));
  3440.             $offset += 8;
  3441.         }
  3442.  
  3443.         return array(
  3444.             'size' => $nm,
  3445.             'cellRangeAddresses' => $cellRangeAddresses,
  3446.         );
  3447.     }
  3448.  
  3449.     /**
  3450.      * Get a sheet range like Sheet1:Sheet3 from REF index
  3451.      * Note: If there is only one sheet in the range, one gets e.g Sheet1
  3452.      */
  3453.     private function _readSheetRangeByRefIndex($index)
  3454.     {
  3455.         // we are assuming that ref index refers to internal workbook
  3456.         // in general, this is wrong, fix later
  3457.         if (isset($this->_ref[$index])) {
  3458.             $firstSheetName $this->_boundsheets[$this->_ref[$index]['firstSheetIndex']]['name'];
  3459.             $lastSheetName $this->_boundsheets[$this->_ref[$index]['lastSheetIndex']]['name'];
  3460.  
  3461.             if ($firstSheetName == $lastSheetName{
  3462.                 // collapsed sheet range
  3463.                 $sheetRange $firstSheetName;
  3464.             else {
  3465.                 $sheetRange "$firstSheetName:$lastSheetName";
  3466.             }
  3467.  
  3468.             // escape the single-quotes
  3469.             $sheetRange str_replace("'""''"$sheetRange);
  3470.  
  3471.             // if there are special characters, we need to enclose the range in single-quotes
  3472.             // todo: check if we have identified the whole set of special characters
  3473.             // it seems that the following characters are not accepted for sheet names
  3474.             // and we may assume that they are not present: []*/:\?
  3475.             if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/"$sheetRange)) {
  3476.                 $sheetRange "'$sheetRange'";
  3477.             }
  3478.  
  3479.             return $sheetRange;
  3480.         }
  3481.         return false;
  3482.     }
  3483.  
  3484.     /**
  3485.      * read BIFF8 constant value array from array data
  3486.      * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40}
  3487.      * section 2.5.8
  3488.      */
  3489.     private function _readBIFF8ConstantArray($arrayData)
  3490.     {
  3491.         // offset: 0; size: 1; number of columns decreased by 1
  3492.         $nc ord($arrayData[0]);
  3493.         // offset: 1; size: 2; number of rows decreased by 1
  3494.         $nr $this->_GetInt2d($arrayData1);
  3495.         $size 3// initialize
  3496.         $arrayData substr($arrayData3);
  3497.         // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values
  3498.         $matrixChunks array();
  3499.         for ($r 1$r <= $nr 1++$r{
  3500.             $items array();
  3501.             for ($c 1$c <= $nc 1++$c{
  3502.                 $constant $this->_readBIFF8Constant($arrayData);
  3503.                 $items[$constant['value'];
  3504.                 $arrayData substr($arrayData$constant['size']);
  3505.                 $size += $constant['size'];
  3506.             }
  3507.             $matrixChunks[implode(','$items)// looks like e.g. '1,"hello"'
  3508.         }
  3509.         $matrix '{' implode(';'$matrixChunks'}';
  3510.  
  3511.         return array(
  3512.             'value' => $matrix,
  3513.             'size' => $size,
  3514.         );
  3515.     }
  3516.  
  3517.     /**
  3518.      * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
  3519.      * section 2.5.7
  3520.      * returns e.g. array('value' => '5', 'size' => 9)
  3521.      */
  3522.     private function _readBIFF8Constant($valueData)
  3523.     {
  3524.         // offset: 0; size: 1; identifier for type of constant
  3525.         $identifier ord($valueData[0]);
  3526.         switch ($identifier{
  3527.         case 0x00// empty constant (what is this?)
  3528.             $value '';
  3529.             $size 9;
  3530.             break;
  3531.         case 0x01// number
  3532.             // offset: 1; size: 8; IEEE 754 floating-point value
  3533.             $value $this->_extractNumber(substr($valueData18));
  3534.             $size 9;
  3535.             break;
  3536.         case 0x02// string value
  3537.             // offset: 1; size: var; Unicode string, 16-bit string length
  3538.             $string $this->_readUnicodeStringLong(substr($valueData1));
  3539.             $value '"' $string['value''"';
  3540.             $size $string['size'];
  3541.             break;
  3542.         case 0x04// boolean
  3543.             // offset: 1; size: 1; 0 = FALSE, 1 = TRUE
  3544.             if (ord($valueData[1])) {
  3545.                 $value 'TRUE';
  3546.             else {
  3547.                 $value 'FALSE';
  3548.             }
  3549.             $size 9;
  3550.             break;
  3551.         case 0x10// error code
  3552.             // offset: 1; size: 1; error code
  3553.             $value $this->_mapErrorCode(ord($valueData[1]));
  3554.             $size 9;
  3555.             break;
  3556.         }
  3557.         return array(
  3558.             'value' => $value,
  3559.             'size' => $size,
  3560.         );
  3561.     }
  3562.  
  3563.     /**
  3564.      * Read byte string (8-bit string length)
  3565.      * OpenOffice documentation: 2.5.2
  3566.      *
  3567.      * @return array 
  3568.      */
  3569.     private function _readByteStringShort($subData)
  3570.     {
  3571.         // offset: 0; size: 1; length of the string (character count)
  3572.         $ln ord($subData[0]);
  3573.         // offset: 1: size: var; character array (8-bit characters)
  3574.         $value $this->_decodeCodepage(substr($subData1$ln));
  3575.  
  3576.         return array(
  3577.             'value' => $value,
  3578.             'size' => $ln// size in bytes of data structure
  3579.         );
  3580.     }
  3581.  
  3582.     /**
  3583.      * Read byte string (16-bit string length)
  3584.      * OpenOffice documentation: 2.5.2
  3585.      *
  3586.      * @return array 
  3587.      */
  3588.     private function _readByteStringLong($subData)
  3589.     {
  3590.         // offset: 0; size: 2; length of the string (character count)
  3591.         $ln $this->_GetInt2d($subData0);
  3592.         // offset: 2: size: var; character array (8-bit characters)
  3593.         $value $this->_decodeCodepage(substr($subData2));
  3594.  
  3595.         //return $string;
  3596.         return array(
  3597.             'value' => $value,
  3598.             'size' => $ln// size in bytes of data structure
  3599.         );
  3600.     }
  3601.  
  3602.     /**
  3603.      * Extracts an Excel Unicode short string (8-bit string length)
  3604.      * OpenOffice documentation: 2.5.3
  3605.      * function will automatically find out where the Unicode string ends.
  3606.      */
  3607.     private function _readUnicodeStringShort($subData)
  3608.     {
  3609.         $value '';
  3610.  
  3611.         // offset: 0: size: 1; length of the string (character count)
  3612.         $characterCount ord($subData[0]);
  3613.  
  3614.         $string $this->_readUnicodeString(substr($subData1)$characterCount);
  3615.  
  3616.         // add 1 for the string length
  3617.         $string['size'+= 1;
  3618.  
  3619.         return $string;
  3620.     }
  3621.  
  3622.     /**
  3623.      * Extracts an Excel Unicode long string (16-bit string length)
  3624.      * OpenOffice documentation: 2.5.3
  3625.      * this function is under construction, needs to support rich text, and Asian phonetic settings
  3626.      */
  3627.     private function _readUnicodeStringLong($subData)
  3628.     {
  3629.         $value '';
  3630.  
  3631.         // offset: 0: size: 2; length of the string (character count)
  3632.         $characterCount $this->_GetInt2d($subData0);
  3633.  
  3634.         $string $this->_readUnicodeString(substr($subData2)$characterCount);
  3635.  
  3636.         // add 2 for the string length
  3637.         $string['size'+= 2;
  3638.  
  3639.         return $string;
  3640.     }
  3641.  
  3642.     /**
  3643.      * Read Unicode string with no string length field, but with known character count
  3644.      * this function is under construction, needs to support rich text, and Asian phonetic settings
  3645.      * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3
  3646.      *
  3647.      * @return array 
  3648.      */
  3649.     private function _readUnicodeString($subData$characterCount)
  3650.     {
  3651.         $value '';
  3652.  
  3653.         // offset: 0: size: 1; option flags
  3654.             // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
  3655.             $isCompressed !((0x01 ord($subData[0])) >> 0);
  3656.  
  3657.             // bit: 2; mask: 0x04; Asian phonetic settings
  3658.             $hasAsian (0x04ord($subData[0]>> 2;
  3659.  
  3660.             // bit: 3; mask: 0x08; Rich-Text settings
  3661.             $hasRichText (0x08ord($subData[0]>> 3;
  3662.  
  3663.         // offset: 1: size: var; character array
  3664.         // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
  3665.         // needs to be fixed
  3666.         $value $this->_encodeUTF16(substr($subData1$isCompressed $characterCount $characterCount)$isCompressed);
  3667.  
  3668.         return array(
  3669.             'value' => $value,
  3670.             'size' => $isCompressed $characterCount $characterCount// the size in bytes including the option flags
  3671.         );
  3672.     }
  3673.  
  3674.     /**
  3675.      * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
  3676.      * Example:  hello"world  -->  "hello""world"
  3677.      *
  3678.      * @param $value string UTF-8 encoded string
  3679.      * @return string 
  3680.      */
  3681.     private function _UTF8toExcelDoubleQuoted($value)
  3682.     {
  3683.         return '"' str_replace('"''""'$value'"';
  3684.     }
  3685.  
  3686.     /**
  3687.      * Reads 8 bytes and returns IEEE 754 float
  3688.      */
  3689.     private function _createNumber($spos)
  3690.     {
  3691.         $rknumhigh $this->_GetInt4d($this->_data$spos 10);
  3692.         $rknumlow $this->_GetInt4d($this->_data$spos 6);
  3693.         $sign ($rknumhigh 0x80000000>> 31;
  3694.         $exp ($rknumhigh 0x7ff00000>> 20;
  3695.         $mantissa (0x100000 ($rknumhigh 0x000fffff));
  3696.         $mantissalow1 ($rknumlow 0x80000000>> 31;
  3697.         $mantissalow2 ($rknumlow 0x7fffffff);
  3698.         $value $mantissa pow(20($exp 1023)));
  3699.  
  3700.         if ($mantissalow1 != 0{
  3701.             $value += pow ((21 ($exp 1023)));
  3702.         }
  3703.  
  3704.         $value += $mantissalow2 pow ((52 ($exp 1023)));
  3705.         if ($sign{
  3706.             $value = -$value;
  3707.         }
  3708.  
  3709.         return    $value;
  3710.     }
  3711.  
  3712.     /**
  3713.      * Same as _createNumber, but not hardcoded to read from $this->_data
  3714.      */
  3715.     private function _extractNumber($subData)
  3716.     {
  3717.         $rknumhigh $this->_GetInt4d($subData4);
  3718.         $rknumlow $this->_GetInt4d($subData0);
  3719.         $sign ($rknumhigh 0x80000000>> 31;
  3720.         $exp ($rknumhigh 0x7ff00000>> 20;
  3721.         $mantissa (0x100000 ($rknumhigh 0x000fffff));
  3722.         $mantissalow1 ($rknumlow 0x80000000>> 31;
  3723.         $mantissalow2 ($rknumlow 0x7fffffff);
  3724.         $value $mantissa pow(20($exp 1023)));
  3725.  
  3726.         if ($mantissalow1 != 0{
  3727.             $value += pow ((21 ($exp 1023)));
  3728.         }
  3729.  
  3730.         $value += $mantissalow2 pow ((52 ($exp 1023)));
  3731.         if ($sign{
  3732.             $value = -$value;
  3733.         }
  3734.  
  3735.         return    $value;
  3736.     }
  3737.  
  3738.     private function _GetIEEE754($rknum)
  3739.     {
  3740.         if (($rknum 0x02!= 0{
  3741.             $value $rknum >> 2;
  3742.         }
  3743.         else {
  3744.             // changes by mmp, info on IEEE754 encoding from
  3745.             // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
  3746.             // The RK format calls for using only the most significant 30 bits
  3747.             // of the 64 bit floating point value. The other 34 bits are assumed
  3748.             // to be 0 so we use the upper 30 bits of $rknum as follows...
  3749.             $sign ($rknum 0x80000000>> 31;
  3750.             $exp ($rknum 0x7ff00000>> 20;
  3751.             $mantissa (0x100000 ($rknum 0x000ffffc));
  3752.             $value $mantissa pow(20($exp 1023)));
  3753.             if ($sign{
  3754.                 $value = -$value;
  3755.             }
  3756.             //end of changes by mmp
  3757.         }
  3758.         if (($rknum 0x01!= 0{
  3759.             $value /= 100;
  3760.         }
  3761.         return $value;
  3762.     }
  3763.  
  3764.     /**
  3765.      * Get UTF-8 string from (compressed or uncompressed) UTF-16 string
  3766.      *
  3767.      * @return string 
  3768.      */
  3769.     private function _encodeUTF16($string$compressed '')
  3770.     {
  3771.         $result $string;
  3772.         if($compressed{
  3773.             $string $this->_uncompressByteString($string);
  3774.          }
  3775.         switch ($this->_encoderFunction){
  3776.             case 'iconv' :
  3777.                 $result iconv('UTF-16LE''UTF-8'$string);
  3778.                 break;
  3779.             case 'mb_convert_encoding' :
  3780.                 $result mb_convert_encoding($string'UTF-8''UTF-16LE');
  3781.                 break;
  3782.         }
  3783.         return $result;
  3784.     }
  3785.  
  3786.     /**
  3787.      * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
  3788.      *
  3789.      * @return string 
  3790.      */
  3791.     private function _uncompressByteString($string)
  3792.     {
  3793.         $uncompressedString '';
  3794.         for ($i 0$i strlen($string)++$i{
  3795.             $uncompressedString .= $string[$i"\0";
  3796.         }
  3797.  
  3798.         return $uncompressedString;
  3799.     }
  3800.  
  3801.     /**
  3802.      * Convert string to UTF-8. Only used for BIFF5.
  3803.      *
  3804.      * @return string 
  3805.      */
  3806.     private function _decodeCodepage($string)
  3807.     {
  3808.         $result $string;
  3809.         if ($this->_codepage{
  3810.             switch ($this->_encoderFunction{
  3811.                 case 'iconv' :
  3812.                     $result iconv($this->_codepage'UTF-8'$string);
  3813.                     break;
  3814.                 case 'mb_convert_encoding' :
  3815.                     $result mb_convert_encoding($string'UTF-8'$this->_codepage );
  3816.                     break;
  3817.             }
  3818.         }
  3819.         return $result;
  3820.     }
  3821.  
  3822.     /**
  3823.      * Read 16-bit unsigned integer
  3824.      *
  3825.      * @return int 
  3826.      */
  3827.     private function _GetInt2d($data$pos)
  3828.     {
  3829.         return ord($data[$pos](ord($data[$pos 1]<< 8);
  3830.     }
  3831.  
  3832.     /**
  3833.      * Read 32-bit signed integer
  3834.      *
  3835.      * @return int 
  3836.      */
  3837.     private function _GetInt4d($data$pos)
  3838.     {
  3839.         //return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) |
  3840.         //    (ord($data[$pos + 2]) << 16) | (ord($data[$pos + 3]) << 24);
  3841.  
  3842.         // FIX: represent numbers correctly on 64-bit system
  3843.         // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
  3844.         $_or_24 ord($data[$pos 3]);
  3845.         if ($_or_24 >= 128{
  3846.             // negative number
  3847.             $_ord_24 = -abs((256 $_or_24<< 24);
  3848.         else {
  3849.             $_ord_24 ($_or_24 127<< 24;
  3850.         }
  3851.         return ord($data[$pos](ord($data[$pos 1]<< 8(ord($data[$pos 2]<< 16$_ord_24;
  3852.     }
  3853.  
  3854.     /**
  3855.      * Map border style
  3856.      * OpenOffice documentation: 2.5.11
  3857.      *
  3858.      * @return string 
  3859.      */
  3860.     private function _mapBorderStyle($index)
  3861.     {
  3862.         switch ($index{
  3863.         case 0x00return PHPExcel_Style_Border::BORDER_NONE;
  3864.         case 0x01return PHPExcel_Style_Border::BORDER_THIN;
  3865.         case 0x02return PHPExcel_Style_Border::BORDER_MEDIUM;
  3866.         case 0x03return PHPExcel_Style_Border::BORDER_DASHED;
  3867.         case 0x04return PHPExcel_Style_Border::BORDER_DOTTED;
  3868.         case 0x05return PHPExcel_Style_Border::BORDER_THICK;
  3869.         case 0x06return PHPExcel_Style_Border::BORDER_DOUBLE;
  3870.         case 0x07return PHPExcel_Style_Border::BORDER_HAIR;
  3871.         case 0x08return PHPExcel_Style_Border::BORDER_MEDIUMDASHED;
  3872.         case 0x09return PHPExcel_Style_Border::BORDER_DASHDOT;
  3873.         case 0x0Areturn PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT;
  3874.         case 0x0Breturn PHPExcel_Style_Border::BORDER_DASHDOTDOT;
  3875.         case 0x0Creturn PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT;
  3876.         case 0x0Dreturn PHPExcel_Style_Border::BORDER_SLANTDASHDOT;
  3877.         defaultreturn false;
  3878.         }
  3879.     }
  3880.  
  3881.     /**
  3882.      * Get fill pattern from index
  3883.      * OpenOffice documentation: 2.5.12
  3884.      *
  3885.      * @return string 
  3886.      */
  3887.     private function _mapFillPattern($index)
  3888.     {
  3889.         switch ($index{
  3890.         case 0x00return PHPExcel_Style_Fill::FILL_NONE;
  3891.         case 0x01return PHPExcel_Style_Fill::FILL_SOLID;
  3892.         case 0x02return PHPExcel_Style_Fill::FILL_PATTERN_MEDIUMGRAY;
  3893.         case 0x03return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRAY;
  3894.         case 0x04return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRAY;
  3895.         case 0x05return PHPExcel_Style_Fill::FILL_PATTERN_DARKHORIZONTAL;
  3896.         case 0x06return PHPExcel_Style_Fill::FILL_PATTERN_DARKVERTICAL;
  3897.         case 0x07return PHPExcel_Style_Fill::FILL_PATTERN_DARKDOWN;
  3898.         case 0x08return PHPExcel_Style_Fill::FILL_PATTERN_DARKUP;
  3899.         case 0x09return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRID;
  3900.         case 0x0Areturn PHPExcel_Style_Fill::FILL_PATTERN_DARKTRELLIS;
  3901.         case 0x0Breturn PHPExcel_Style_Fill::FILL_PATTERN_LIGHTHORIZONTAL;
  3902.         case 0x0Creturn PHPExcel_Style_Fill::FILL_PATTERN_LIGHTVERTICAL;
  3903.         case 0x0Dreturn PHPExcel_Style_Fill::FILL_PATTERN_LIGHTDOWN;
  3904.         case 0x0Ereturn PHPExcel_Style_Fill::FILL_PATTERN_LIGHTUP;
  3905.         case 0x0Freturn PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRID;
  3906.         case 0x10return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTTRELLIS;
  3907.         case 0x11return PHPExcel_Style_Fill::FILL_PATTERN_GRAY125;
  3908.         case 0x12return PHPExcel_Style_Fill::FILL_PATTERN_GRAY0625;
  3909.         defaultreturn false;
  3910.         }
  3911.     }
  3912.  
  3913.     /**
  3914.      * Map error code, e.g. '#N/A'
  3915.      *
  3916.      * @return string 
  3917.      */
  3918.     private function _mapErrorCode($subData)
  3919.     {
  3920.         switch ($subData{
  3921.         case 0x00return '#NULL!';        break;
  3922.         case 0x07return '#DIV/0!';    break;
  3923.         case 0x0Freturn '#VALUE!';    break;
  3924.         case 0x17return '#REF!';        break;
  3925.         case 0x1Dreturn '#NAME?';        break;
  3926.         case 0x24return '#NUM!';        break;
  3927.         case 0x2Areturn '#N/A';        break;
  3928.         defaultreturn false;
  3929.         }
  3930.     }
  3931.  
  3932.     /**
  3933.      * Map color array from BIFF8 built-in color index
  3934.      *
  3935.      * @return array 
  3936.      */
  3937.     private function _mapColor($subData)
  3938.     {
  3939.         switch ($subData{
  3940.             case 0x08return array('rgb' => '000000');
  3941.             case 0x09return array('rgb' => 'FFFFFF');
  3942.             case 0x0Areturn array('rgb' => 'FF0000');
  3943.             case 0x0Breturn array('rgb' => '00FF00');
  3944.             case 0x0Creturn array('rgb' => '0000FF');
  3945.             case 0x0Dreturn array('rgb' => 'FFFF00');
  3946.             case 0x0Ereturn array('rgb' => 'FF00FF');
  3947.             case 0x0Freturn array('rgb' => '00FFFF');
  3948.             case 0x10return array('rgb' => '800000');
  3949.             case 0x11return array('rgb' => '008000');
  3950.             case 0x12return array('rgb' => '000080');
  3951.             case 0x13return array('rgb' => '808000');
  3952.             case 0x14return array('rgb' => '800080');
  3953.             case 0x15return array('rgb' => '008080');
  3954.             case 0x16return array('rgb' => 'C0C0C0');
  3955.             case 0x17return array('rgb' => '808080');
  3956.             case 0x18return array('rgb' => '9999FF');
  3957.             case 0x19return array('rgb' => '993366');
  3958.             case 0x1Areturn array('rgb' => 'FFFFCC');
  3959.             case 0x1Breturn array('rgb' => 'CCFFFF');
  3960.             case 0x1Creturn array('rgb' => '660066');
  3961.             case 0x1Dreturn array('rgb' => 'FF8080');
  3962.             case 0x1Ereturn array('rgb' => '0066CC');
  3963.             case 0x1Freturn array('rgb' => 'CCCCFF');
  3964.             case 0x20return array('rgb' => '000080');
  3965.             case 0x21return array('rgb' => 'FF00FF');
  3966.             case 0x22return array('rgb' => 'FFFF00');
  3967.             case 0x23return array('rgb' => '00FFFF');
  3968.             case 0x24return array('rgb' => '800080');
  3969.             case 0x25return array('rgb' => '800000');
  3970.             case 0x26return array('rgb' => '008080');
  3971.             case 0x27return array('rgb' => '0000FF');
  3972.             case 0x28return array('rgb' => '00CCFF');
  3973.             case 0x29return array('rgb' => 'CCFFFF');
  3974.             case 0x2Areturn array('rgb' => 'CCFFCC');
  3975.             case 0x2Breturn array('rgb' => 'FFFF99');
  3976.             case 0x2Creturn array('rgb' => '99CCFF');
  3977.             case 0x2Dreturn array('rgb' => 'FF99CC');
  3978.             case 0x2Ereturn array('rgb' => 'CC99FF');
  3979.             case 0x2Freturn array('rgb' => 'FFCC99');
  3980.             case 0x30return array('rgb' => '3366FF');
  3981.             case 0x31return array('rgb' => '33CCCC');
  3982.             case 0x32return array('rgb' => '99CC00');
  3983.             case 0x33return array('rgb' => 'FFCC00');
  3984.             case 0x34return array('rgb' => 'FF9900');
  3985.             case 0x35return array('rgb' => 'FF6600');
  3986.             case 0x36return array('rgb' => '666699');
  3987.             case 0x37return array('rgb' => '969696');
  3988.             case 0x38return array('rgb' => '003366');
  3989.             case 0x39return array('rgb' => '339966');
  3990.             case 0x3Areturn array('rgb' => '003300');
  3991.             case 0x3Breturn array('rgb' => '333300');
  3992.             case 0x3Creturn array('rgb' => '993300');
  3993.             case 0x3Dreturn array('rgb' => '993366');
  3994.             case 0x3Ereturn array('rgb' => '333399');
  3995.             case 0x3Freturn array('rgb' => '333333');
  3996.             defaultreturn false;
  3997.         }
  3998.     }
  3999.  
  4000.     /**
  4001.      * Dump a byte sequence, only used for debugging
  4002.      */
  4003.     private function _dump($data)
  4004.     {
  4005.         for ($i 0$i strlen($data)++$i{
  4006.             echo sprintf('%02X'ord($data[$i])) ' ';
  4007.         }
  4008.     }
  4009.  
  4010. }

Documentation generated on Mon, 27 Oct 2008 08:38:34 +0100 by phpDocumentor 1.4.1