PageRenderTime 73ms CodeModel.GetById 20ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 1ms

/Classes/PHPExcel/Reader/Excel5.php

https://github.com/iGroup/PHPExcel
PHP | 6771 lines | 3844 code | 1123 blank | 1804 comment | 502 complexity | c57009d79814586976c523f55568e084 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2/**
   3 * PHPExcel
   4 *
   5 * Copyright (c) 2006 - 2012 PHPExcel
   6 *
   7 * This library is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU Lesser General Public
   9 * License as published by the Free Software Foundation; either
  10 * version 2.1 of the License, or (at your option) any later version.
  11 *
  12 * This library is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 * Lesser General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU Lesser General Public
  18 * License along with this library; if not, write to the Free Software
  19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  20 *
  21 * @category   PHPExcel
  22 * @package    PHPExcel_Reader_Excel5
  23 * @copyright  Copyright (c) 2006 - 2012 PHPExcel (http://www.codeplex.com/PHPExcel)
  24 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt	LGPL
  25 * @version    ##VERSION##, ##DATE##
  26 */
  27
  28// Original file header of ParseXL (used as the base for this class):
  29// --------------------------------------------------------------------------------
  30// Adapted from Excel_Spreadsheet_Reader developed by users bizon153,
  31// trex005, and mmp11 (SourceForge.net)
  32// http://sourceforge.net/projects/phpexcelreader/
  33// Primary changes made by canyoncasa (dvc) for ParseXL 1.00 ...
  34//	 Modelled moreso after Perl Excel Parse/Write modules
  35//	 Added Parse_Excel_Spreadsheet object
  36//		 Reads a whole worksheet or tab as row,column array or as
  37//		 associated hash of indexed rows and named column fields
  38//	 Added variables for worksheet (tab) indexes and names
  39//	 Added an object call for loading individual woorksheets
  40//	 Changed default indexing defaults to 0 based arrays
  41//	 Fixed date/time and percent formats
  42//	 Includes patches found at SourceForge...
  43//		 unicode patch by nobody
  44//		 unpack("d") machine depedency patch by matchy
  45//		 boundsheet utf16 patch by bjaenichen
  46//	 Renamed functions for shorter names
  47//	 General code cleanup and rigor, including <80 column width
  48//	 Included a testcase Excel file and PHP example calls
  49//	 Code works for PHP 5.x
  50
  51// Primary changes made by canyoncasa (dvc) for ParseXL 1.10 ...
  52// http://sourceforge.net/tracker/index.php?func=detail&aid=1466964&group_id=99160&atid=623334
  53//	 Decoding of formula conditions, results, and tokens.
  54//	 Support for user-defined named cells added as an array "namedcells"
  55//		 Patch code for user-defined named cells supports single cells only.
  56//		 NOTE: this patch only works for BIFF8 as BIFF5-7 use a different
  57//		 external sheet reference structure
  58
  59
  60/** PHPExcel root directory */
  61if (!defined('PHPEXCEL_ROOT')) {
  62	/**
  63	 * @ignore
  64	 */
  65	define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../');
  66	require(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
  67}
  68
  69/**
  70 * PHPExcel_Reader_Excel5
  71 *
  72 * This class uses {@link http://sourceforge.net/projects/phpexcelreader/parseXL}
  73 *
  74 * @category	PHPExcel
  75 * @package		PHPExcel_Reader_Excel5
  76 * @copyright	Copyright (c) 2006 - 2012 PHPExcel (http://www.codeplex.com/PHPExcel)
  77 */
  78class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader
  79{
  80	// ParseXL definitions
  81	const XLS_BIFF8						= 0x0600;
  82	const XLS_BIFF7						= 0x0500;
  83	const XLS_WorkbookGlobals			= 0x0005;
  84	const XLS_Worksheet					= 0x0010;
  85
  86	// record identifiers
  87	const XLS_Type_FORMULA				= 0x0006;
  88	const XLS_Type_EOF					= 0x000a;
  89	const XLS_Type_PROTECT				= 0x0012;
  90	const XLS_Type_OBJECTPROTECT		= 0x0063;
  91	const XLS_Type_SCENPROTECT			= 0x00dd;
  92	const XLS_Type_PASSWORD				= 0x0013;
  93	const XLS_Type_HEADER				= 0x0014;
  94	const XLS_Type_FOOTER				= 0x0015;
  95	const XLS_Type_EXTERNSHEET			= 0x0017;
  96	const XLS_Type_DEFINEDNAME			= 0x0018;
  97	const XLS_Type_VERTICALPAGEBREAKS	= 0x001a;
  98	const XLS_Type_HORIZONTALPAGEBREAKS	= 0x001b;
  99	const XLS_Type_NOTE					= 0x001c;
 100	const XLS_Type_SELECTION			= 0x001d;
 101	const XLS_Type_DATEMODE				= 0x0022;
 102	const XLS_Type_EXTERNNAME			= 0x0023;
 103	const XLS_Type_LEFTMARGIN			= 0x0026;
 104	const XLS_Type_RIGHTMARGIN			= 0x0027;
 105	const XLS_Type_TOPMARGIN			= 0x0028;
 106	const XLS_Type_BOTTOMMARGIN			= 0x0029;
 107	const XLS_Type_PRINTGRIDLINES		= 0x002b;
 108	const XLS_Type_FILEPASS				= 0x002f;
 109	const XLS_Type_FONT					= 0x0031;
 110	const XLS_Type_CONTINUE				= 0x003c;
 111	const XLS_Type_PANE					= 0x0041;
 112	const XLS_Type_CODEPAGE				= 0x0042;
 113	const XLS_Type_DEFCOLWIDTH 			= 0x0055;
 114	const XLS_Type_OBJ					= 0x005d;
 115	const XLS_Type_COLINFO				= 0x007d;
 116	const XLS_Type_IMDATA				= 0x007f;
 117	const XLS_Type_SHEETPR				= 0x0081;
 118	const XLS_Type_HCENTER				= 0x0083;
 119	const XLS_Type_VCENTER				= 0x0084;
 120	const XLS_Type_SHEET				= 0x0085;
 121	const XLS_Type_PALETTE				= 0x0092;
 122	const XLS_Type_SCL					= 0x00a0;
 123	const XLS_Type_PAGESETUP			= 0x00a1;
 124	const XLS_Type_MULRK				= 0x00bd;
 125	const XLS_Type_MULBLANK				= 0x00be;
 126	const XLS_Type_DBCELL				= 0x00d7;
 127	const XLS_Type_XF					= 0x00e0;
 128	const XLS_Type_MERGEDCELLS			= 0x00e5;
 129	const XLS_Type_MSODRAWINGGROUP		= 0x00eb;
 130	const XLS_Type_MSODRAWING			= 0x00ec;
 131	const XLS_Type_SST					= 0x00fc;
 132	const XLS_Type_LABELSST				= 0x00fd;
 133	const XLS_Type_EXTSST				= 0x00ff;
 134	const XLS_Type_EXTERNALBOOK			= 0x01ae;
 135	const XLS_Type_DATAVALIDATIONS		= 0x01b2;
 136	const XLS_Type_TXO					= 0x01b6;
 137	const XLS_Type_HYPERLINK			= 0x01b8;
 138	const XLS_Type_DATAVALIDATION		= 0x01be;
 139	const XLS_Type_DIMENSION			= 0x0200;
 140	const XLS_Type_BLANK				= 0x0201;
 141	const XLS_Type_NUMBER				= 0x0203;
 142	const XLS_Type_LABEL				= 0x0204;
 143	const XLS_Type_BOOLERR				= 0x0205;
 144	const XLS_Type_STRING				= 0x0207;
 145	const XLS_Type_ROW					= 0x0208;
 146	const XLS_Type_INDEX				= 0x020b;
 147	const XLS_Type_ARRAY				= 0x0221;
 148	const XLS_Type_DEFAULTROWHEIGHT 	= 0x0225;
 149	const XLS_Type_WINDOW2				= 0x023e;
 150	const XLS_Type_RK					= 0x027e;
 151	const XLS_Type_STYLE				= 0x0293;
 152	const XLS_Type_FORMAT				= 0x041e;
 153	const XLS_Type_SHAREDFMLA			= 0x04bc;
 154	const XLS_Type_BOF					= 0x0809;
 155	const XLS_Type_SHEETPROTECTION		= 0x0867;
 156	const XLS_Type_RANGEPROTECTION		= 0x0868;
 157	const XLS_Type_SHEETLAYOUT			= 0x0862;
 158	const XLS_Type_XFEXT				= 0x087d;
 159	const XLS_Type_UNKNOWN				= 0xffff;
 160
 161
 162	/**
 163	 * Summary Information stream data.
 164	 *
 165	 * @var string
 166	 */
 167	private $_summaryInformation;
 168
 169	/**
 170	 * Extended Summary Information stream data.
 171	 *
 172	 * @var string
 173	 */
 174	private $_documentSummaryInformation;
 175
 176	/**
 177	 * User-Defined Properties stream data.
 178	 *
 179	 * @var string
 180	 */
 181	private $_userDefinedProperties;
 182
 183	/**
 184	 * Workbook stream data. (Includes workbook globals substream as well as sheet substreams)
 185	 *
 186	 * @var string
 187	 */
 188	private $_data;
 189
 190	/**
 191	 * Size in bytes of $this->_data
 192	 *
 193	 * @var int
 194	 */
 195	private $_dataSize;
 196
 197	/**
 198	 * Current position in stream
 199	 *
 200	 * @var integer
 201	 */
 202	private $_pos;
 203
 204	/**
 205	 * Workbook to be returned by the reader.
 206	 *
 207	 * @var PHPExcel
 208	 */
 209	private $_phpExcel;
 210
 211	/**
 212	 * Worksheet that is currently being built by the reader.
 213	 *
 214	 * @var PHPExcel_Worksheet
 215	 */
 216	private $_phpSheet;
 217
 218	/**
 219	 * BIFF version
 220	 *
 221	 * @var int
 222	 */
 223	private $_version;
 224
 225	/**
 226	 * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
 227	 * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'
 228	 *
 229	 * @var string
 230	 */
 231	private $_codepage;
 232
 233	/**
 234	 * Shared formats
 235	 *
 236	 * @var array
 237	 */
 238	private $_formats;
 239
 240	/**
 241	 * Shared fonts
 242	 *
 243	 * @var array
 244	 */
 245	private $_objFonts;
 246
 247	/**
 248	 * Color palette
 249	 *
 250	 * @var array
 251	 */
 252	private $_palette;
 253
 254	/**
 255	 * Worksheets
 256	 *
 257	 * @var array
 258	 */
 259	private $_sheets;
 260
 261	/**
 262	 * External books
 263	 *
 264	 * @var array
 265	 */
 266	private $_externalBooks;
 267
 268	/**
 269	 * REF structures. Only applies to BIFF8.
 270	 *
 271	 * @var array
 272	 */
 273	private $_ref;
 274
 275	/**
 276	 * External names
 277	 *
 278	 * @var array
 279	 */
 280	private $_externalNames;
 281
 282	/**
 283	 * Defined names
 284	 *
 285	 * @var array
 286	 */
 287	private $_definedname;
 288
 289	/**
 290	 * Shared strings. Only applies to BIFF8.
 291	 *
 292	 * @var array
 293	 */
 294	private $_sst;
 295
 296	/**
 297	 * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
 298	 *
 299	 * @var boolean
 300	 */
 301	private $_frozen;
 302
 303	/**
 304	 * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
 305	 *
 306	 * @var boolean
 307	 */
 308	private $_isFitToPages;
 309
 310	/**
 311	 * Objects. One OBJ record contributes with one entry.
 312	 *
 313	 * @var array
 314	 */
 315	private $_objs;
 316
 317	/**
 318	 * Text Objects. One TXO record corresponds with one entry.
 319	 *
 320	 * @var array
 321	 */
 322	private $_textObjects;
 323
 324	/**
 325	 * Cell Annotations (BIFF8)
 326	 *
 327	 * @var array
 328	 */
 329	private $_cellNotes;
 330
 331	/**
 332	 * The combined MSODRAWINGGROUP data
 333	 *
 334	 * @var string
 335	 */
 336	private $_drawingGroupData;
 337
 338	/**
 339	 * The combined MSODRAWING data (per sheet)
 340	 *
 341	 * @var string
 342	 */
 343	private $_drawingData;
 344
 345	/**
 346	 * Keep track of XF index
 347	 *
 348	 * @var int
 349	 */
 350	private $_xfIndex;
 351
 352	/**
 353	 * Mapping of XF index (that is a cell XF) to final index in cellXf collection
 354	 *
 355	 * @var array
 356	 */
 357	private $_mapCellXfIndex;
 358
 359	/**
 360	 * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection
 361	 *
 362	 * @var array
 363	 */
 364	private $_mapCellStyleXfIndex;
 365
 366	/**
 367	 * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
 368	 *
 369	 * @var array
 370	 */
 371	private $_sharedFormulas;
 372
 373	/**
 374	 * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
 375	 * refers to a shared formula.
 376	 *
 377	 * @var array
 378	 */
 379	private $_sharedFormulaParts;
 380
 381
 382	/**
 383	 * Create a new PHPExcel_Reader_Excel5 instance
 384	 */
 385	public function __construct() {
 386		$this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
 387	}
 388
 389
 390	/**
 391	 * Can the current PHPExcel_Reader_IReader read the file?
 392	 *
 393	 * @param 	string 		$pFileName
 394	 * @return 	boolean
 395	 * @throws PHPExcel_Reader_Exception
 396	 */
 397	public function canRead($pFilename)
 398	{
 399		// Check if file exists
 400		if (!file_exists($pFilename)) {
 401			throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist.");
 402		}
 403
 404		try {
 405			// Use ParseXL for the hard work.
 406			$ole = new PHPExcel_Shared_OLERead();
 407
 408			// get excel data
 409			$res = $ole->read($pFilename);
 410			return true;
 411		} catch (PHPExcel_Reader_Exception $e) {
 412			return false;
 413		}
 414	}
 415
 416
 417	/**
 418	 * Reads names of the worksheets from a file, without parsing the whole file to a PHPExcel object
 419	 *
 420	 * @param 	string 		$pFilename
 421	 * @throws 	PHPExcel_Reader_Exception
 422	 */
 423	public function listWorksheetNames($pFilename)
 424	{
 425		// Check if file exists
 426		if (!file_exists($pFilename)) {
 427			throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist.");
 428		}
 429
 430		$worksheetNames = array();
 431
 432		// Read the OLE file
 433		$this->_loadOLE($pFilename);
 434
 435		// total byte size of Excel data (workbook global substream + sheet substreams)
 436		$this->_dataSize = strlen($this->_data);
 437
 438		$this->_pos		= 0;
 439		$this->_sheets	= array();
 440
 441		// Parse Workbook Global Substream
 442		while ($this->_pos < $this->_dataSize) {
 443			$code = self::_GetInt2d($this->_data, $this->_pos);
 444
 445			switch ($code) {
 446				case self::XLS_Type_BOF:	$this->_readBof();		break;
 447				case self::XLS_Type_SHEET:	$this->_readSheet();	break;
 448				case self::XLS_Type_EOF:	$this->_readDefault();	break 2;
 449				default:					$this->_readDefault();	break;
 450			}
 451		}
 452
 453		foreach ($this->_sheets as $sheet) {
 454			if ($sheet['sheetType'] != 0x00) {
 455				// 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
 456				continue;
 457			}
 458
 459			$worksheetNames[] = $sheet['name'];
 460		}
 461
 462		return $worksheetNames;
 463	}
 464
 465
 466	/**
 467	 * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns)
 468	 *
 469	 * @param   string     $pFilename
 470	 * @throws   PHPExcel_Reader_Exception
 471	 */
 472	public function listWorksheetInfo($pFilename)
 473	{
 474		// Check if file exists
 475		if (!file_exists($pFilename)) {
 476			throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist.");
 477		}
 478
 479		$worksheetInfo = array();
 480
 481		// Read the OLE file
 482		$this->_loadOLE($pFilename);
 483
 484		// total byte size of Excel data (workbook global substream + sheet substreams)
 485		$this->_dataSize = strlen($this->_data);
 486
 487		// initialize
 488		$this->_pos    = 0;
 489		$this->_sheets = array();
 490
 491		// Parse Workbook Global Substream
 492		while ($this->_pos < $this->_dataSize) {
 493			$code = self::_GetInt2d($this->_data, $this->_pos);
 494
 495			switch ($code) {
 496				case self::XLS_Type_BOF:        $this->_readBof();        break;
 497				case self::XLS_Type_SHEET:      $this->_readSheet();      break;
 498				case self::XLS_Type_EOF:        $this->_readDefault();    break 2;
 499				default:                        $this->_readDefault();    break;
 500			}
 501		}
 502
 503		// Parse the individual sheets
 504		foreach ($this->_sheets as $sheet) {
 505
 506			if ($sheet['sheetType'] != 0x00) {
 507				// 0x00: Worksheet
 508				// 0x02: Chart
 509				// 0x06: Visual Basic module
 510				continue;
 511			}
 512
 513			$tmpInfo = array();
 514			$tmpInfo['worksheetName'] = $sheet['name'];
 515			$tmpInfo['lastColumnLetter'] = 'A';
 516			$tmpInfo['lastColumnIndex'] = 0;
 517			$tmpInfo['totalRows'] = 0;
 518			$tmpInfo['totalColumns'] = 0;
 519
 520			$this->_pos = $sheet['offset'];
 521
 522			while ($this->_pos <= $this->_dataSize - 4) {
 523				$code = self::_GetInt2d($this->_data, $this->_pos);
 524
 525				switch ($code) {
 526					case self::XLS_Type_RK:
 527					case self::XLS_Type_LABELSST:
 528					case self::XLS_Type_NUMBER:
 529					case self::XLS_Type_FORMULA:
 530					case self::XLS_Type_BOOLERR:
 531					case self::XLS_Type_LABEL:
 532						$length = self::_GetInt2d($this->_data, $this->_pos + 2);
 533						$recordData = substr($this->_data, $this->_pos + 4, $length);
 534
 535						// move stream pointer to next record
 536						$this->_pos += 4 + $length;
 537
 538						$rowIndex = self::_GetInt2d($recordData, 0) + 1;
 539						$columnIndex = self::_GetInt2d($recordData, 2);
 540
 541						$tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
 542						$tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
 543						break;
 544					case self::XLS_Type_BOF:      $this->_readBof();          break;
 545					case self::XLS_Type_EOF:      $this->_readDefault();      break 2;
 546					default:                      $this->_readDefault();      break;
 547				}
 548			}
 549
 550			$tmpInfo['lastColumnLetter'] = PHPExcel_Cell::stringFromColumnIndex($tmpInfo['lastColumnIndex']);
 551			$tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
 552
 553			$worksheetInfo[] = $tmpInfo;
 554		}
 555
 556		return $worksheetInfo;
 557	}
 558
 559
 560	/**
 561	 * Loads PHPExcel from file
 562	 *
 563	 * @param 	string 		$pFilename
 564	 * @return 	PHPExcel
 565	 * @throws 	PHPExcel_Reader_Exception
 566	 */
 567	public function load($pFilename)
 568	{
 569		// Read the OLE file
 570		$this->_loadOLE($pFilename);
 571
 572		// Initialisations
 573		$this->_phpExcel = new PHPExcel;
 574		$this->_phpExcel->removeSheetByIndex(0); // remove 1st sheet
 575		if (!$this->_readDataOnly) {
 576			$this->_phpExcel->removeCellStyleXfByIndex(0); // remove the default style
 577			$this->_phpExcel->removeCellXfByIndex(0); // remove the default style
 578		}
 579
 580		// Read the summary information stream (containing meta data)
 581		$this->_readSummaryInformation();
 582
 583		// Read the Additional document summary information stream (containing application-specific meta data)
 584		$this->_readDocumentSummaryInformation();
 585
 586		// total byte size of Excel data (workbook global substream + sheet substreams)
 587		$this->_dataSize = strlen($this->_data);
 588
 589		// initialize
 590		$this->_pos					= 0;
 591		$this->_codepage			= 'CP1252';
 592		$this->_formats				= array();
 593		$this->_objFonts			= array();
 594		$this->_palette				= array();
 595		$this->_sheets				= array();
 596		$this->_externalBooks		= array();
 597		$this->_ref					= array();
 598		$this->_definedname			= array();
 599		$this->_sst					= array();
 600		$this->_drawingGroupData	= '';
 601		$this->_xfIndex				= '';
 602		$this->_mapCellXfIndex		= array();
 603		$this->_mapCellStyleXfIndex	= array();
 604
 605		// Parse Workbook Global Substream
 606		while ($this->_pos < $this->_dataSize) {
 607			$code = self::_GetInt2d($this->_data, $this->_pos);
 608
 609			switch ($code) {
 610				case self::XLS_Type_BOF:			$this->_readBof();				break;
 611				case self::XLS_Type_FILEPASS:		$this->_readFilepass();			break;
 612				case self::XLS_Type_CODEPAGE:		$this->_readCodepage();			break;
 613				case self::XLS_Type_DATEMODE:		$this->_readDateMode();			break;
 614				case self::XLS_Type_FONT:			$this->_readFont();				break;
 615				case self::XLS_Type_FORMAT:			$this->_readFormat();			break;
 616				case self::XLS_Type_XF:				$this->_readXf();				break;
 617				case self::XLS_Type_XFEXT:			$this->_readXfExt();			break;
 618				case self::XLS_Type_STYLE:			$this->_readStyle();			break;
 619				case self::XLS_Type_PALETTE:		$this->_readPalette();			break;
 620				case self::XLS_Type_SHEET:			$this->_readSheet();			break;
 621				case self::XLS_Type_EXTERNALBOOK:	$this->_readExternalBook();		break;
 622				case self::XLS_Type_EXTERNNAME:		$this->_readExternName();		break;
 623				case self::XLS_Type_EXTERNSHEET:	$this->_readExternSheet();		break;
 624				case self::XLS_Type_DEFINEDNAME:	$this->_readDefinedName();		break;
 625				case self::XLS_Type_MSODRAWINGGROUP:	$this->_readMsoDrawingGroup();	break;
 626				case self::XLS_Type_SST:			$this->_readSst();				break;
 627				case self::XLS_Type_EOF:			$this->_readDefault();			break 2;
 628				default:							$this->_readDefault();			break;
 629			}
 630		}
 631
 632		// Resolve indexed colors for font, fill, and border colors
 633		// Cannot be resolved already in XF record, because PALETTE record comes afterwards
 634		if (!$this->_readDataOnly) {
 635			foreach ($this->_objFonts as $objFont) {
 636				if (isset($objFont->colorIndex)) {
 637					$color = self::_readColor($objFont->colorIndex,$this->_palette,$this->_version);
 638					$objFont->getColor()->setRGB($color['rgb']);
 639				}
 640			}
 641
 642			foreach ($this->_phpExcel->getCellXfCollection() as $objStyle) {
 643				// fill start and end color
 644				$fill = $objStyle->getFill();
 645
 646				if (isset($fill->startcolorIndex)) {
 647					$startColor = self::_readColor($fill->startcolorIndex,$this->_palette,$this->_version);
 648					$fill->getStartColor()->setRGB($startColor['rgb']);
 649				}
 650
 651				if (isset($fill->endcolorIndex)) {
 652					$endColor = self::_readColor($fill->endcolorIndex,$this->_palette,$this->_version);
 653					$fill->getEndColor()->setRGB($endColor['rgb']);
 654				}
 655
 656				// border colors
 657				$top      = $objStyle->getBorders()->getTop();
 658				$right    = $objStyle->getBorders()->getRight();
 659				$bottom   = $objStyle->getBorders()->getBottom();
 660				$left     = $objStyle->getBorders()->getLeft();
 661				$diagonal = $objStyle->getBorders()->getDiagonal();
 662
 663				if (isset($top->colorIndex)) {
 664					$borderTopColor = self::_readColor($top->colorIndex,$this->_palette,$this->_version);
 665					$top->getColor()->setRGB($borderTopColor['rgb']);
 666				}
 667
 668				if (isset($right->colorIndex)) {
 669					$borderRightColor = self::_readColor($right->colorIndex,$this->_palette,$this->_version);
 670					$right->getColor()->setRGB($borderRightColor['rgb']);
 671				}
 672
 673				if (isset($bottom->colorIndex)) {
 674					$borderBottomColor = self::_readColor($bottom->colorIndex,$this->_palette,$this->_version);
 675					$bottom->getColor()->setRGB($borderBottomColor['rgb']);
 676				}
 677
 678				if (isset($left->colorIndex)) {
 679					$borderLeftColor = self::_readColor($left->colorIndex,$this->_palette,$this->_version);
 680					$left->getColor()->setRGB($borderLeftColor['rgb']);
 681				}
 682
 683				if (isset($diagonal->colorIndex)) {
 684					$borderDiagonalColor = self::_readColor($diagonal->colorIndex,$this->_palette,$this->_version);
 685					$diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
 686				}
 687			}
 688		}
 689
 690		// treat MSODRAWINGGROUP records, workbook-level Escher
 691		if (!$this->_readDataOnly && $this->_drawingGroupData) {
 692			$escherWorkbook = new PHPExcel_Shared_Escher();
 693			$reader = new PHPExcel_Reader_Excel5_Escher($escherWorkbook);
 694			$escherWorkbook = $reader->load($this->_drawingGroupData);
 695
 696			// debug Escher stream
 697			//$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
 698			//$debug->load($this->_drawingGroupData);
 699		}
 700
 701		// Parse the individual sheets
 702		foreach ($this->_sheets as $sheet) {
 703
 704			if ($sheet['sheetType'] != 0x00) {
 705				// 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
 706				continue;
 707			}
 708
 709			// check if sheet should be skipped
 710			if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) {
 711				continue;
 712			}
 713
 714			// add sheet to PHPExcel object
 715			$this->_phpSheet = $this->_phpExcel->createSheet();
 716			//	Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
 717			//		cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
 718			//		name in line with the formula, not the reverse
 719			$this->_phpSheet->setTitle($sheet['name'],false);
 720			$this->_phpSheet->setSheetState($sheet['sheetState']);
 721
 722			$this->_pos = $sheet['offset'];
 723
 724			// Initialize isFitToPages. May change after reading SHEETPR record.
 725			$this->_isFitToPages = false;
 726
 727			// Initialize drawingData
 728			$this->_drawingData = '';
 729
 730			// Initialize objs
 731			$this->_objs = array();
 732
 733			// Initialize shared formula parts
 734			$this->_sharedFormulaParts = array();
 735
 736			// Initialize shared formulas
 737			$this->_sharedFormulas = array();
 738
 739			// Initialize text objs
 740			$this->_textObjects = array();
 741
 742			// Initialize cell annotations
 743			$this->_cellNotes = array();
 744			$this->textObjRef = -1;
 745
 746			while ($this->_pos <= $this->_dataSize - 4) {
 747				$code = self::_GetInt2d($this->_data, $this->_pos);
 748
 749				switch ($code) {
 750					case self::XLS_Type_BOF:					$this->_readBof();						break;
 751					case self::XLS_Type_PRINTGRIDLINES:			$this->_readPrintGridlines();			break;
 752					case self::XLS_Type_DEFAULTROWHEIGHT:		$this->_readDefaultRowHeight();			break;
 753					case self::XLS_Type_SHEETPR:				$this->_readSheetPr();					break;
 754					case self::XLS_Type_HORIZONTALPAGEBREAKS:	$this->_readHorizontalPageBreaks();		break;
 755					case self::XLS_Type_VERTICALPAGEBREAKS:		$this->_readVerticalPageBreaks();		break;
 756					case self::XLS_Type_HEADER:					$this->_readHeader();					break;
 757					case self::XLS_Type_FOOTER:					$this->_readFooter();					break;
 758					case self::XLS_Type_HCENTER:				$this->_readHcenter();					break;
 759					case self::XLS_Type_VCENTER:				$this->_readVcenter();					break;
 760					case self::XLS_Type_LEFTMARGIN:				$this->_readLeftMargin();				break;
 761					case self::XLS_Type_RIGHTMARGIN:			$this->_readRightMargin();				break;
 762					case self::XLS_Type_TOPMARGIN:				$this->_readTopMargin();				break;
 763					case self::XLS_Type_BOTTOMMARGIN:			$this->_readBottomMargin();				break;
 764					case self::XLS_Type_PAGESETUP:				$this->_readPageSetup();				break;
 765					case self::XLS_Type_PROTECT:				$this->_readProtect();					break;
 766					case self::XLS_Type_SCENPROTECT:			$this->_readScenProtect();				break;
 767					case self::XLS_Type_OBJECTPROTECT:			$this->_readObjectProtect();			break;
 768					case self::XLS_Type_PASSWORD:				$this->_readPassword();					break;
 769					case self::XLS_Type_DEFCOLWIDTH:			$this->_readDefColWidth();				break;
 770					case self::XLS_Type_COLINFO:				$this->_readColInfo();					break;
 771					case self::XLS_Type_DIMENSION:				$this->_readDefault();					break;
 772					case self::XLS_Type_ROW:					$this->_readRow();						break;
 773					case self::XLS_Type_DBCELL:					$this->_readDefault();					break;
 774					case self::XLS_Type_RK:						$this->_readRk();						break;
 775					case self::XLS_Type_LABELSST:				$this->_readLabelSst();					break;
 776					case self::XLS_Type_MULRK:					$this->_readMulRk();					break;
 777					case self::XLS_Type_NUMBER:					$this->_readNumber();					break;
 778					case self::XLS_Type_FORMULA:				$this->_readFormula();					break;
 779					case self::XLS_Type_SHAREDFMLA:				$this->_readSharedFmla();				break;
 780					case self::XLS_Type_BOOLERR:				$this->_readBoolErr();					break;
 781					case self::XLS_Type_MULBLANK:				$this->_readMulBlank();					break;
 782					case self::XLS_Type_LABEL:					$this->_readLabel();					break;
 783					case self::XLS_Type_BLANK:					$this->_readBlank();					break;
 784					case self::XLS_Type_MSODRAWING:				$this->_readMsoDrawing();				break;
 785					case self::XLS_Type_OBJ:					$this->_readObj();						break;
 786					case self::XLS_Type_WINDOW2:				$this->_readWindow2();					break;
 787					case self::XLS_Type_SCL:					$this->_readScl();						break;
 788					case self::XLS_Type_PANE:					$this->_readPane();						break;
 789					case self::XLS_Type_SELECTION:				$this->_readSelection();				break;
 790					case self::XLS_Type_MERGEDCELLS:			$this->_readMergedCells();				break;
 791					case self::XLS_Type_HYPERLINK:				$this->_readHyperLink();				break;
 792					case self::XLS_Type_DATAVALIDATIONS:		$this->_readDataValidations();			break;
 793					case self::XLS_Type_DATAVALIDATION:			$this->_readDataValidation();			break;
 794					case self::XLS_Type_SHEETLAYOUT:			$this->_readSheetLayout();				break;
 795					case self::XLS_Type_SHEETPROTECTION:		$this->_readSheetProtection();			break;
 796					case self::XLS_Type_RANGEPROTECTION:		$this->_readRangeProtection();			break;
 797					case self::XLS_Type_NOTE:					$this->_readNote();						break;
 798					//case self::XLS_Type_IMDATA:				$this->_readImData();					break;
 799					case self::XLS_Type_TXO:					$this->_readTextObject();				break;
 800					case self::XLS_Type_CONTINUE:				$this->_readContinue();					break;
 801					case self::XLS_Type_EOF:					$this->_readDefault();					break 2;
 802					default:									$this->_readDefault();					break;
 803				}
 804
 805			}
 806
 807			// treat MSODRAWING records, sheet-level Escher
 808			if (!$this->_readDataOnly && $this->_drawingData) {
 809				$escherWorksheet = new PHPExcel_Shared_Escher();
 810				$reader = new PHPExcel_Reader_Excel5_Escher($escherWorksheet);
 811				$escherWorksheet = $reader->load($this->_drawingData);
 812
 813				// debug Escher stream
 814				//$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
 815				//$debug->load($this->_drawingData);
 816
 817				// get all spContainers in one long array, so they can be mapped to OBJ records
 818				$allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
 819			}
 820
 821			// treat OBJ records
 822			foreach ($this->_objs as $n => $obj) {
 823//				echo '<hr /><b>Object</b> reference is ',$n,'<br />';
 824//				var_dump($obj);
 825//				echo '<br />';
 826
 827				// the first shape container never has a corresponding OBJ record, hence $n + 1
 828				if (isset($allSpContainers[$n + 1]) && is_object($allSpContainers[$n + 1])) {
 829					$spContainer = $allSpContainers[$n + 1];
 830
 831					// we skip all spContainers that are a part of a group shape since we cannot yet handle those
 832					if ($spContainer->getNestingLevel() > 1) {
 833						continue;
 834					}
 835
 836					// calculate the width and height of the shape
 837					list($startColumn, $startRow) = PHPExcel_Cell::coordinateFromString($spContainer->getStartCoordinates());
 838					list($endColumn, $endRow) = PHPExcel_Cell::coordinateFromString($spContainer->getEndCoordinates());
 839
 840					$startOffsetX = $spContainer->getStartOffsetX();
 841					$startOffsetY = $spContainer->getStartOffsetY();
 842					$endOffsetX = $spContainer->getEndOffsetX();
 843					$endOffsetY = $spContainer->getEndOffsetY();
 844
 845					$width = PHPExcel_Shared_Excel5::getDistanceX($this->_phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
 846					$height = PHPExcel_Shared_Excel5::getDistanceY($this->_phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
 847
 848					// calculate offsetX and offsetY of the shape
 849					$offsetX = $startOffsetX * PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, $startColumn) / 1024;
 850					$offsetY = $startOffsetY * PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $startRow) / 256;
 851
 852					switch ($obj['otObjType']) {
 853						case 0x19:
 854							// Note
 855//							echo 'Cell Annotation Object<br />';
 856//							echo 'Object ID is ',$obj['idObjID'],'<br />';
 857//
 858							if (isset($this->_cellNotes[$obj['idObjID']])) {
 859								$cellNote = $this->_cellNotes[$obj['idObjID']];
 860
 861								if (isset($this->_textObjects[$obj['idObjID']])) {
 862									$textObject = $this->_textObjects[$obj['idObjID']];
 863									$this->_cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
 864								}
 865							}
 866							break;
 867
 868						case 0x08:
 869//							echo 'Picture Object<br />';
 870							// picture
 871
 872							// get index to BSE entry (1-based)
 873							$BSEindex = $spContainer->getOPT(0x0104);
 874							$BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
 875							$BSE = $BSECollection[$BSEindex - 1];
 876							$blipType = $BSE->getBlipType();
 877
 878							// need check because some blip types are not supported by Escher reader such as EMF
 879							if ($blip = $BSE->getBlip()) {
 880								$ih = imagecreatefromstring($blip->getData());
 881								$drawing = new PHPExcel_Worksheet_MemoryDrawing();
 882								$drawing->setImageResource($ih);
 883
 884								// width, height, offsetX, offsetY
 885								$drawing->setResizeProportional(false);
 886								$drawing->setWidth($width);
 887								$drawing->setHeight($height);
 888								$drawing->setOffsetX($offsetX);
 889								$drawing->setOffsetY($offsetY);
 890
 891								switch ($blipType) {
 892									case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG:
 893										$drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG);
 894										$drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_JPEG);
 895										break;
 896
 897									case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG:
 898										$drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG);
 899										$drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_PNG);
 900										break;
 901								}
 902
 903								$drawing->setWorksheet($this->_phpSheet);
 904								$drawing->setCoordinates($spContainer->getStartCoordinates());
 905							}
 906
 907							break;
 908
 909						default:
 910							// other object type
 911							break;
 912
 913					}
 914				}
 915			}
 916
 917			// treat SHAREDFMLA records
 918			if ($this->_version == self::XLS_BIFF8) {
 919				foreach ($this->_sharedFormulaParts as $cell => $baseCell) {
 920					list($column, $row) = PHPExcel_Cell::coordinateFromString($cell);
 921					if (($this->getReadFilter() !== NULL) && $this->getReadFilter()->readCell($column, $row, $this->_phpSheet->getTitle()) ) {
 922						$formula = $this->_getFormulaFromStructure($this->_sharedFormulas[$baseCell], $cell);
 923						$this->_phpSheet->getCell($cell)->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA);
 924					}
 925				}
 926			}
 927
 928			if (!empty($this->_cellNotes)) {
 929				foreach($this->_cellNotes as $note => $noteDetails) {
 930					if (!isset($noteDetails['objTextData'])) {
 931						if (isset($this->_textObjects[$note])) {
 932							$textObject = $this->_textObjects[$note];
 933							$noteDetails['objTextData'] = $textObject;
 934						} else {
 935							$noteDetails['objTextData']['text'] = '';
 936						}
 937					}
 938//					echo '<b>Cell annotation ',$note,'</b><br />';
 939//					var_dump($noteDetails);
 940//					echo '<br />';
 941					$cellAddress = str_replace('$','',$noteDetails['cellRef']);
 942					$this->_phpSheet->getComment( $cellAddress )
 943													->setAuthor( $noteDetails['author'] )
 944													->setText($this->_parseRichText($noteDetails['objTextData']['text']) );
 945				}
 946			}
 947		}
 948
 949		// add the named ranges (defined names)
 950		foreach ($this->_definedname as $definedName) {
 951			if ($definedName['isBuiltInName']) {
 952				switch ($definedName['name']) {
 953
 954				case pack('C', 0x06):
 955					// print area
 956					//	in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
 957
 958					$ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
 959
 960					$extractedRanges = array();
 961					foreach ($ranges as $range) {
 962						// $range should look like one of these
 963						//		Foo!$C$7:$J$66
 964						//		Bar!$A$1:$IV$2
 965
 966						$explodes = explode('!', $range);	// FIXME: what if sheetname contains exclamation mark?
 967						$sheetName = $explodes[0];
 968
 969						if (count($explodes) == 2) {
 970							$extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
 971						}
 972					}
 973					if ($docSheet = $this->_phpExcel->getSheetByName($sheetName)) {
 974						$docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
 975					}
 976					break;
 977
 978				case pack('C', 0x07):
 979					// print titles (repeating rows)
 980					// Assuming BIFF8, there are 3 cases
 981					// 1. repeating rows
 982					//		formula looks like this: Sheet!$A$1:$IV$2
 983					//		rows 1-2 repeat
 984					// 2. repeating columns
 985					//		formula looks like this: Sheet!$A$1:$B$65536
 986					//		columns A-B repeat
 987					// 3. both repeating rows and repeating columns
 988					//		formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
 989
 990					$ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
 991
 992					foreach ($ranges as $range) {
 993						// $range should look like this one of these
 994						//		Sheet!$A$1:$B$65536
 995						//		Sheet!$A$1:$IV$2
 996
 997						$explodes = explode('!', $range);
 998
 999						if (count($explodes) == 2) {
1000							if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) {
1001
1002								$extractedRange = $explodes[1];
1003								$extractedRange = str_replace('$', '', $extractedRange);
1004
1005								$coordinateStrings = explode(':', $extractedRange);
1006								if (count($coordinateStrings) == 2) {
1007									list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[0]);
1008									list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[1]);
1009
1010									if ($firstColumn == 'A' and $lastColumn == 'IV') {
1011										// then we have repeating rows
1012										$docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow, $lastRow));
1013									} elseif ($firstRow == 1 and $lastRow == 65536) {
1014										// then we have repeating columns
1015										$docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn, $lastColumn));
1016									}
1017								}
1018							}
1019						}
1020					}
1021					break;
1022
1023				}
1024			} else {
1025				// Extract range
1026				$explodes = explode('!', $definedName['formula']);
1027
1028				if (count($explodes) == 2) {
1029					if (($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) ||
1030						($docSheet = $this->_phpExcel->getSheetByName(trim($explodes[0],"'")))) {
1031						$extractedRange = $explodes[1];
1032						$extractedRange = str_replace('$', '', $extractedRange);
1033
1034						$localOnly = ($definedName['scope'] == 0) ? false : true;
1035
1036						$scope = ($definedName['scope'] == 0) ?
1037							null : $this->_phpExcel->getSheetByName($this->_sheets[$definedName['scope'] - 1]['name']);
1038
1039						$this->_phpExcel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $docSheet, $extractedRange, $localOnly, $scope) );
1040					}
1041				} else {
1042					//	Named Value
1043					//	TODO Provide support for named values
1044				}
1045			}
1046		}
1047
1048		return $this->_phpExcel;
1049	}
1050
1051
1052	/**
1053	 * Use OLE reader to extract the relevant data streams from the OLE file
1054	 *
1055	 * @param string $pFilename
1056	 */
1057	private function _loadOLE($pFilename)
1058	{
1059		// OLE reader
1060		$ole = new PHPExcel_Shared_OLERead();
1061
1062		// get excel data,
1063		$res = $ole->read($pFilename);
1064		// Get workbook data: workbook stream + sheet streams
1065		$this->_data = $ole->getStream($ole->wrkbook);
1066
1067		// Get summary information data
1068		$this->_summaryInformation = $ole->getStream($ole->summaryInformation);
1069
1070		// Get additional document summary information data
1071		$this->_documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation);
1072
1073		// Get user-defined property data
1074//		$this->_userDefinedProperties = $ole->getUserDefinedProperties();
1075	}
1076
1077
1078	/**
1079	 * Read summary information
1080	 */
1081	private function _readSummaryInformation()
1082	{
1083		if (!isset($this->_summaryInformation)) {
1084			return;
1085		}
1086
1087		// offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
1088		// offset: 2; size: 2;
1089		// offset: 4; size: 2; OS version
1090		// offset: 6; size: 2; OS indicator
1091		// offset: 8; size: 16
1092		// offset: 24; size: 4; section count
1093		$secCount = self::_GetInt4d($this->_summaryInformation, 24);
1094
1095		// offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
1096		// offset: 44; size: 4
1097		$secOffset = self::_GetInt4d($this->_summaryInformation, 44);
1098
1099		// section header
1100		// offset: $secOffset; size: 4; section length
1101		$secLength = self::_GetInt4d($this->_summaryInformation, $secOffset);
1102
1103		// offset: $secOffset+4; size: 4; property count
1104		$countProperties = self::_GetInt4d($this->_summaryInformation, $secOffset+4);
1105
1106		// initialize code page (used to resolve string values)
1107		$codePage = 'CP1252';
1108
1109		// offset: ($secOffset+8); size: var
1110		// loop through property decarations and properties
1111		for ($i = 0; $i < $countProperties; ++$i) {
1112
1113			// offset: ($secOffset+8) + (8 * $i); size: 4; property ID
1114			$id = self::_GetInt4d($this->_summaryInformation, ($secOffset+8) + (8 * $i));
1115
1116			// Use value of property id as appropriate
1117			// offset: ($secOffset+12) + (8 * $i); size: 4; offset from beginning of section (48)
1118			$offset = self::_GetInt4d($this->_summaryInformation, ($secOffset+12) + (8 * $i));
1119
1120			$type = self::_GetInt4d($this->_summaryInformation, $secOffset + $offset);
1121
1122			// initialize property value
1123			$value = null;
1124
1125			// extract property value based on property type
1126			switch ($type) {
1127				case 0x02: // 2 byte signed integer
1128					$value = self::_GetInt2d($this->_summaryInformation, $secOffset + 4 + $offset);
1129					break;
1130
1131				case 0x03: // 4 byte signed integer
1132					$value = self::_GetInt4d($this->_summaryInformation, $secOffset + 4 + $offset);
1133					break;
1134
1135				case 0x13: // 4 byte unsigned integer
1136					// not needed yet, fix later if necessary
1137					break;
1138
1139				case 0x1E: // null-terminated string prepended by dword string length
1140					$byteLength = self::_GetInt4d($this->_summaryInformation, $secOffset + 4 + $offset);
1141					$value = substr($this->_summaryInformation, $secOffset + 8 + $offset, $byteLength);
1142					$value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage);
1143					$value = rtrim($value);
1144					break;
1145
1146				case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
1147					// PHP-time
1148					$value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_summaryInformation, $secOffset + 4 + $offset, 8));
1149					break;
1150
1151				case 0x47: // Clipboard format
1152					// not needed yet, fix later if necessary
1153					break;
1154			}
1155
1156			switch ($id) {
1157				case 0x01:	//	Code Page
1158					$codePage = PHPExcel_Shared_CodePage::NumberToName($value);
1159					break;
1160
1161				case 0x02:	//	Title
1162					$this->_phpExcel->getProperties()->setTitle($value);
1163					break;
1164
1165				case 0x03:	//	Subject
1166					$this->_phpExcel->getProperties()->setSubject($value);
1167					break;
1168
1169				case 0x04:	//	Author (Creator)
1170					$this->_phpExcel->getProperties()->setCreator($value);
1171					break;
1172
1173				case 0x05:	//	Keywords
1174					$this->_phpExcel->getProperties()->setKeywords($value);
1175					break;
1176
1177				case 0x06:	//	Comments (Description)
1178					$this->_phpExcel->getProperties()->setDescription($value);
1179					break;
1180
1181				case 0x07:	//	Template
1182					//	Not supported by PHPExcel
1183					break;
1184
1185				case 0x08:	//	Last Saved By (LastModifiedBy)
1186					$this->_phpExcel->getProperties()->setLastModifiedBy($value);
1187					break;
1188
1189				case 0x09:	//	Revision
1190					//	Not supported by PHPExcel
1191					break;
1192
1193				case 0x0A:	//	Total Editing Time
1194					//	Not supported by PHPExcel
1195					break;
1196
1197				case 0x0B:	//	Last Printed
1198					//	Not supported by PHPExcel
1199					break;
1200
1201				case 0x0C:	//	Created Date/Time
1202					$this->_phpExcel->getProperties()->setCreated($value);
1203					break;
1204
1205				case 0x0D:	//	Modified Date/Time
1206					$this->_phpExcel->getProperties()->setModified($value);
1207					break;
1208
1209				case 0x0E:	//	Number of Pages
1210					//	Not supported by PHPExcel
1211					break;
1212
1213				case 0x0F:	//	Number of Words
1214					//	Not supported by PHPExcel
1215					break;
1216
1217				case 0x10:	//	Number of Characters
1218					//	Not supported by PHPExcel
1219					break;
1220
1221				case 0x11:	//	Thumbnail
1222					//	Not supported by PHPExcel
1223					break;
1224
1225				case 0x12:	//	Name of creating application
1226					//	Not supported by PHPExcel
1227					break;
1228
1229				case 0x13:	//	Security
1230					//	Not supported by PHPExcel
1231					break;
1232
1233			}
1234		}
1235	}
1236
1237
1238	/**
1239	 * Read additional document summary information
1240	 */
1241	private function _readDocumentSummaryInformation()
1242	{
1243		if (!isset($this->_documentSummaryInformation)) {
1244			return;
1245		}
1246
1247		//	offset: 0;	size: 2;	must be 0xFE 0xFF (UTF-16 LE byte order mark)
1248		//	offset: 2;	size: 2;
1249		//	offset: 4;	size: 2;	OS version
1250		//	offset: 6;	size: 2;	OS indicator
1251		//	offset: 8;	size: 16
1252		//	offset: 24;	size: 4;	section count
1253		$secCount = self::_GetInt4d($this->_documentSummaryInformation, 24);
1254//		echo '$secCount = ',$secCount,'<br />';
1255
1256		// offset: 28;	size: 16;	first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
1257		// offset: 44;	size: 4;	first section offset
1258		$secOffset = self::_GetInt4d($this->_documentSummaryInformation, 44);
1259//		echo '$secOffset = ',$secOffset,'<br />';
1260
1261		//	section header
1262		//	offset: $secOffset;	size: 4;	section length
1263		$secLength = self::_GetInt4d($this->_documentSummaryInformation, $secOffset);
1264//		echo '$secLength = ',$secLength,'<br />';
1265
1266		//	offset: $secOffset+4;	size: 4;	property count
1267		$countProperties = self::_GetInt4d($this->_documentSummaryInformation, $secOffset+4);
1268//		echo '$countProperties = ',$countProperties,'<br />';
1269
1270		// initialize code page (used to resolve string values)
1271		$codePage = 'CP1252';
1272
1273		//	offset: ($secOffset+8);	size: var
1274		//	loop through property decarations and properties
1275		for ($i = 0; $i < $countProperties; ++$i) {
1276//			echo 'Property ',$i,'<br />';
1277			//	offset: ($secOffset+8) + (8 * $i);	size: 4;	property ID
1278			$id = self::_GetInt4d($this->_documentSummaryInformation, ($secOffset+8) + (8 * $i));
1279//			echo 'ID is ',$id,'<br />';
1280
1281			// Use value of property id as appropriate
1282			// offset: 60 + 8 * $i;	size: 4;	offset from beginning of section (48)
1283			$offset = self::_GetInt4d($this->_documentSummaryInformation, ($secOffset+12) + (8 * $i));
1284
1285			$type = self::_GetInt4d($this->_documentSummaryInformation, $secOffset + $offset);
1286//			echo 'Type is ',$type,', ';
1287
1288			// initialize property value
1289			$value = null;
1290
1291			// extract property value based on property type
1292			switch ($type) {
1293				case 0x02:	//	2 byte signed integer
1294					$value = self::_GetInt2d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
1295					break;
1296
1297				case 0x03:	//	4 byte signed integer
1298					$value = self::_GetInt4d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
1299					break;
1300
1301				case 0x0B:  // Boolean
1302					$value = self::_GetInt2d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
1303					$value = ($value == 0 ? false : true);
1304					break;
1305
1306				case 0x13:	//	4 byte unsigned integer
1307					// not needed yet, fix later if necessary
1308					break;
1309
1310				case 0x1E:	//	null-terminated string prepended by dword string length
1311					$byteLength = self::_GetInt4d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
1312					$value = substr($this->_documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
1313					$value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage);
1314					$value = rtrim($value);
1315					break;
1316
1317				case 0x40:	//	Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
1318					// PHP-Time
1319					$value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_documentSummaryInformation, $secOffset + 4 + $offset, 8));
1320					break;
1321
1322				case 0x47:	//	Clipboard format
1323					// not needed yet, fix later if necessary
1324					break;
1325			}
1326
1327			switch ($id) {
1328				case 0x01:	//	Code Page
1329					$codePage = PHPExcel_Shared_CodePage::NumberToName($value);
1330					break;
1331
1332				case 0x02:	//	Category
1333					$this->_phpExcel->getProperties()->setCategory($value);
1334					break;
1335
1336				case 0x03:	//	Presentation Target
1337					//	Not supported by PHPExcel
1338					break;
1339
1340				case 0x04:	//	Bytes
1341					//	Not supported by PHPExcel
1342					break;
1343
1344				case 0x05:	//	Lines
1345					//	Not supported by PHPExcel
1346					break;
1347
1348				case 0x06:	//	Paragraphs
1349					//	Not supported by PHPExcel
1350					break;
1351
1352				case 0x07:	//	Slides
1353					//	Not supported by PHPExcel
1354					break;
1355
1356				case 0x08:	//	Notes
1357					//	Not supported by PHPExcel
1358					break;
1359
1360				case 0x09:	//	Hidden Slides
1361					//	Not supported by PHPExcel
1362					break;
1363
1364				case 0x0A:	//	MM Clips
1365					//	Not supported by PHPExcel
1366					break;
1367
1368				case 0x0B:	//	Scale Crop
1369					//	Not supported by PHPExcel
1370					break;
1371
1372				case 0x0C:	//	Heading Pairs
1373					//	Not supported by PHPExcel
1374					break;
1375
1376				case 0x0D:	//	Titles of Parts
1377					//	Not supported by PHPExcel
1378					break;
1379
1380				case 0x0E:	//	Manager
1381					$this->_phpExcel->getProperties()->setManager($value);
1382					break;
1383
1384				case 0x0F:	//	Company
1385					$this->_phpExcel->getProperties()->setCompany($value);
1386					break;
1387
1388				case 0x10:	//	Links up-to-date
1389					//	Not supported by PHPExcel
1390					break;
1391
1392			}
1393		}
1394	}
1395
1396
1397	/**
1398	 * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
1399	 */
1400	private function _readDefault()
1401	{
1402		$length = self::_GetInt2d($this->_data, $this->_pos + 2);
1403//		$recordData = substr($this->_data, $this->_pos + 4, $length);
1404
1405		// move stream pointer to next record
1406		$this->_pos += 4 + $length;
1407	}
1408
1409
1410	/**
1411	 *	The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
1412	 *		this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
1413	 */
1414	private function _readNote()
1415	{
1416//		echo '<b>Read Cell Annotation</b><br />';
1417		$length = self::_GetInt2d($this->_data, $this->_pos + 2);
1418		$recordData = substr($this->_data, $this->_pos + 4, $length);
1419
1420		// move stream pointer to next record
1421		$this->_pos += 4 + $length;
1422
1423		if ($this->_readDataOnly) {
1424			return;
1425		}
1426
1427		$cellAddress = $this->_readBIFF8CellAddress(substr($recordData, 0, 4));
1428		if ($this->_version == self::XLS_BIFF8) {
1429			$noteObjID = self::_GetInt2d($recordData, 6);
1430			$noteAuthor = self::_readUnicodeStringLong(substr($recordData, 8));
1431			$noteAuthor = $noteAuthor['value'];
1432//			echo 'Note Address=',$cellAddress,'<br />';
1433//			echo 'Note Object ID=',$noteObjID,'<br />';
1434//			echo 'Note Author=',$noteAuthor,'<hr />';
1435//
1436			$this->_cellNotes[$noteObjID] = array('cellRef'		=> $cellAddress,
1437												  'objectID'	=> $noteObjID,
1438												  'author'		=> $noteAuthor
1439												 );
1440		} else {
1441			$extension = false;
1442			if ($cellAddress == '$B$65536') {
1443				//	If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
1444				//		note from the previous cell annotation. We're not yet handling this, so annotations longer than the
1445				//		max 2048 bytes will probably throw a wobbly.
1446				$row = self::_GetInt2d($recordData, 0);
1447				$extension = true;
1448				$cellAddress = array_pop(array_keys($this->_phpSheet->getComments()));
1449			}
1450//			echo 'Note Address=',$cellAddress,'<br />';
1451
1452			$cellAddress = str_replace('$','',$cellAddress);
1453			$noteLength = self::_GetInt2d($recordData, 4);
1454			$noteText = trim(substr($recordData, 6));
1455//			echo 'Note Length=',$noteLength,'<br />';
1456//			echo 'Note Text=',$noteText,'<br />';
1457
1458			if ($extension) {
1459				//	Concatenate this extension with the currently set comment for the cell
1460				$comment = $this->_phpSheet->getComment( $cellAddress );
1461				$commentText = $comment->getText()->getPlainText();
1462				$comment->setText($this->_parseRichText($commentText.$noteText) );
1463			} else {
1464				//	Set comment for the cell
1465				$this->_phpSheet->getComment( $cellAddress )
1466//													->setAuthor( $author )
1467													->setText($this->_parseRichText($noteText) );
1468			}
1469		}
1470
1471	}
1472
1473
1474	/**
1475	 *	The TEXT Object record contains the text associated with a cell annotation.
1476	 */
1477	private function _readTextObject()
1478	{
1479		$length = self::_GetInt2d($this->_data, $this->_pos + 2);
1480		$recordData = substr($this->_data, $this->_pos + 4, $length);
1481
1482		// move stream pointer to next record
1483		$this->_pos += 4 + $length;
1484
1485		if ($this->_readDataOnly) {
1486			return;
1487		}
1488
1489		// recordData consists of an array of subrecords looking like this:
1490		//	grbit: 2 bytes; Option Flags
1491		//	rot: 2 bytes; rotation
1492		//	cchText: 2 bytes; length of the text (in the first continue record)
1493		//	cbRuns: 2 bytes; length of the formatting (in the second continue record)
1494		// followed by the continuation records containing the actual text and formatting
1495		$grbitOpts	= self::_GetInt2d($recordData, 0);
1496		$rot		= self::_GetInt2d($recordData, 2);
1497		$cchText	= self::_GetInt2d($recordData, 10);
1498		$cbRuns		= self::_GetInt2d($recordData, 12);
1499		$text		= $this->_getSplicedRecordData();
1500
1501		$this->_textObjects[$this->textObjRef] = array(
1502				'text'		=> substr($text["recordData"],$text["spliceOffsets"][0]+1,$cchText),
1503				'format'	=

Large files files are truncated, but you can click here to view the full file