PageRenderTime 386ms CodeModel.GetById 151ms app.highlight 112ms RepoModel.GetById 58ms app.codeStats 4ms

/framework/lib/classes/PHPExcel/Reader/Excel5.php

https://bitbucket.org/designbyheart/original
PHP | 6667 lines | 3767 code | 1018 blank | 1882 comment | 492 complexity | 0be0e05c81062da5161e5cdf36e4a8c5 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 - 2011 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 - 2011 PHPExcel (http://www.codeplex.com/PHPExcel)
  24 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt	LGPL
  25 * @version    1.7.6, 2011-02-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 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 - 2011 PHPExcel (http://www.codeplex.com/PHPExcel)
  77 */
  78class PHPExcel_Reader_Excel5 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	 *	Read data only?
 163	 *	Identifies whether the Reader should only read data values for cells, and ignore any formatting information;
 164	 *		or whether it should read both data and formatting
 165	 *
 166	 *	@var	boolean
 167	 */
 168	private $_readDataOnly = false;
 169
 170	/**
 171	 *	Restrict which sheets should be loaded?
 172	 *	This property holds an array of worksheet names to be loaded. If null, then all worksheets will be loaded.
 173	 *
 174	 *	@var	array of string
 175	 */
 176	private $_loadSheetsOnly = null;
 177
 178	/**
 179	 * PHPExcel_Reader_IReadFilter instance
 180	 *
 181	 * @var PHPExcel_Reader_IReadFilter
 182	 */
 183	private $_readFilter = null;
 184
 185	/**
 186	 * Summary Information stream data.
 187	 *
 188	 * @var string
 189	 */
 190	private $_summaryInformation;
 191
 192	/**
 193	 * Extended Summary Information stream data.
 194	 *
 195	 * @var string
 196	 */
 197	private $_documentSummaryInformation;
 198
 199	/**
 200	 * User-Defined Properties stream data.
 201	 *
 202	 * @var string
 203	 */
 204	private $_userDefinedProperties;
 205
 206	/**
 207	 * Workbook stream data. (Includes workbook globals substream as well as sheet substreams)
 208	 *
 209	 * @var string
 210	 */
 211	private $_data;
 212
 213	/**
 214	 * Size in bytes of $this->_data
 215	 *
 216	 * @var int
 217	 */
 218	private $_dataSize;
 219
 220	/**
 221	 * Current position in stream
 222	 *
 223	 * @var integer
 224	 */
 225	private $_pos;
 226
 227	/**
 228	 * Workbook to be returned by the reader.
 229	 *
 230	 * @var PHPExcel
 231	 */
 232	private $_phpExcel;
 233
 234	/**
 235	 * Worksheet that is currently being built by the reader.
 236	 *
 237	 * @var PHPExcel_Worksheet
 238	 */
 239	private $_phpSheet;
 240
 241	/**
 242	 * BIFF version
 243	 *
 244	 * @var int
 245	 */
 246	private $_version;
 247
 248	/**
 249	 * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
 250	 * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'
 251	 *
 252	 * @var string
 253	 */
 254	private $_codepage;
 255
 256	/**
 257	 * Shared formats
 258	 *
 259	 * @var array
 260	 */
 261	private $_formats;
 262
 263	/**
 264	 * Shared fonts
 265	 *
 266	 * @var array
 267	 */
 268	private $_objFonts;
 269
 270	/**
 271	 * Color palette
 272	 *
 273	 * @var array
 274	 */
 275	private $_palette;
 276
 277	/**
 278	 * Worksheets
 279	 *
 280	 * @var array
 281	 */
 282	private $_sheets;
 283
 284	/**
 285	 * External books
 286	 *
 287	 * @var array
 288	 */
 289	private $_externalBooks;
 290
 291	/**
 292	 * REF structures. Only applies to BIFF8.
 293	 *
 294	 * @var array
 295	 */
 296	private $_ref;
 297
 298	/**
 299	 * External names
 300	 *
 301	 * @var array
 302	 */
 303	private $_externalNames;
 304
 305	/**
 306	 * Defined names
 307	 *
 308	 * @var array
 309	 */
 310	private $_definedname;
 311
 312	/**
 313	 * Shared strings. Only applies to BIFF8.
 314	 *
 315	 * @var array
 316	 */
 317	private $_sst;
 318
 319	/**
 320	 * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
 321	 *
 322	 * @var boolean
 323	 */
 324	private $_frozen;
 325
 326	/**
 327	 * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
 328	 *
 329	 * @var boolean
 330	 */
 331	private $_isFitToPages;
 332
 333	/**
 334	 * Objects. One OBJ record contributes with one entry.
 335	 *
 336	 * @var array
 337	 */
 338	private $_objs;
 339
 340	/**
 341	 * Text Objects. One TXO record corresponds with one entry.
 342	 *
 343	 * @var array
 344	 */
 345	private $_textObjects;
 346
 347	/**
 348	 * Cell Annotations (BIFF8)
 349	 *
 350	 * @var array
 351	 */
 352	private $_cellNotes;
 353
 354	/**
 355	 * The combined MSODRAWINGGROUP data
 356	 *
 357	 * @var string
 358	 */
 359	private $_drawingGroupData;
 360
 361	/**
 362	 * The combined MSODRAWING data (per sheet)
 363	 *
 364	 * @var string
 365	 */
 366	private $_drawingData;
 367
 368	/**
 369	 * Keep track of XF index
 370	 *
 371	 * @var int
 372	 */
 373	private $_xfIndex;
 374
 375	/**
 376	 * Mapping of XF index (that is a cell XF) to final index in cellXf collection
 377	 *
 378	 * @var array
 379	 */
 380	private $_mapCellXfIndex;
 381
 382	/**
 383	 * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection
 384	 *
 385	 * @var array
 386	 */
 387	private $_mapCellStyleXfIndex;
 388
 389	/**
 390	 * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
 391	 *
 392	 * @var array
 393	 */
 394	private $_sharedFormulas;
 395
 396	/**
 397	 * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
 398	 * refers to a shared formula.
 399	 *
 400	 * @var array
 401	 */
 402	private $_sharedFormulaParts;
 403
 404
 405	/**
 406	 *	Read data only?
 407	 *		If this is true, then the Reader will only read data values for cells, it will not read any formatting information.
 408	 *		If false (the default) it will read data and formatting.
 409	 *
 410	 *	@return	boolean
 411	 */
 412	public function getReadDataOnly()
 413	{
 414		return $this->_readDataOnly;
 415	}
 416
 417	/**
 418	 *	Set read data only
 419	 *		Set to true, to advise the Reader only to read data values for cells, and to ignore any formatting information.
 420	 *		Set to false (the default) to advise the Reader to read both data and formatting for cells.
 421	 *
 422	 *	@param	boolean	$pValue
 423	 *
 424	 *	@return	PHPExcel_Reader_Excel5
 425	 */
 426	public function setReadDataOnly($pValue = false)
 427	{
 428		$this->_readDataOnly = $pValue;
 429		return $this;
 430	}
 431
 432	/**
 433	 *	Get which sheets to load
 434	 *		Returns either an array of worksheet names (the list of worksheets that should be loaded), or a null
 435	 *			indicating that all worksheets in the workbook should be loaded.
 436	 *
 437	 *	@return mixed
 438	 */
 439	public function getLoadSheetsOnly()
 440	{
 441		return $this->_loadSheetsOnly;
 442	}
 443
 444	/**
 445	 *	Set which sheets to load
 446	 *
 447	 *	@param mixed $value
 448	 *		This should be either an array of worksheet names to be loaded, or a string containing a single worksheet name.
 449	 *		If NULL, then it tells the Reader to read all worksheets in the workbook
 450	 *
 451	 *	@return PHPExcel_Reader_Excel5
 452	 */
 453	public function setLoadSheetsOnly($value = null)
 454	{
 455		$this->_loadSheetsOnly = is_array($value) ?
 456			$value : array($value);
 457		return $this;
 458	}
 459
 460	/**
 461	 *	Set all sheets to load
 462	 *		Tells the Reader to load all worksheets from the workbook.
 463	 *
 464	 *	@return	PHPExcel_Reader_Excel5
 465	 */
 466	public function setLoadAllSheets()
 467	{
 468		$this->_loadSheetsOnly = null;
 469		return $this;
 470	}
 471
 472	/**
 473	 * Read filter
 474	 *
 475	 * @return PHPExcel_Reader_IReadFilter
 476	 */
 477	public function getReadFilter() {
 478		return $this->_readFilter;
 479	}
 480
 481	/**
 482	 * Set read filter
 483	 *
 484	 * @param PHPExcel_Reader_IReadFilter $pValue
 485	 * @return PHPExcel_Reader_Excel5
 486	 */
 487	public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
 488		$this->_readFilter = $pValue;
 489		return $this;
 490	}
 491
 492	/**
 493	 * Create a new PHPExcel_Reader_Excel5 instance
 494	 */
 495	public function __construct() {
 496		$this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
 497	}
 498
 499	/**
 500	 * Can the current PHPExcel_Reader_IReader read the file?
 501	 *
 502	 * @param 	string 		$pFileName
 503	 * @return 	boolean
 504	 */
 505	public function canRead($pFilename)
 506	{
 507		// Check if file exists
 508		if (!file_exists($pFilename)) {
 509			throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
 510		}
 511
 512		try {
 513			// Use ParseXL for the hard work.
 514			$ole = new PHPExcel_Shared_OLERead();
 515
 516			// get excel data
 517			$res = $ole->read($pFilename);
 518			return true;
 519
 520		} catch (Exception $e) {
 521			return false;
 522		}
 523	}
 524
 525	/**
 526	 * Reads names of the worksheets from a file, without parsing the whole file to a PHPExcel object
 527	 *
 528	 * @param 	string 		$pFilename
 529	 * @throws 	Exception
 530	 */
 531	public function listWorksheetNames($pFilename)
 532	{
 533		// Check if file exists
 534		if (!file_exists($pFilename)) {
 535			throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
 536		}
 537
 538		$worksheetNames = array();
 539
 540		// Read the OLE file
 541		$this->_loadOLE($pFilename);
 542
 543		// total byte size of Excel data (workbook global substream + sheet substreams)
 544		$this->_dataSize = strlen($this->_data);
 545
 546		$this->_pos		= 0;
 547		$this->_sheets	= array();
 548
 549		// Parse Workbook Global Substream
 550		while ($this->_pos < $this->_dataSize) {
 551			$code = self::_GetInt2d($this->_data, $this->_pos);
 552
 553			switch ($code) {
 554				case self::XLS_Type_BOF:	$this->_readBof();		break;
 555				case self::XLS_Type_SHEET:	$this->_readSheet();	break;
 556				case self::XLS_Type_EOF:	$this->_readDefault();	break 2;
 557				default:					$this->_readDefault();	break;
 558			}
 559		}
 560
 561		foreach ($this->_sheets as $sheet) {
 562			if ($sheet['sheetType'] != 0x00) {
 563				// 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
 564				continue;
 565			}
 566
 567			$worksheetNames[] = $sheet['name'];
 568		}
 569
 570		return $worksheetNames;
 571	}
 572
 573
 574	/**
 575	 * Loads PHPExcel from file
 576	 *
 577	 * @param 	string 		$pFilename
 578	 * @return 	PHPExcel
 579	 * @throws 	Exception
 580	 */
 581	public function load($pFilename)
 582	{
 583		// Read the OLE file
 584		$this->_loadOLE($pFilename);
 585
 586		// Initialisations
 587		$this->_phpExcel = new PHPExcel;
 588		$this->_phpExcel->removeSheetByIndex(0); // remove 1st sheet
 589		if (!$this->_readDataOnly) {
 590			$this->_phpExcel->removeCellStyleXfByIndex(0); // remove the default style
 591			$this->_phpExcel->removeCellXfByIndex(0); // remove the default style
 592		}
 593
 594		// Read the summary information stream (containing meta data)
 595		$this->_readSummaryInformation();
 596
 597		// Read the Additional document summary information stream (containing application-specific meta data)
 598		$this->_readDocumentSummaryInformation();
 599
 600		// total byte size of Excel data (workbook global substream + sheet substreams)
 601		$this->_dataSize = strlen($this->_data);
 602
 603		// initialize
 604		$this->_pos					= 0;
 605		$this->_codepage			= 'CP1252';
 606		$this->_formats				= array();
 607		$this->_objFonts			= array();
 608		$this->_palette				= array();
 609		$this->_sheets				= array();
 610		$this->_externalBooks		= array();
 611		$this->_ref					= array();
 612		$this->_definedname			= array();
 613		$this->_sst					= array();
 614		$this->_drawingGroupData	= '';
 615		$this->_xfIndex				= '';
 616		$this->_mapCellXfIndex		= array();
 617		$this->_mapCellStyleXfIndex	= array();
 618
 619		// Parse Workbook Global Substream
 620		while ($this->_pos < $this->_dataSize) {
 621			$code = self::_GetInt2d($this->_data, $this->_pos);
 622
 623			switch ($code) {
 624				case self::XLS_Type_BOF:			$this->_readBof();				break;
 625				case self::XLS_Type_FILEPASS:		$this->_readFilepass();			break;
 626				case self::XLS_Type_CODEPAGE:		$this->_readCodepage();			break;
 627				case self::XLS_Type_DATEMODE:		$this->_readDateMode();			break;
 628				case self::XLS_Type_FONT:			$this->_readFont();				break;
 629				case self::XLS_Type_FORMAT:			$this->_readFormat();			break;
 630				case self::XLS_Type_XF:				$this->_readXf();				break;
 631				case self::XLS_Type_XFEXT:			$this->_readXfExt();			break;
 632				case self::XLS_Type_STYLE:			$this->_readStyle();			break;
 633				case self::XLS_Type_PALETTE:		$this->_readPalette();			break;
 634				case self::XLS_Type_SHEET:			$this->_readSheet();			break;
 635				case self::XLS_Type_EXTERNALBOOK:	$this->_readExternalBook();		break;
 636				case self::XLS_Type_EXTERNNAME:		$this->_readExternName();		break;
 637				case self::XLS_Type_EXTERNSHEET:	$this->_readExternSheet();		break;
 638				case self::XLS_Type_DEFINEDNAME:	$this->_readDefinedName();		break;
 639				case self::XLS_Type_MSODRAWINGGROUP:	$this->_readMsoDrawingGroup();	break;
 640				case self::XLS_Type_SST:			$this->_readSst();				break;
 641				case self::XLS_Type_EOF:			$this->_readDefault();			break 2;
 642				default:							$this->_readDefault();			break;
 643			}
 644		}
 645
 646		// Resolve indexed colors for font, fill, and border colors
 647		// Cannot be resolved already in XF record, because PALETTE record comes afterwards
 648		if (!$this->_readDataOnly) {
 649			foreach ($this->_objFonts as $objFont) {
 650				if (isset($objFont->colorIndex)) {
 651					$color = self::_readColor($objFont->colorIndex,$this->_palette,$this->_version);
 652					$objFont->getColor()->setRGB($color['rgb']);
 653				}
 654			}
 655
 656			foreach ($this->_phpExcel->getCellXfCollection() as $objStyle) {
 657				// fill start and end color
 658				$fill = $objStyle->getFill();
 659
 660				if (isset($fill->startcolorIndex)) {
 661					$startColor = self::_readColor($fill->startcolorIndex,$this->_palette,$this->_version);
 662					$fill->getStartColor()->setRGB($startColor['rgb']);
 663				}
 664
 665				if (isset($fill->endcolorIndex)) {
 666					$endColor = self::_readColor($fill->endcolorIndex,$this->_palette,$this->_version);
 667					$fill->getEndColor()->setRGB($endColor['rgb']);
 668				}
 669
 670				// border colors
 671				$top      = $objStyle->getBorders()->getTop();
 672				$right    = $objStyle->getBorders()->getRight();
 673				$bottom   = $objStyle->getBorders()->getBottom();
 674				$left     = $objStyle->getBorders()->getLeft();
 675				$diagonal = $objStyle->getBorders()->getDiagonal();
 676
 677				if (isset($top->colorIndex)) {
 678					$borderTopColor = self::_readColor($top->colorIndex,$this->_palette,$this->_version);
 679					$top->getColor()->setRGB($borderTopColor['rgb']);
 680				}
 681
 682				if (isset($right->colorIndex)) {
 683					$borderRightColor = self::_readColor($right->colorIndex,$this->_palette,$this->_version);
 684					$right->getColor()->setRGB($borderRightColor['rgb']);
 685				}
 686
 687				if (isset($bottom->colorIndex)) {
 688					$borderBottomColor = self::_readColor($bottom->colorIndex,$this->_palette,$this->_version);
 689					$bottom->getColor()->setRGB($borderBottomColor['rgb']);
 690				}
 691
 692				if (isset($left->colorIndex)) {
 693					$borderLeftColor = self::_readColor($left->colorIndex,$this->_palette,$this->_version);
 694					$left->getColor()->setRGB($borderLeftColor['rgb']);
 695				}
 696
 697				if (isset($diagonal->colorIndex)) {
 698					$borderDiagonalColor = self::_readColor($diagonal->colorIndex,$this->_palette,$this->_version);
 699					$diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
 700				}
 701			}
 702		}
 703
 704		// treat MSODRAWINGGROUP records, workbook-level Escher
 705		if (!$this->_readDataOnly && $this->_drawingGroupData) {
 706			$escherWorkbook = new PHPExcel_Shared_Escher();
 707			$reader = new PHPExcel_Reader_Excel5_Escher($escherWorkbook);
 708			$escherWorkbook = $reader->load($this->_drawingGroupData);
 709
 710			// debug Escher stream
 711			//$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
 712			//$debug->load($this->_drawingGroupData);
 713		}
 714
 715		// Parse the individual sheets
 716		foreach ($this->_sheets as $sheet) {
 717
 718			if ($sheet['sheetType'] != 0x00) {
 719				// 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
 720				continue;
 721			}
 722
 723			// check if sheet should be skipped
 724			if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) {
 725				continue;
 726			}
 727
 728			// add sheet to PHPExcel object
 729			$this->_phpSheet = $this->_phpExcel->createSheet();
 730			$this->_phpSheet->setTitle($sheet['name']);
 731			$this->_phpSheet->setSheetState($sheet['sheetState']);
 732
 733			$this->_pos = $sheet['offset'];
 734
 735			// Initialize isFitToPages. May change after reading SHEETPR record.
 736			$this->_isFitToPages = false;
 737
 738			// Initialize drawingData
 739			$this->_drawingData = '';
 740
 741			// Initialize objs
 742			$this->_objs = array();
 743
 744			// Initialize shared formula parts
 745			$this->_sharedFormulaParts = array();
 746
 747			// Initialize shared formulas
 748			$this->_sharedFormulas = array();
 749
 750			// Initialize text objs
 751			$this->_textObjects = array();
 752
 753			// Initialize cell annotations
 754			$this->_cellNotes = array();
 755			$this->textObjRef = -1;
 756
 757			while ($this->_pos <= $this->_dataSize - 4) {
 758				$code = self::_GetInt2d($this->_data, $this->_pos);
 759
 760				switch ($code) {
 761					case self::XLS_Type_BOF:					$this->_readBof();						break;
 762					case self::XLS_Type_PRINTGRIDLINES:			$this->_readPrintGridlines();			break;
 763					case self::XLS_Type_DEFAULTROWHEIGHT:		$this->_readDefaultRowHeight();			break;
 764					case self::XLS_Type_SHEETPR:				$this->_readSheetPr();					break;
 765					case self::XLS_Type_HORIZONTALPAGEBREAKS:	$this->_readHorizontalPageBreaks();		break;
 766					case self::XLS_Type_VERTICALPAGEBREAKS:		$this->_readVerticalPageBreaks();		break;
 767					case self::XLS_Type_HEADER:					$this->_readHeader();					break;
 768					case self::XLS_Type_FOOTER:					$this->_readFooter();					break;
 769					case self::XLS_Type_HCENTER:				$this->_readHcenter();					break;
 770					case self::XLS_Type_VCENTER:				$this->_readVcenter();					break;
 771					case self::XLS_Type_LEFTMARGIN:				$this->_readLeftMargin();				break;
 772					case self::XLS_Type_RIGHTMARGIN:			$this->_readRightMargin();				break;
 773					case self::XLS_Type_TOPMARGIN:				$this->_readTopMargin();				break;
 774					case self::XLS_Type_BOTTOMMARGIN:			$this->_readBottomMargin();				break;
 775					case self::XLS_Type_PAGESETUP:				$this->_readPageSetup();				break;
 776					case self::XLS_Type_PROTECT:				$this->_readProtect();					break;
 777					case self::XLS_Type_SCENPROTECT:			$this->_readScenProtect();				break;
 778					case self::XLS_Type_OBJECTPROTECT:			$this->_readObjectProtect();			break;
 779					case self::XLS_Type_PASSWORD:				$this->_readPassword();					break;
 780					case self::XLS_Type_DEFCOLWIDTH:			$this->_readDefColWidth();				break;
 781					case self::XLS_Type_COLINFO:				$this->_readColInfo();					break;
 782					case self::XLS_Type_DIMENSION:				$this->_readDefault();					break;
 783					case self::XLS_Type_ROW:					$this->_readRow();						break;
 784					case self::XLS_Type_DBCELL:					$this->_readDefault();					break;
 785					case self::XLS_Type_RK:						$this->_readRk();						break;
 786					case self::XLS_Type_LABELSST:				$this->_readLabelSst();					break;
 787					case self::XLS_Type_MULRK:					$this->_readMulRk();					break;
 788					case self::XLS_Type_NUMBER:					$this->_readNumber();					break;
 789					case self::XLS_Type_FORMULA:				$this->_readFormula();					break;
 790					case self::XLS_Type_SHAREDFMLA:				$this->_readSharedFmla();				break;
 791					case self::XLS_Type_BOOLERR:				$this->_readBoolErr();					break;
 792					case self::XLS_Type_MULBLANK:				$this->_readMulBlank();					break;
 793					case self::XLS_Type_LABEL:					$this->_readLabel();					break;
 794					case self::XLS_Type_BLANK:					$this->_readBlank();					break;
 795					case self::XLS_Type_MSODRAWING:				$this->_readMsoDrawing();				break;
 796					case self::XLS_Type_OBJ:					$this->_readObj();						break;
 797					case self::XLS_Type_WINDOW2:				$this->_readWindow2();					break;
 798					case self::XLS_Type_SCL:					$this->_readScl();						break;
 799					case self::XLS_Type_PANE:					$this->_readPane();						break;
 800					case self::XLS_Type_SELECTION:				$this->_readSelection();				break;
 801					case self::XLS_Type_MERGEDCELLS:			$this->_readMergedCells();				break;
 802					case self::XLS_Type_HYPERLINK:				$this->_readHyperLink();				break;
 803					case self::XLS_Type_DATAVALIDATIONS:		$this->_readDataValidations();			break;
 804					case self::XLS_Type_DATAVALIDATION:			$this->_readDataValidation();			break;
 805					case self::XLS_Type_SHEETLAYOUT:			$this->_readSheetLayout();				break;
 806					case self::XLS_Type_SHEETPROTECTION:		$this->_readSheetProtection();			break;
 807					case self::XLS_Type_RANGEPROTECTION:		$this->_readRangeProtection();			break;
 808					case self::XLS_Type_NOTE:					$this->_readNote();						break;
 809					//case self::XLS_Type_IMDATA:				$this->_readImData();					break;
 810					case self::XLS_Type_TXO:					$this->_readTextObject();				break;
 811					case self::XLS_Type_CONTINUE:				$this->_readContinue();					break;
 812					case self::XLS_Type_EOF:					$this->_readDefault();					break 2;
 813					default:									$this->_readDefault();					break;
 814				}
 815
 816			}
 817
 818			// treat MSODRAWING records, sheet-level Escher
 819			if (!$this->_readDataOnly && $this->_drawingData) {
 820				$escherWorksheet = new PHPExcel_Shared_Escher();
 821				$reader = new PHPExcel_Reader_Excel5_Escher($escherWorksheet);
 822				$escherWorksheet = $reader->load($this->_drawingData);
 823
 824				// debug Escher stream
 825				//$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
 826				//$debug->load($this->_drawingData);
 827
 828				// get all spContainers in one long array, so they can be mapped to OBJ records
 829				$allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
 830			}
 831
 832			// treat OBJ records
 833			foreach ($this->_objs as $n => $obj) {
 834//				echo '<hr /><b>Object</b> reference is ',$n,'<br />';
 835//				var_dump($obj);
 836//				echo '<br />';
 837
 838				// the first shape container never has a corresponding OBJ record, hence $n + 1
 839				$spContainer = $allSpContainers[$n + 1];
 840
 841				// we skip all spContainers that are a part of a group shape since we cannot yet handle those
 842				if ($spContainer->getNestingLevel() > 1) {
 843					continue;
 844				}
 845
 846				// calculate the width and height of the shape
 847				list($startColumn, $startRow) = PHPExcel_Cell::coordinateFromString($spContainer->getStartCoordinates());
 848				list($endColumn, $endRow) = PHPExcel_Cell::coordinateFromString($spContainer->getEndCoordinates());
 849
 850				$startOffsetX = $spContainer->getStartOffsetX();
 851				$startOffsetY = $spContainer->getStartOffsetY();
 852				$endOffsetX = $spContainer->getEndOffsetX();
 853				$endOffsetY = $spContainer->getEndOffsetY();
 854
 855				$width = PHPExcel_Shared_Excel5::getDistanceX($this->_phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
 856				$height = PHPExcel_Shared_Excel5::getDistanceY($this->_phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
 857
 858				// calculate offsetX and offsetY of the shape
 859				$offsetX = $startOffsetX * PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, $startColumn) / 1024;
 860				$offsetY = $startOffsetY * PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $startRow) / 256;
 861
 862				switch ($obj['otObjType']) {
 863
 864				case 0x19:
 865					// Note
 866//					echo 'Cell Annotation Object<br />';
 867//					echo 'Object ID is ',$obj['idObjID'],'<br />';
 868//
 869					if (isset($this->_cellNotes[$obj['idObjID']])) {
 870						$cellNote = $this->_cellNotes[$obj['idObjID']];
 871
 872//						echo '_cellNotes[',$obj['idObjID'],']: ';
 873//						var_dump($cellNote);
 874//						echo '<br />';
 875//
 876						if (isset($this->_textObjects[$obj['idObjID']])) {
 877							$textObject = $this->_textObjects[$obj['idObjID']];
 878//							echo '_textObject: ';
 879//							var_dump($textObject);
 880//							echo '<br />';
 881//
 882							$this->_cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
 883							$text = $textObject['text'];
 884						}
 885//						echo $text,'<br />';
 886					}
 887					break;
 888
 889				case 0x08:
 890//					echo 'Picture Object<br />';
 891					// picture
 892
 893					// get index to BSE entry (1-based)
 894					$BSEindex = $spContainer->getOPT(0x0104);
 895					$BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
 896					$BSE = $BSECollection[$BSEindex - 1];
 897					$blipType = $BSE->getBlipType();
 898
 899					// need check because some blip types are not supported by Escher reader such as EMF
 900					if ($blip = $BSE->getBlip()) {
 901						$ih = imagecreatefromstring($blip->getData());
 902						$drawing = new PHPExcel_Worksheet_MemoryDrawing();
 903						$drawing->setImageResource($ih);
 904
 905						// width, height, offsetX, offsetY
 906						$drawing->setResizeProportional(false);
 907						$drawing->setWidth($width);
 908						$drawing->setHeight($height);
 909						$drawing->setOffsetX($offsetX);
 910						$drawing->setOffsetY($offsetY);
 911
 912						switch ($blipType) {
 913							case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG:
 914								$drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG);
 915								$drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_JPEG);
 916								break;
 917
 918							case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG:
 919								$drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG);
 920								$drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_PNG);
 921								break;
 922						}
 923
 924						$drawing->setWorksheet($this->_phpSheet);
 925						$drawing->setCoordinates($spContainer->getStartCoordinates());
 926					}
 927
 928					break;
 929
 930				default:
 931					// other object type
 932					break;
 933
 934				}
 935			}
 936
 937			// treat SHAREDFMLA records
 938			if ($this->_version == self::XLS_BIFF8) {
 939				foreach ($this->_sharedFormulaParts as $cell => $baseCell) {
 940					list($column, $row) = PHPExcel_Cell::coordinateFromString($cell);
 941					if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($column, $row, $this->_phpSheet->getTitle()) ) {
 942						$formula = $this->_getFormulaFromStructure($this->_sharedFormulas[$baseCell], $cell);
 943						$this->_phpSheet->getCell($cell)->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA);
 944					}
 945				}
 946			}
 947
 948			if (count($this->_cellNotes) > 0) {
 949				foreach($this->_cellNotes as $note => $noteDetails) {
 950//					echo '<b>Cell annotation ',$note,'</b><br />';
 951//					var_dump($noteDetails);
 952//					echo '<br />';
 953					$cellAddress = str_replace('$','',$noteDetails['cellRef']);
 954					$this->_phpSheet->getComment( $cellAddress )
 955													->setAuthor( $noteDetails['author'] )
 956													->setText($this->_parseRichText($noteDetails['objTextData']['text']) );
 957				}
 958			}
 959		}
 960
 961		// add the named ranges (defined names)
 962		foreach ($this->_definedname as $definedName) {
 963			if ($definedName['isBuiltInName']) {
 964				switch ($definedName['name']) {
 965
 966				case pack('C', 0x06):
 967					// print area
 968					//	in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
 969
 970					$ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
 971
 972					$extractedRanges = array();
 973					foreach ($ranges as $range) {
 974						// $range should look like one of these
 975						//		Foo!$C$7:$J$66
 976						//		Bar!$A$1:$IV$2
 977
 978						$explodes = explode('!', $range);	// FIXME: what if sheetname contains exclamation mark?
 979						$sheetName = $explodes[0];
 980
 981						if (count($explodes) == 2) {
 982							$extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
 983						}
 984					}
 985					if ($docSheet = $this->_phpExcel->getSheetByName($sheetName)) {
 986						$docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
 987					}
 988					break;
 989
 990				case pack('C', 0x07):
 991					// print titles (repeating rows)
 992					// Assuming BIFF8, there are 3 cases
 993					// 1. repeating rows
 994					//		formula looks like this: Sheet!$A$1:$IV$2
 995					//		rows 1-2 repeat
 996					// 2. repeating columns
 997					//		formula looks like this: Sheet!$A$1:$B$65536
 998					//		columns A-B repeat
 999					// 3. both repeating rows and repeating columns
1000					//		formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
1001
1002					$ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
1003
1004					foreach ($ranges as $range) {
1005						// $range should look like this one of these
1006						//		Sheet!$A$1:$B$65536
1007						//		Sheet!$A$1:$IV$2
1008
1009						$explodes = explode('!', $range);
1010
1011						if (count($explodes) == 2) {
1012							if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) {
1013
1014								$extractedRange = $explodes[1];
1015								$extractedRange = str_replace('$', '', $extractedRange);
1016
1017								$coordinateStrings = explode(':', $extractedRange);
1018								if (count($coordinateStrings) == 2) {
1019									list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[0]);
1020									list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[1]);
1021
1022									if ($firstColumn == 'A' and $lastColumn == 'IV') {
1023										// then we have repeating rows
1024										$docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow, $lastRow));
1025									} elseif ($firstRow == 1 and $lastRow == 65536) {
1026										// then we have repeating columns
1027										$docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn, $lastColumn));
1028									}
1029								}
1030							}
1031						}
1032					}
1033					break;
1034
1035				}
1036			} else {
1037				// Extract range
1038				$explodes = explode('!', $definedName['formula']);
1039
1040				if (count($explodes) == 2) {
1041					if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) {
1042						$extractedRange = $explodes[1];
1043						$extractedRange = str_replace('$', '', $extractedRange);
1044
1045						$localOnly = ($definedName['scope'] == 0) ? false : true;
1046						$scope = ($definedName['scope'] == 0) ?
1047							null : $this->_phpExcel->getSheetByName($this->_sheets[$definedName['scope'] - 1]['name']);
1048
1049						$this->_phpExcel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $docSheet, $extractedRange, $localOnly, $scope) );
1050					}
1051				}
1052			}
1053		}
1054
1055		return $this->_phpExcel;
1056	}
1057
1058	/**
1059	 * Use OLE reader to extract the relevant data streams from the OLE file
1060	 *
1061	 * @param string $pFilename
1062	 */
1063	private function _loadOLE($pFilename)
1064	{
1065		// OLE reader
1066		$ole = new PHPExcel_Shared_OLERead();
1067
1068		// get excel data,
1069		$res = $ole->read($pFilename);
1070		// Get workbook data: workbook stream + sheet streams
1071		$this->_data = $ole->getStream($ole->wrkbook);
1072
1073		// Get summary information data
1074		$this->_summaryInformation = $ole->getStream($ole->summaryInformation);
1075
1076		// Get additional document summary information data
1077		$this->_documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation);
1078
1079		// Get user-defined property data
1080//		$this->_userDefinedProperties = $ole->getUserDefinedProperties();
1081	}
1082
1083	/**
1084	 * Read summary information
1085	 */
1086	private function _readSummaryInformation()
1087	{
1088		if (!isset($this->_summaryInformation)) {
1089			return;
1090		}
1091
1092		// offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
1093		// offset: 2; size: 2;
1094		// offset: 4; size: 2; OS version
1095		// offset: 6; size: 2; OS indicator
1096		// offset: 8; size: 16
1097		// offset: 24; size: 4; section count
1098		$secCount = self::_GetInt4d($this->_summaryInformation, 24);
1099
1100		// 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
1101		// offset: 44; size: 4
1102		$secOffset = self::_GetInt4d($this->_summaryInformation, 44);
1103
1104		// section header
1105		// offset: $secOffset; size: 4; section length
1106		$secLength = self::_GetInt4d($this->_summaryInformation, $secOffset);
1107
1108		// offset: $secOffset+4; size: 4; property count
1109		$countProperties = self::_GetInt4d($this->_summaryInformation, $secOffset+4);
1110
1111		// initialize code page (used to resolve string values)
1112		$codePage = 'CP1252';
1113
1114		// offset: ($secOffset+8); size: var
1115		// loop through property decarations and properties
1116		for ($i = 0; $i < $countProperties; ++$i) {
1117
1118			// offset: ($secOffset+8) + (8 * $i); size: 4; property ID
1119			$id = self::_GetInt4d($this->_summaryInformation, ($secOffset+8) + (8 * $i));
1120
1121			// Use value of property id as appropriate
1122			// offset: ($secOffset+12) + (8 * $i); size: 4; offset from beginning of section (48)
1123			$offset = self::_GetInt4d($this->_summaryInformation, ($secOffset+12) + (8 * $i));
1124
1125			$type = self::_GetInt4d($this->_summaryInformation, $secOffset + $offset);
1126
1127			// initialize property value
1128			$value = null;
1129
1130			// extract property value based on property type
1131			switch ($type) {
1132				case 0x02: // 2 byte signed integer
1133					$value = self::_GetInt2d($this->_summaryInformation, $secOffset + 4 + $offset);
1134					break;
1135
1136				case 0x03: // 4 byte signed integer
1137					$value = self::_GetInt4d($this->_summaryInformation, $secOffset + 4 + $offset);
1138					break;
1139
1140				case 0x13: // 4 byte unsigned integer
1141					// not needed yet, fix later if necessary
1142					break;
1143
1144				case 0x1E: // null-terminated string prepended by dword string length
1145					$byteLength = self::_GetInt4d($this->_summaryInformation, $secOffset + 4 + $offset);
1146					$value = substr($this->_summaryInformation, $secOffset + 8 + $offset, $byteLength);
1147					$value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage);
1148					$value = rtrim($value);
1149					break;
1150
1151				case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
1152					// PHP-time
1153					$value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_summaryInformation, $secOffset + 4 + $offset, 8));
1154					break;
1155
1156				case 0x47: // Clipboard format
1157					// not needed yet, fix later if necessary
1158					break;
1159			}
1160
1161			switch ($id) {
1162				case 0x01:	//	Code Page
1163					$codePage = PHPExcel_Shared_CodePage::NumberToName($value);
1164					break;
1165
1166				case 0x02:	//	Title
1167					$this->_phpExcel->getProperties()->setTitle($value);
1168					break;
1169
1170				case 0x03:	//	Subject
1171					$this->_phpExcel->getProperties()->setSubject($value);
1172					break;
1173
1174				case 0x04:	//	Author (Creator)
1175					$this->_phpExcel->getProperties()->setCreator($value);
1176					break;
1177
1178				case 0x05:	//	Keywords
1179					$this->_phpExcel->getProperties()->setKeywords($value);
1180					break;
1181
1182				case 0x06:	//	Comments (Description)
1183					$this->_phpExcel->getProperties()->setDescription($value);
1184					break;
1185
1186				case 0x07:	//	Template
1187					//	Not supported by PHPExcel
1188					break;
1189
1190				case 0x08:	//	Last Saved By (LastModifiedBy)
1191					$this->_phpExcel->getProperties()->setLastModifiedBy($value);
1192					break;
1193
1194				case 0x09:	//	Revision
1195					//	Not supported by PHPExcel
1196					break;
1197
1198				case 0x0A:	//	Total Editing Time
1199					//	Not supported by PHPExcel
1200					break;
1201
1202				case 0x0B:	//	Last Printed
1203					//	Not supported by PHPExcel
1204					break;
1205
1206				case 0x0C:	//	Created Date/Time
1207					$this->_phpExcel->getProperties()->setCreated($value);
1208					break;
1209
1210				case 0x0D:	//	Modified Date/Time
1211					$this->_phpExcel->getProperties()->setModified($value);
1212					break;
1213
1214				case 0x0E:	//	Number of Pages
1215					//	Not supported by PHPExcel
1216					break;
1217
1218				case 0x0F:	//	Number of Words
1219					//	Not supported by PHPExcel
1220					break;
1221
1222				case 0x10:	//	Number of Characters
1223					//	Not supported by PHPExcel
1224					break;
1225
1226				case 0x11:	//	Thumbnail
1227					//	Not supported by PHPExcel
1228					break;
1229
1230				case 0x12:	//	Name of creating application
1231					//	Not supported by PHPExcel
1232					break;
1233
1234				case 0x13:	//	Security
1235					//	Not supported by PHPExcel
1236					break;
1237
1238			}
1239		}
1240	}
1241
1242	/**
1243	 * Read additional document summary information
1244	 */
1245	private function _readDocumentSummaryInformation()
1246	{
1247		if (!isset($this->_documentSummaryInformation)) {
1248			return;
1249		}
1250
1251		//	offset: 0;	size: 2;	must be 0xFE 0xFF (UTF-16 LE byte order mark)
1252		//	offset: 2;	size: 2;
1253		//	offset: 4;	size: 2;	OS version
1254		//	offset: 6;	size: 2;	OS indicator
1255		//	offset: 8;	size: 16
1256		//	offset: 24;	size: 4;	section count
1257		$secCount = self::_GetInt4d($this->_documentSummaryInformation, 24);
1258//		echo '$secCount = ',$secCount,'<br />';
1259
1260		// 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
1261		// offset: 44;	size: 4;	first section offset
1262		$secOffset = self::_GetInt4d($this->_documentSummaryInformation, 44);
1263//		echo '$secOffset = ',$secOffset,'<br />';
1264
1265		//	section header
1266		//	offset: $secOffset;	size: 4;	section length
1267		$secLength = self::_GetInt4d($this->_documentSummaryInformation, $secOffset);
1268//		echo '$secLength = ',$secLength,'<br />';
1269
1270		//	offset: $secOffset+4;	size: 4;	property count
1271		$countProperties = self::_GetInt4d($this->_documentSummaryInformation, $secOffset+4);
1272//		echo '$countProperties = ',$countProperties,'<br />';
1273
1274		// initialize code page (used to resolve string values)
1275		$codePage = 'CP1252';
1276
1277		//	offset: ($secOffset+8);	size: var
1278		//	loop through property decarations and properties
1279		for ($i = 0; $i < $countProperties; ++$i) {
1280//			echo 'Property ',$i,'<br />';
1281			//	offset: ($secOffset+8) + (8 * $i);	size: 4;	property ID
1282			$id = self::_GetInt4d($this->_documentSummaryInformation, ($secOffset+8) + (8 * $i));
1283//			echo 'ID is ',$id,'<br />';
1284
1285			// Use value of property id as appropriate
1286			// offset: 60 + 8 * $i;	size: 4;	offset from beginning of section (48)
1287			$offset = self::_GetInt4d($this->_documentSummaryInformation, ($secOffset+12) + (8 * $i));
1288
1289			$type = self::_GetInt4d($this->_documentSummaryInformation, $secOffset + $offset);
1290//			echo 'Type is ',$type,', ';
1291
1292			// initialize property value
1293			$value = null;
1294
1295			// extract property value based on property type
1296			switch ($type) {
1297				case 0x02:	//	2 byte signed integer
1298					$value = self::_GetInt2d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
1299					break;
1300
1301				case 0x03:	//	4 byte signed integer
1302					$value = self::_GetInt4d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
1303					break;
1304
1305				case 0x13:	//	4 byte unsigned integer
1306					// not needed yet, fix later if necessary
1307					break;
1308
1309				case 0x1E:	//	null-terminated string prepended by dword string length
1310					$byteLength = self::_GetInt4d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
1311					$value = substr($this->_documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
1312					$value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage);
1313					$value = rtrim($value);
1314					break;
1315
1316				case 0x40:	//	Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
1317					// PHP-Time
1318					$value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_documentSummaryInformation, $secOffset + 4 + $offset, 8));
1319					break;
1320
1321				case 0x47:	//	Clipboard format
1322					// not needed yet, fix later if necessary
1323					break;
1324			}
1325
1326			switch ($id) {
1327				case 0x01:	//	Code Page
1328					$codePage = PHPExcel_Shared_CodePage::NumberToName($value);
1329					break;
1330
1331				case 0x02:	//	Category
1332					$this->_phpExcel->getProperties()->setCategory($value);
1333					break;
1334
1335				case 0x03:	//	Presentation Target
1336					//	Not supported by PHPExcel
1337					break;
1338
1339				case 0x04:	//	Bytes
1340					//	Not supported by PHPExcel
1341					break;
1342
1343				case 0x05:	//	Lines
1344					//	Not supported by PHPExcel
1345					break;
1346
1347				case 0x06:	//	Paragraphs
1348					//	Not supported by PHPExcel
1349					break;
1350
1351				case 0x07:	//	Slides
1352					//	Not supported by PHPExcel
1353					break;
1354
1355				case 0x08:	//	Notes
1356					//	Not supported by PHPExcel
1357					break;
1358
1359				case 0x09:	//	Hidden Slides
1360					//	Not supported by PHPExcel
1361					break;
1362
1363				case 0x0A:	//	MM Clips
1364					//	Not supported by PHPExcel
1365					break;
1366
1367				case 0x0B:	//	Scale Crop
1368					//	Not supported by PHPExcel
1369					break;
1370
1371				case 0x0C:	//	Heading Pairs
1372					//	Not supported by PHPExcel
1373					break;
1374
1375				case 0x0D:	//	Titles of Parts
1376					//	Not supported by PHPExcel
1377					break;
1378
1379				case 0x0E:	//	Manager
1380					$this->_phpExcel->getProperties()->setManager($value);
1381					break;
1382
1383				case 0x0F:	//	Company
1384					$this->_phpExcel->getProperties()->setCompany($value);
1385					break;
1386
1387				case 0x10:	//	Links up-to-date
1388					//	Not supported by PHPExcel
1389					break;
1390
1391			}
1392		}
1393	}
1394
1395	/**
1396	 * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
1397	 */
1398	private function _readDefault()
1399	{
1400		$length = self::_GetInt2d($this->_data, $this->_pos + 2);
1401//		$recordData = substr($this->_data, $this->_pos + 4, $length);
1402
1403		// move stream pointer to next record
1404		$this->_pos += 4 + $length;
1405	}
1406
1407
1408	/**
1409	 *	The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
1410	 *		this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
1411	 */
1412	private function _readNote()
1413	{
1414//		echo '<b>Read Cell Annotation</b><br />';
1415		$length = self::_GetInt2d($this->_data, $this->_pos + 2);
1416		$recordData = substr($this->_data, $this->_pos + 4, $length);
1417
1418		// move stream pointer to next record
1419		$this->_pos += 4 + $length;
1420
1421		if ($this->_readDataOnly) {
1422			return;
1423		}
1424
1425		$cellAddress = $this->_readBIFF8CellAddress(substr($recordData, 0, 4));
1426		if ($this->_version == self::XLS_BIFF8) {
1427			$noteObjID = self::_GetInt2d($recordData, 6);
1428			$noteAuthor = self::_readUnicodeStringLong(substr($recordData, 8));
1429			$noteAuthor = $noteAuthor['value'];
1430//			echo 'Note Address=',$cellAddress,'<br />';
1431//			echo 'Note Object ID=',$noteObjID,'<br />';
1432//			echo 'Note Author=',$noteAuthor,'<hr />';
1433//
1434			$this->_cellNotes[$noteObjID] = array('cellRef'		=> $cellAddress,
1435												  'objectID'	=> $noteObjID,
1436												  'author'		=> $noteAuthor
1437												 );
1438		} else {
1439			$extension = false;
1440			if ($cellAddress == '$B$65536') {
1441				//	If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
1442				//		note from the previous cell annotation. We're not yet handling this, so annotations longer than the
1443				//		max 2048 bytes will probably throw a wobbly.
1444				$row = self::_GetInt2d($recordData, 0);
1445				$extension = true;
1446				$cellAddress = array_pop(array_keys($this->_phpSheet->getComments()));
1447			}
1448//			echo 'Note Address=',$cellAddress,'<br />';
1449
1450			$cellAddress = str_replace('$','',$cellAddress);
1451			$noteLength = self::_GetInt2d($recordData, 4);
1452			$noteText = trim(substr($recordData, 6));
1453//			echo 'Note Length=',$noteLength,'<br />';
1454//			echo 'Note Text=',$noteText,'<br />';
1455
1456			if ($extension) {
1457				//	Concatenate this extension with the currently set comment for the cell
1458				$comment = $this->_phpSheet->getComment( $cellAddress );
1459				$commentText = $comment->getText()->getPlainText();
1460				$comment->setText($this->_parseRichText($commentText.$noteText) );
1461			} else {
1462				//	Set comment for the cell
1463				$this->_phpSheet->getComment( $cellAddress )
1464//													->setAuthor( $author )
1465													->setText($this->_parseRichText($noteText) );
1466			}
1467		}
1468
1469	}
1470
1471	/**
1472	 *	The TEXT Object record contains the text associated with a cell annotation.
1473	 */
1474	private function _readTextObject()
1475	{
1476		$length = self::_GetInt2d($this->_data, $this->_pos + 2);
1477		$recordData = substr($this->_data, $this->_pos + 4, $length);
1478
1479		// move stream pointer to next record
1480		$this->_pos += 4 + $length;
1481
1482		if ($this->_readDataOnly) {
1483			return;
1484		}
1485
1486		// recordData consists of an array of subrecords looking like this:
1487		//	grbit: 2 bytes; Option Flags
1488		//	rot: 2 bytes; rotation
1489		//	cchText: 2 bytes; length of the text (in the first continue record)
1490		//	cbRuns: 2 bytes; length of the formatting (in the second continue record)
1491		// followed by the continuation records containing the actual text and formatting
1492		$grbitOpts	= self::_GetInt2d($recordData, 0);
1493		$rot		= self::_GetInt2d($recordData, 2);
1494		$cchText	= self::_GetInt2d($recordData, 10);
1495		$cbRuns		= self::_GetInt2d($recordData, 12);
1496		$text		= $this->_getSplicedRecordData();
1497
1498		$this->_textObjects[$this->textObjRef] = array(
1499				'text'		=> substr($text["recordData"],$text["spliceOffsets"][0]+1,$cchText),
1500				'format'	=> substr($text["recordData"],$text["spliceOffsets"][1],$cbRuns),
1501				'alignment'	=> $grbitOpts,
1502				'rotation'	=> $rot
1503			 );
1504
1505//		echo '<b>_readTextObject()</b><br />';
1506//		var_dump($this->_textObjects[$this->textObjRef]);
1507//		echo '<br />';
1508	}
1509
1510	/**
1511	 * Read BOF
1512	 */
1513	private function _readBof()
1514	{
1515		$length = self::_GetInt2d($this->_data, $this->_pos + 2);
1516		$recordData = substr($this->_data, $this->_pos + 4, $length);
1517
1518		// move stream pointer to next record
1519		$this->_pos += 4 + $length;
1520
1521		// offset: 2; size: 2; type of the following data
1522		$substreamType = self::_GetInt2d($recordData, 2);
1523
1524		switch ($substreamType) {
1525			case self::XLS_WorkbookGlobals:
1526				$version = self::_GetInt2d($recordData, 0);
1527				if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
1528					throw new Exception('Cannot read this Excel file. Version is too old.');
1529				}
1530				$this->_version = $version;
1531				break;
1532
1533			case self::XLS_Worksheet:
1534				// do not use this version information for anything
1535				// it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
1536				break;
1537
1538			default:
1539				// substream, e.g. chart
1540				// just skip the entire substream
1541				do {
1542					$code = self::_GetInt2d($this->_data, $this->_pos);
1543					$this->_readDefault();
1544				} while ($code != self::XLS_Type_EOF && $this->_pos < $this->_dataSize);
1545				break;
1546		}
1547	}
1548
1549	/**
1550	 * FILEPASS
1551	 *
1552	 * This record is part of the File Protection Block. It
1553	 * contains information about the read/write password of the
1554	 * file. All record contents following this record will be
1555	 * encrypted.
1556	 *
1557	 * --	"OpenOffice.org's Documentation of the Microsoft
1558	 * 		Excel File Format"
1559	 */
1560	private function _readFilepass()
1561	{
1562		$length = self::_GetInt2d($this->_data, $this->_pos + 2);
1563//		$recordData = substr($this->_data, $this->_pos + 4, $length);
1564
1565		// move stream pointer to next record
1566		$this->_pos += 4 + $length;
1567
1568		throw new Exception('Cannot read encrypted file');
1569	}
1570
1571	/**
1572	 * CODEPAGE
1573	 *
1574	 * This record stores the text encoding used to write byte
1575	 * strings, stored as MS Windows code page identifier.
1576	 *
1577	 * --	"OpenOffice.org's Documentation of the Microsoft
1578	 * 		Excel File Format"
1579	 */
1580	private function _readCodepage()
1581	{
1582		$length = self::_GetInt2d($this->_data, $this->_pos + 2);
1583		$recordData = substr($this->_data, $this->_pos + 4, $length);
1584
1585		// move stream pointer to next record
1586		$this->_pos += 4 + $length;
1587
1588		// offset: 0; size: 2; code page identifier
1589		$codepage = self::_GetInt2d($recordData, 0);
1590
1591		$this->_codepage = PHPExcel_Shared_CodePage::NumberToName($codepage);
1592	}
1593
1594	/**
1595	 * DATEMODE
1596	 *

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