PageRenderTime 63ms CodeModel.GetById 11ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 0ms

/PHPExcel/Writer/HTML.php

https://bitbucket.org/nfredricks/wp-employee-time
PHP | 1377 lines | 715 code | 197 blank | 465 comment | 150 complexity | 87ea1ed3c57bbed850b7d9e455ec9fde MD5 | raw 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_Writer
  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	1.7.8, 2012-10-12
  26 */
  27
  28
  29/**
  30 * PHPExcel_Writer_HTML
  31 *
  32 * @category   PHPExcel
  33 * @package	PHPExcel_Writer
  34 * @copyright  Copyright (c) 2006 - 2012 PHPExcel (http://www.codeplex.com/PHPExcel)
  35 */
  36class PHPExcel_Writer_HTML implements PHPExcel_Writer_IWriter {
  37	/**
  38	 * PHPExcel object
  39	 *
  40	 * @var PHPExcel
  41	 */
  42	protected $_phpExcel;
  43
  44	/**
  45	 * Sheet index to write
  46	 *
  47	 * @var int
  48	 */
  49	private $_sheetIndex	= 0;
  50
  51	/**
  52	 * Pre-calculate formulas
  53	 *
  54	 * @var boolean
  55	 */
  56	private $_preCalculateFormulas = true;
  57
  58	/**
  59	 * Images root
  60	 *
  61	 * @var string
  62	 */
  63	private $_imagesRoot	= '.';
  64
  65	/**
  66	 * Use inline CSS?
  67	 *
  68	 * @var boolean
  69	 */
  70	private $_useInlineCss = false;
  71
  72	/**
  73	 * Array of CSS styles
  74	 *
  75	 * @var array
  76	 */
  77	private $_cssStyles = null;
  78
  79	/**
  80	 * Array of column widths in points
  81	 *
  82	 * @var array
  83	 */
  84	private $_columnWidths = null;
  85
  86	/**
  87	 * Default font
  88	 *
  89	 * @var PHPExcel_Style_Font
  90	 */
  91	private $_defaultFont;
  92
  93	/**
  94	 * Flag whether spans have been calculated
  95	 *
  96	 * @var boolean
  97	 */
  98	private $_spansAreCalculated	= false;
  99
 100	/**
 101	 * Excel cells that should not be written as HTML cells
 102	 *
 103	 * @var array
 104	 */
 105	private $_isSpannedCell	= array();
 106
 107	/**
 108	 * Excel cells that are upper-left corner in a cell merge
 109	 *
 110	 * @var array
 111	 */
 112	private $_isBaseCell	= array();
 113
 114	/**
 115	 * Excel rows that should not be written as HTML rows
 116	 *
 117	 * @var array
 118	 */
 119	private $_isSpannedRow	= array();
 120
 121	/**
 122	 * Is the current writer creating PDF?
 123	 *
 124	 * @var boolean
 125	 */
 126	protected $_isPdf = false;
 127
 128	/**
 129	 * Generate the Navigation block
 130	 *
 131	 * @var boolean
 132	 */
 133	private $_generateSheetNavigationBlock = true;
 134
 135	/**
 136	 * Create a new PHPExcel_Writer_HTML
 137	 *
 138	 * @param	PHPExcel	$phpExcel	PHPExcel object
 139	 */
 140	public function __construct(PHPExcel $phpExcel) {
 141		$this->_phpExcel = $phpExcel;
 142		$this->_defaultFont = $this->_phpExcel->getDefaultStyle()->getFont();
 143	}
 144
 145	/**
 146	 * Save PHPExcel to file
 147	 *
 148	 * @param	string		$pFilename
 149	 * @throws	Exception
 150	 */
 151	public function save($pFilename = null) {
 152		// garbage collect
 153		$this->_phpExcel->garbageCollect();
 154
 155		$saveDebugLog = PHPExcel_Calculation::getInstance()->writeDebugLog;
 156		PHPExcel_Calculation::getInstance()->writeDebugLog = false;
 157		$saveArrayReturnType = PHPExcel_Calculation::getArrayReturnType();
 158		PHPExcel_Calculation::setArrayReturnType(PHPExcel_Calculation::RETURN_ARRAY_AS_VALUE);
 159
 160		// Build CSS
 161		$this->buildCSS(!$this->_useInlineCss);
 162
 163		// Open file
 164		$fileHandle = fopen($pFilename, 'wb+');
 165		if ($fileHandle === false) {
 166			throw new Exception("Could not open file $pFilename for writing.");
 167		}
 168
 169		// Write headers
 170		fwrite($fileHandle, $this->generateHTMLHeader(!$this->_useInlineCss));
 171
 172		// Write navigation (tabs)
 173		if ((!$this->_isPdf) && ($this->_generateSheetNavigationBlock)) {
 174			fwrite($fileHandle, $this->generateNavigation());
 175		}
 176
 177		// Write data
 178		fwrite($fileHandle, $this->generateSheetData());
 179
 180		// Write footer
 181		fwrite($fileHandle, $this->generateHTMLFooter());
 182
 183		// Close file
 184		fclose($fileHandle);
 185
 186		PHPExcel_Calculation::setArrayReturnType($saveArrayReturnType);
 187		PHPExcel_Calculation::getInstance()->writeDebugLog = $saveDebugLog;
 188	}
 189
 190	/**
 191	 * Map VAlign
 192	 *
 193	 * @param	string		$vAlign		Vertical alignment
 194	 * @return string
 195	 */
 196	private function _mapVAlign($vAlign) {
 197		switch ($vAlign) {
 198			case PHPExcel_Style_Alignment::VERTICAL_BOTTOM:		return 'bottom';
 199			case PHPExcel_Style_Alignment::VERTICAL_TOP:		return 'top';
 200			case PHPExcel_Style_Alignment::VERTICAL_CENTER:
 201			case PHPExcel_Style_Alignment::VERTICAL_JUSTIFY:	return 'middle';
 202			default: return 'baseline';
 203		}
 204	}
 205
 206	/**
 207	 * Map HAlign
 208	 *
 209	 * @param	string		$hAlign		Horizontal alignment
 210	 * @return string|false
 211	 */
 212	private function _mapHAlign($hAlign) {
 213		switch ($hAlign) {
 214			case PHPExcel_Style_Alignment::HORIZONTAL_GENERAL:				return false;
 215			case PHPExcel_Style_Alignment::HORIZONTAL_LEFT:					return 'left';
 216			case PHPExcel_Style_Alignment::HORIZONTAL_RIGHT:				return 'right';
 217			case PHPExcel_Style_Alignment::HORIZONTAL_CENTER:
 218			case PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS:	return 'center';
 219			case PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY:				return 'justify';
 220			default: return false;
 221		}
 222	}
 223
 224	/**
 225	 * Map border style
 226	 *
 227	 * @param	int		$borderStyle		Sheet index
 228	 * @return	string
 229	 */
 230	private function _mapBorderStyle($borderStyle) {
 231		switch ($borderStyle) {
 232			case PHPExcel_Style_Border::BORDER_NONE:				return 'none';
 233			case PHPExcel_Style_Border::BORDER_DASHDOT:				return '1px dashed';
 234			case PHPExcel_Style_Border::BORDER_DASHDOTDOT:			return '1px dotted';
 235			case PHPExcel_Style_Border::BORDER_DASHED:				return '1px dashed';
 236			case PHPExcel_Style_Border::BORDER_DOTTED:				return '1px dotted';
 237			case PHPExcel_Style_Border::BORDER_DOUBLE:				return '3px double';
 238			case PHPExcel_Style_Border::BORDER_HAIR:				return '1px solid';
 239			case PHPExcel_Style_Border::BORDER_MEDIUM:				return '2px solid';
 240			case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT:		return '2px dashed';
 241			case PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT:	return '2px dotted';
 242			case PHPExcel_Style_Border::BORDER_MEDIUMDASHED:		return '2px dashed';
 243			case PHPExcel_Style_Border::BORDER_SLANTDASHDOT:		return '2px dashed';
 244			case PHPExcel_Style_Border::BORDER_THICK:				return '3px solid';
 245			case PHPExcel_Style_Border::BORDER_THIN:				return '1px solid';
 246			default: return '1px solid'; // map others to thin
 247		}
 248	}
 249
 250	/**
 251	 * Get sheet index
 252	 *
 253	 * @return int
 254	 */
 255	public function getSheetIndex() {
 256		return $this->_sheetIndex;
 257	}
 258
 259	/**
 260	 * Set sheet index
 261	 *
 262	 * @param	int		$pValue		Sheet index
 263	 * @return PHPExcel_Writer_HTML
 264	 */
 265	public function setSheetIndex($pValue = 0) {
 266		$this->_sheetIndex = $pValue;
 267		return $this;
 268	}
 269
 270	/**
 271	 * Get sheet index
 272	 *
 273	 * @return boolean
 274	 */
 275	public function getGenerateSheetNavigationBlock() {
 276		return $this->_generateSheetNavigationBlock;
 277	}
 278
 279	/**
 280	 * Set sheet index
 281	 *
 282	 * @param	boolean		$pValue		Flag indicating whether the sheet navigation block should be generated or not
 283	 * @return PHPExcel_Writer_HTML
 284	 */
 285	public function setGenerateSheetNavigationBlock($pValue = true) {
 286		$this->_generateSheetNavigationBlock = (bool) $pValue;
 287		return $this;
 288	}
 289
 290	/**
 291	 * Write all sheets (resets sheetIndex to NULL)
 292	 */
 293	public function writeAllSheets() {
 294		$this->_sheetIndex = null;
 295		return $this;
 296	}
 297
 298	/**
 299	 * Generate HTML header
 300	 *
 301	 * @param	boolean		$pIncludeStyles		Include styles?
 302	 * @return	string
 303	 * @throws Exception
 304	 */
 305	public function generateHTMLHeader($pIncludeStyles = false) {
 306		// PHPExcel object known?
 307		if (is_null($this->_phpExcel)) {
 308			throw new Exception('Internal PHPExcel object not set to an instance of an object.');
 309		}
 310
 311		// Construct HTML
 312		$properties = $this->_phpExcel->getProperties();
 313		$html = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' . PHP_EOL;
 314		$html .= '<!-- Generated by PHPExcel - http://www.phpexcel.net -->' . PHP_EOL;
 315		$html .= '<html>' . PHP_EOL;
 316		$html .= '  <head>' . PHP_EOL;
 317		$html .= '	  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . PHP_EOL;
 318		if ($properties->getTitle() > '')
 319			$html .= '	  <title>' . htmlspecialchars($properties->getTitle()) . '</title>' . PHP_EOL;
 320
 321		if ($properties->getCreator() > '')
 322			$html .= '	  <meta name="author" content="' . htmlspecialchars($properties->getCreator()) . '" />' . PHP_EOL;
 323		if ($properties->getTitle() > '')
 324			$html .= '	  <meta name="title" content="' . htmlspecialchars($properties->getTitle()) . '" />' . PHP_EOL;
 325		if ($properties->getDescription() > '')
 326			$html .= '	  <meta name="description" content="' . htmlspecialchars($properties->getDescription()) . '" />' . PHP_EOL;
 327		if ($properties->getSubject() > '')
 328			$html .= '	  <meta name="subject" content="' . htmlspecialchars($properties->getSubject()) . '" />' . PHP_EOL;
 329		if ($properties->getKeywords() > '')
 330			$html .= '	  <meta name="keywords" content="' . htmlspecialchars($properties->getKeywords()) . '" />' . PHP_EOL;
 331		if ($properties->getCategory() > '')
 332			$html .= '	  <meta name="category" content="' . htmlspecialchars($properties->getCategory()) . '" />' . PHP_EOL;
 333		if ($properties->getCompany() > '')
 334			$html .= '	  <meta name="company" content="' . htmlspecialchars($properties->getCompany()) . '" />' . PHP_EOL;
 335		if ($properties->getManager() > '')
 336			$html .= '	  <meta name="manager" content="' . htmlspecialchars($properties->getManager()) . '" />' . PHP_EOL;
 337
 338		if ($pIncludeStyles) {
 339			$html .= $this->generateStyles(true);
 340		}
 341
 342		$html .= '  </head>' . PHP_EOL;
 343		$html .= '' . PHP_EOL;
 344		$html .= '  <body>' . PHP_EOL;
 345
 346		// Return
 347		return $html;
 348	}
 349
 350	/**
 351	 * Generate sheet data
 352	 *
 353	 * @return	string
 354	 * @throws Exception
 355	 */
 356	public function generateSheetData() {
 357		// PHPExcel object known?
 358		if (is_null($this->_phpExcel)) {
 359			throw new Exception('Internal PHPExcel object not set to an instance of an object.');
 360		}
 361
 362		// Ensure that Spans have been calculated?
 363		if (!$this->_spansAreCalculated) {
 364			$this->_calculateSpans();
 365		}
 366
 367		// Fetch sheets
 368		$sheets = array();
 369		if (is_null($this->_sheetIndex)) {
 370			$sheets = $this->_phpExcel->getAllSheets();
 371		} else {
 372			$sheets[] = $this->_phpExcel->getSheet($this->_sheetIndex);
 373		}
 374
 375		// Construct HTML
 376		$html = '';
 377
 378		// Loop all sheets
 379		$sheetId = 0;
 380		foreach ($sheets as $sheet) {
 381			// Write table header
 382			$html .= $this->_generateTableHeader($sheet);
 383
 384			// Get worksheet dimension
 385			$dimension = explode(':', $sheet->calculateWorksheetDimension());
 386			$dimension[0] = PHPExcel_Cell::coordinateFromString($dimension[0]);
 387			$dimension[0][0] = PHPExcel_Cell::columnIndexFromString($dimension[0][0]) - 1;
 388			$dimension[1] = PHPExcel_Cell::coordinateFromString($dimension[1]);
 389			$dimension[1][0] = PHPExcel_Cell::columnIndexFromString($dimension[1][0]) - 1;
 390
 391			// row min,max
 392			$rowMin = $dimension[0][1];
 393			$rowMax = $dimension[1][1];
 394
 395			// calculate start of <tbody>, <thead>
 396			$tbodyStart = $rowMin;
 397			$tbodyEnd   = $rowMax;
 398			$theadStart = $theadEnd   = 0; // default: no <thead>	no </thead>
 399			if ($sheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
 400				$rowsToRepeatAtTop = $sheet->getPageSetup()->getRowsToRepeatAtTop();
 401
 402				// we can only support repeating rows that start at top row
 403				if ($rowsToRepeatAtTop[0] == 1) {
 404					$theadStart = $rowsToRepeatAtTop[0];
 405					$theadEnd   = $rowsToRepeatAtTop[1];
 406					$tbodyStart = $rowsToRepeatAtTop[1] + 1;
 407				}
 408			}
 409
 410			// Loop through cells
 411			$row = $rowMin-1;
 412			while($row++ < $rowMax) {
 413				// <thead> ?
 414				if ($row == $theadStart) {
 415					$html .= '		<thead>' . PHP_EOL;
 416				}
 417
 418				// <tbody> ?
 419				if ($row == $tbodyStart) {
 420					$html .= '		<tbody>' . PHP_EOL;
 421				}
 422
 423				// Write row if there are HTML table cells in it
 424				if ( !isset($this->_isSpannedRow[$sheet->getParent()->getIndex($sheet)][$row]) ) {
 425					// Start a new rowData
 426					$rowData = array();
 427					// Loop through columns
 428					$column = $dimension[0][0] - 1;
 429					while($column++ < $dimension[1][0]) {
 430						// Cell exists?
 431						if ($sheet->cellExistsByColumnAndRow($column, $row)) {
 432							$rowData[$column] = $sheet->getCellByColumnAndRow($column, $row);
 433						} else {
 434							$rowData[$column] = '';
 435						}
 436					}
 437					$html .= $this->_generateRow($sheet, $rowData, $row - 1);
 438				}
 439
 440				// </thead> ?
 441				if ($row == $theadEnd) {
 442					$html .= '		</thead>' . PHP_EOL;
 443				}
 444
 445				// </tbody> ?
 446				if ($row == $tbodyEnd) {
 447					$html .= '		</tbody>' . PHP_EOL;
 448				}
 449			}
 450
 451			// Write table footer
 452			$html .= $this->_generateTableFooter();
 453
 454			// Writing PDF?
 455			if ($this->_isPdf) {
 456				if (is_null($this->_sheetIndex) && $sheetId + 1 < $this->_phpExcel->getSheetCount()) {
 457					$html .= '<div style="page-break-before:always" />';
 458				}
 459			}
 460
 461			// Next sheet
 462			++$sheetId;
 463		}
 464
 465		// Return
 466		return $html;
 467	}
 468
 469	/**
 470	 * Generate sheet tabs
 471	 *
 472	 * @return	string
 473	 * @throws Exception
 474	 */
 475	public function generateNavigation()
 476	{
 477		// PHPExcel object known?
 478		if (is_null($this->_phpExcel)) {
 479			throw new Exception('Internal PHPExcel object not set to an instance of an object.');
 480		}
 481
 482		// Fetch sheets
 483		$sheets = array();
 484		if (is_null($this->_sheetIndex)) {
 485			$sheets = $this->_phpExcel->getAllSheets();
 486		} else {
 487			$sheets[] = $this->_phpExcel->getSheet($this->_sheetIndex);
 488		}
 489
 490		// Construct HTML
 491		$html = '';
 492
 493		// Only if there are more than 1 sheets
 494		if (count($sheets) > 1) {
 495			// Loop all sheets
 496			$sheetId = 0;
 497
 498			$html .= '<ul class="navigation">' . PHP_EOL;
 499
 500			foreach ($sheets as $sheet) {
 501				$html .= '  <li class="sheet' . $sheetId . '"><a href="#sheet' . $sheetId . '">' . $sheet->getTitle() . '</a></li>' . PHP_EOL;
 502				++$sheetId;
 503			}
 504
 505			$html .= '</ul>' . PHP_EOL;
 506		}
 507
 508		return $html;
 509	}
 510
 511	/**
 512	 * Generate image tag in cell
 513	 *
 514	 * @param	PHPExcel_Worksheet	$pSheet			PHPExcel_Worksheet
 515	 * @param	string				$coordinates	Cell coordinates
 516	 * @return	string
 517	 * @throws	Exception
 518	 */
 519	private function _writeImageTagInCell(PHPExcel_Worksheet $pSheet, $coordinates) {
 520		// Construct HTML
 521		$html = '';
 522
 523		// Write images
 524		foreach ($pSheet->getDrawingCollection() as $drawing) {
 525			if ($drawing instanceof PHPExcel_Worksheet_Drawing) {
 526				if ($drawing->getCoordinates() == $coordinates) {
 527					$filename = $drawing->getPath();
 528
 529					// Strip off eventual '.'
 530					if (substr($filename, 0, 1) == '.') {
 531						$filename = substr($filename, 1);
 532					}
 533
 534					// Prepend images root
 535					$filename = $this->getImagesRoot() . $filename;
 536
 537					// Strip off eventual '.'
 538					if (substr($filename, 0, 1) == '.' && substr($filename, 0, 2) != './') {
 539						$filename = substr($filename, 1);
 540					}
 541
 542					// Convert UTF8 data to PCDATA
 543					$filename = htmlspecialchars($filename);
 544
 545					$html .= PHP_EOL;
 546					$html .= '		<img style="position: relative; left: ' . $drawing->getOffsetX() . 'px; top: ' . $drawing->getOffsetY() . 'px; width: ' . $drawing->getWidth() . 'px; height: ' . $drawing->getHeight() . 'px;" src="' . $filename . '" border="0" width="' . $drawing->getWidth() . '" height="' . $drawing->getHeight() . '" />' . PHP_EOL;
 547				}
 548			}
 549		}
 550
 551		// Return
 552		return $html;
 553	}
 554
 555	/**
 556	 * Generate CSS styles
 557	 *
 558	 * @param	boolean	$generateSurroundingHTML	Generate surrounding HTML tags? (<style> and </style>)
 559	 * @return	string
 560	 * @throws	Exception
 561	 */
 562	public function generateStyles($generateSurroundingHTML = true) {
 563		// PHPExcel object known?
 564		if (is_null($this->_phpExcel)) {
 565			throw new Exception('Internal PHPExcel object not set to an instance of an object.');
 566		}
 567
 568		// Build CSS
 569		$css = $this->buildCSS($generateSurroundingHTML);
 570
 571		// Construct HTML
 572		$html = '';
 573
 574		// Start styles
 575		if ($generateSurroundingHTML) {
 576			$html .= '	<style type="text/css">' . PHP_EOL;
 577			$html .= '	  html { ' . $this->_assembleCSS($css['html']) . ' }' . PHP_EOL;
 578		}
 579
 580		// Write all other styles
 581		foreach ($css as $styleName => $styleDefinition) {
 582			if ($styleName != 'html') {
 583				$html .= '	  ' . $styleName . ' { ' . $this->_assembleCSS($styleDefinition) . ' }' . PHP_EOL;
 584			}
 585		}
 586
 587		// End styles
 588		if ($generateSurroundingHTML) {
 589			$html .= '	</style>' . PHP_EOL;
 590		}
 591
 592		// Return
 593		return $html;
 594	}
 595
 596	/**
 597	 * Build CSS styles
 598	 *
 599	 * @param	boolean	$generateSurroundingHTML	Generate surrounding HTML style? (html { })
 600	 * @return	array
 601	 * @throws	Exception
 602	 */
 603	public function buildCSS($generateSurroundingHTML = true) {
 604		// PHPExcel object known?
 605		if (is_null($this->_phpExcel)) {
 606			throw new Exception('Internal PHPExcel object not set to an instance of an object.');
 607		}
 608
 609		// Cached?
 610		if (!is_null($this->_cssStyles)) {
 611			return $this->_cssStyles;
 612		}
 613
 614		// Ensure that spans have been calculated
 615		if (!$this->_spansAreCalculated) {
 616			$this->_calculateSpans();
 617		}
 618
 619		// Construct CSS
 620		$css = array();
 621
 622		// Start styles
 623		if ($generateSurroundingHTML) {
 624			// html { }
 625			$css['html']['font-family']	  = 'Calibri, Arial, Helvetica, sans-serif';
 626			$css['html']['font-size']		= '11pt';
 627			$css['html']['background-color'] = 'white';
 628		}
 629
 630
 631		// table { }
 632		$css['table']['border-collapse']  = 'collapse';
 633	    if (!$this->_isPdf) {
 634			$css['table']['page-break-after'] = 'always';
 635		}
 636
 637		// .gridlines td { }
 638		$css['.gridlines td']['border'] = '1px dotted black';
 639
 640		// .b {}
 641		$css['.b']['text-align'] = 'center'; // BOOL
 642
 643		// .e {}
 644		$css['.e']['text-align'] = 'center'; // ERROR
 645
 646		// .f {}
 647		$css['.f']['text-align'] = 'right'; // FORMULA
 648
 649		// .inlineStr {}
 650		$css['.inlineStr']['text-align'] = 'left'; // INLINE
 651
 652		// .n {}
 653		$css['.n']['text-align'] = 'right'; // NUMERIC
 654
 655		// .s {}
 656		$css['.s']['text-align'] = 'left'; // STRING
 657
 658		// Calculate cell style hashes
 659		foreach ($this->_phpExcel->getCellXfCollection() as $index => $style) {
 660			$css['td.style' . $index] = $this->_createCSSStyle( $style );
 661		}
 662
 663		// Fetch sheets
 664		$sheets = array();
 665		if (is_null($this->_sheetIndex)) {
 666			$sheets = $this->_phpExcel->getAllSheets();
 667		} else {
 668			$sheets[] = $this->_phpExcel->getSheet($this->_sheetIndex);
 669		}
 670
 671		// Build styles per sheet
 672		foreach ($sheets as $sheet) {
 673			// Calculate hash code
 674			$sheetIndex = $sheet->getParent()->getIndex($sheet);
 675
 676			// Build styles
 677			// Calculate column widths
 678			$sheet->calculateColumnWidths();
 679
 680			// col elements, initialize
 681			$highestColumnIndex = PHPExcel_Cell::columnIndexFromString($sheet->getHighestColumn()) - 1;
 682			$column = -1;
 683			while($column++ < $highestColumnIndex) {
 684				$this->_columnWidths[$sheetIndex][$column] = 42; // approximation
 685				$css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = '42pt';
 686			}
 687
 688			// col elements, loop through columnDimensions and set width
 689			foreach ($sheet->getColumnDimensions() as $columnDimension) {
 690				if (($width = PHPExcel_Shared_Drawing::cellDimensionToPixels($columnDimension->getWidth(), $this->_defaultFont)) >= 0) {
 691					$width = PHPExcel_Shared_Drawing::pixelsToPoints($width);
 692					$column = PHPExcel_Cell::columnIndexFromString($columnDimension->getColumnIndex()) - 1;
 693					$this->_columnWidths[$sheetIndex][$column] = $width;
 694					$css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = $width . 'pt';
 695
 696					if ($columnDimension->getVisible() === false) {
 697						$css['table.sheet' . $sheetIndex . ' col.col' . $column]['visibility'] = 'collapse';
 698						$css['table.sheet' . $sheetIndex . ' col.col' . $column]['*display'] = 'none'; // target IE6+7
 699					}
 700				}
 701			}
 702
 703			// Default row height
 704			$rowDimension = $sheet->getDefaultRowDimension();
 705
 706			// table.sheetN tr { }
 707			$css['table.sheet' . $sheetIndex . ' tr'] = array();
 708
 709			if ($rowDimension->getRowHeight() == -1) {
 710				$pt_height = PHPExcel_Shared_Font::getDefaultRowHeightByFont($this->_phpExcel->getDefaultStyle()->getFont());
 711			} else {
 712				$pt_height = $rowDimension->getRowHeight();
 713			}
 714			$css['table.sheet' . $sheetIndex . ' tr']['height'] = $pt_height . 'pt';
 715			if ($rowDimension->getVisible() === false) {
 716				$css['table.sheet' . $sheetIndex . ' tr']['display']	= 'none';
 717				$css['table.sheet' . $sheetIndex . ' tr']['visibility'] = 'hidden';
 718			}
 719
 720			// Calculate row heights
 721			foreach ($sheet->getRowDimensions() as $rowDimension) {
 722				$row = $rowDimension->getRowIndex() - 1;
 723
 724				// table.sheetN tr.rowYYYYYY { }
 725				$css['table.sheet' . $sheetIndex . ' tr.row' . $row] = array();
 726
 727				if ($rowDimension->getRowHeight() == -1) {
 728					$pt_height = PHPExcel_Shared_Font::getDefaultRowHeightByFont($this->_phpExcel->getDefaultStyle()->getFont());
 729				} else {
 730					$pt_height = $rowDimension->getRowHeight();
 731				}
 732				$css['table.sheet' . $sheetIndex . ' tr.row' . $row]['height'] = $pt_height . 'pt';
 733				if ($rowDimension->getVisible() === false) {
 734					$css['table.sheet' . $sheetIndex . ' tr.row' . $row]['display'] = 'none';
 735					$css['table.sheet' . $sheetIndex . ' tr.row' . $row]['visibility'] = 'hidden';
 736				}
 737			}
 738		}
 739
 740		// Cache
 741		if (is_null($this->_cssStyles)) {
 742			$this->_cssStyles = $css;
 743		}
 744
 745		// Return
 746		return $css;
 747	}
 748
 749	/**
 750	 * Create CSS style
 751	 *
 752	 * @param	PHPExcel_Style		$pStyle			PHPExcel_Style
 753	 * @return	array
 754	 */
 755	private function _createCSSStyle(PHPExcel_Style $pStyle) {
 756		// Construct CSS
 757		$css = '';
 758
 759		// Create CSS
 760		$css = array_merge(
 761			$this->_createCSSStyleAlignment($pStyle->getAlignment())
 762			, $this->_createCSSStyleBorders($pStyle->getBorders())
 763			, $this->_createCSSStyleFont($pStyle->getFont())
 764			, $this->_createCSSStyleFill($pStyle->getFill())
 765		);
 766
 767		// Return
 768		return $css;
 769	}
 770
 771	/**
 772	 * Create CSS style (PHPExcel_Style_Alignment)
 773	 *
 774	 * @param	PHPExcel_Style_Alignment		$pStyle			PHPExcel_Style_Alignment
 775	 * @return	array
 776	 */
 777	private function _createCSSStyleAlignment(PHPExcel_Style_Alignment $pStyle) {
 778		// Construct CSS
 779		$css = array();
 780
 781		// Create CSS
 782		$css['vertical-align'] = $this->_mapVAlign($pStyle->getVertical());
 783		if ($textAlign = $this->_mapHAlign($pStyle->getHorizontal())) {
 784			$css['text-align'] = $textAlign;
 785			if(in_array($textAlign,array('left','right')))
 786				$css['padding-'.$textAlign] = (string)((int)$pStyle->getIndent() * 9).'px';
 787		}
 788
 789		// Return
 790		return $css;
 791	}
 792
 793	/**
 794	 * Create CSS style (PHPExcel_Style_Font)
 795	 *
 796	 * @param	PHPExcel_Style_Font		$pStyle			PHPExcel_Style_Font
 797	 * @return	array
 798	 */
 799	private function _createCSSStyleFont(PHPExcel_Style_Font $pStyle) {
 800		// Construct CSS
 801		$css = array();
 802
 803		// Create CSS
 804		if ($pStyle->getBold()) {
 805			$css['font-weight'] = 'bold';
 806		}
 807		if ($pStyle->getUnderline() != PHPExcel_Style_Font::UNDERLINE_NONE && $pStyle->getStrikethrough()) {
 808			$css['text-decoration'] = 'underline line-through';
 809		} else if ($pStyle->getUnderline() != PHPExcel_Style_Font::UNDERLINE_NONE) {
 810			$css['text-decoration'] = 'underline';
 811		} else if ($pStyle->getStrikethrough()) {
 812			$css['text-decoration'] = 'line-through';
 813		}
 814		if ($pStyle->getItalic()) {
 815			$css['font-style'] = 'italic';
 816		}
 817
 818		$css['color']		= '#' . $pStyle->getColor()->getRGB();
 819		$css['font-family']	= '\'' . $pStyle->getName() . '\'';
 820		$css['font-size']	= $pStyle->getSize() . 'pt';
 821
 822		// Return
 823		return $css;
 824	}
 825
 826	/**
 827	 * Create CSS style (PHPExcel_Style_Borders)
 828	 *
 829	 * @param	PHPExcel_Style_Borders		$pStyle			PHPExcel_Style_Borders
 830	 * @return	array
 831	 */
 832	private function _createCSSStyleBorders(PHPExcel_Style_Borders $pStyle) {
 833		// Construct CSS
 834		$css = array();
 835
 836		// Create CSS
 837		$css['border-bottom']	= $this->_createCSSStyleBorder($pStyle->getBottom());
 838		$css['border-top']		= $this->_createCSSStyleBorder($pStyle->getTop());
 839		$css['border-left']		= $this->_createCSSStyleBorder($pStyle->getLeft());
 840		$css['border-right']	= $this->_createCSSStyleBorder($pStyle->getRight());
 841
 842		// Return
 843		return $css;
 844	}
 845
 846	/**
 847	 * Create CSS style (PHPExcel_Style_Border)
 848	 *
 849	 * @param	PHPExcel_Style_Border		$pStyle			PHPExcel_Style_Border
 850	 * @return	string
 851	 */
 852	private function _createCSSStyleBorder(PHPExcel_Style_Border $pStyle) {
 853		// Create CSS
 854		$css = $this->_mapBorderStyle($pStyle->getBorderStyle()) . ' #' . $pStyle->getColor()->getRGB();
 855
 856		// Return
 857		return $css;
 858	}
 859
 860	/**
 861	 * Create CSS style (PHPExcel_Style_Fill)
 862	 *
 863	 * @param	PHPExcel_Style_Fill		$pStyle			PHPExcel_Style_Fill
 864	 * @return	array
 865	 */
 866	private function _createCSSStyleFill(PHPExcel_Style_Fill $pStyle) {
 867		// Construct HTML
 868		$css = array();
 869
 870		// Create CSS
 871		$value = $pStyle->getFillType() == PHPExcel_Style_Fill::FILL_NONE ?
 872			'white' : '#' . $pStyle->getStartColor()->getRGB();
 873		$css['background-color'] = $value;
 874
 875		// Return
 876		return $css;
 877	}
 878
 879	/**
 880	 * Generate HTML footer
 881	 */
 882	public function generateHTMLFooter() {
 883		// Construct HTML
 884		$html = '';
 885		$html .= '  </body>' . PHP_EOL;
 886		$html .= '</html>' . PHP_EOL;
 887
 888		// Return
 889		return $html;
 890	}
 891
 892	/**
 893	 * Generate table header
 894	 *
 895	 * @param	PHPExcel_Worksheet	$pSheet		The worksheet for the table we are writing
 896	 * @return	string
 897	 * @throws	Exception
 898	 */
 899	private function _generateTableHeader($pSheet) {
 900		$sheetIndex = $pSheet->getParent()->getIndex($pSheet);
 901
 902		// Construct HTML
 903		$html = '';
 904
 905		if (!$this->_useInlineCss) {
 906			$gridlines = $pSheet->getShowGridLines() ? ' gridlines' : '';
 907			$html .= '	<table border="0" cellpadding="0" cellspacing="0" id="sheet' . $sheetIndex . '" class="sheet' . $sheetIndex . $gridlines . '">' . PHP_EOL;
 908		} else {
 909			$style = isset($this->_cssStyles['table']) ?
 910				$this->_assembleCSS($this->_cssStyles['table']) : '';
 911
 912			if ($this->_isPdf && $pSheet->getShowGridLines()) {
 913				$html .= '	<table border="1" cellpadding="1" id="sheet' . $sheetIndex . '" cellspacing="4" style="' . $style . '">' . PHP_EOL;
 914			} else {
 915				$html .= '	<table border="0" cellpadding="1" id="sheet' . $sheetIndex . '" cellspacing="4" style="' . $style . '">' . PHP_EOL;
 916			}
 917		}
 918
 919		// Write <col> elements
 920		$highestColumnIndex = PHPExcel_Cell::columnIndexFromString($pSheet->getHighestColumn()) - 1;
 921		$i = -1;
 922		while($i++ < $highestColumnIndex) {
 923		    if (!$this->_isPdf) {
 924				if (!$this->_useInlineCss) {
 925					$html .= '		<col class="col' . $i . '">' . PHP_EOL;
 926				} else {
 927					$style = isset($this->_cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i]) ?
 928						$this->_assembleCSS($this->_cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i]) : '';
 929					$html .= '		<col style="' . $style . '">' . PHP_EOL;
 930				}
 931			}
 932		}
 933
 934		// Return
 935		return $html;
 936	}
 937
 938	/**
 939	 * Generate table footer
 940	 *
 941	 * @throws	Exception
 942	 */
 943	private function _generateTableFooter() {
 944		// Construct HTML
 945		$html = '';
 946		$html .= '	</table>' . PHP_EOL;
 947
 948		// Return
 949		return $html;
 950	}
 951
 952	/**
 953	 * Generate row
 954	 *
 955	 * @param	PHPExcel_Worksheet	$pSheet			PHPExcel_Worksheet
 956	 * @param	array				$pValues		Array containing cells in a row
 957	 * @param	int					$pRow			Row number (0-based)
 958	 * @return	string
 959	 * @throws	Exception
 960	 */
 961	private function _generateRow(PHPExcel_Worksheet $pSheet, $pValues = null, $pRow = 0) {
 962		if (is_array($pValues)) {
 963			// Construct HTML
 964			$html = '';
 965
 966			// Sheet index
 967			$sheetIndex = $pSheet->getParent()->getIndex($pSheet);
 968
 969			// DomPDF and breaks
 970			if ($this->_isPdf && count($pSheet->getBreaks()) > 0) {
 971				$breaks = $pSheet->getBreaks();
 972
 973				// check if a break is needed before this row
 974				if (isset($breaks['A' . $pRow])) {
 975					// close table: </table>
 976					$html .= $this->_generateTableFooter();
 977
 978					// insert page break
 979					$html .= '<div style="page-break-before:always" />';
 980
 981					// open table again: <table> + <col> etc.
 982					$html .= $this->_generateTableHeader($pSheet);
 983				}
 984			}
 985
 986			// Write row start
 987			if (!$this->_useInlineCss) {
 988				$html .= '		  <tr class="row' . $pRow . '">' . PHP_EOL;
 989			} else {
 990				$style = isset($this->_cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow])
 991					? $this->_assembleCSS($this->_cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]) : '';
 992
 993				$html .= '		  <tr style="' . $style . '">' . PHP_EOL;
 994			}
 995
 996			// Write cells
 997			$colNum = 0;
 998			foreach ($pValues as $cell) {
 999				$coordinate = PHPExcel_Cell::stringFromColumnIndex($colNum) . ($pRow + 1);
1000
1001				if (!$this->_useInlineCss) {
1002					$cssClass = '';
1003					$cssClass = 'column' . $colNum;
1004				} else {
1005					$cssClass = array();
1006					if (isset($this->_cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum])) {
1007						$this->_cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum];
1008					}
1009				}
1010				$colSpan = 1;
1011				$rowSpan = 1;
1012
1013				// initialize
1014				$cellData = '';
1015
1016				// PHPExcel_Cell
1017				if ($cell instanceof PHPExcel_Cell) {
1018					if (is_null($cell->getParent())) {
1019						$cell->attach($pSheet);
1020					}
1021					// Value
1022					if ($cell->getValue() instanceof PHPExcel_RichText) {
1023						// Loop through rich text elements
1024						$elements = $cell->getValue()->getRichTextElements();
1025						foreach ($elements as $element) {
1026							// Rich text start?
1027							if ($element instanceof PHPExcel_RichText_Run) {
1028								$cellData .= '<span style="' . $this->_assembleCSS($this->_createCSSStyleFont($element->getFont())) . '">';
1029
1030								if ($element->getFont()->getSuperScript()) {
1031									$cellData .= '<sup>';
1032								} else if ($element->getFont()->getSubScript()) {
1033									$cellData .= '<sub>';
1034								}
1035							}
1036
1037							// Convert UTF8 data to PCDATA
1038							$cellText = $element->getText();
1039							$cellData .= htmlspecialchars($cellText);
1040
1041							if ($element instanceof PHPExcel_RichText_Run) {
1042								if ($element->getFont()->getSuperScript()) {
1043									$cellData .= '</sup>';
1044								} else if ($element->getFont()->getSubScript()) {
1045									$cellData .= '</sub>';
1046								}
1047
1048								$cellData .= '</span>';
1049							}
1050						}
1051					} else {
1052						if ($this->_preCalculateFormulas) {
1053							$cellData = PHPExcel_Style_NumberFormat::toFormattedString(
1054								$cell->getCalculatedValue(),
1055								$pSheet->getParent()->getCellXfByIndex( $cell->getXfIndex() )->getNumberFormat()->getFormatCode(),
1056								array($this, 'formatColor')
1057							);
1058						} else {
1059							$cellData = PHPExcel_Style_NumberFormat::ToFormattedString(
1060								$cell->getValue(),
1061								$pSheet->getParent()->getCellXfByIndex( $cell->getXfIndex() )->getNumberFormat()->getFormatCode(),
1062								array($this, 'formatColor')
1063							);
1064						}
1065						$cellData = htmlspecialchars($cellData);
1066						if ($pSheet->getParent()->getCellXfByIndex( $cell->getXfIndex() )->getFont()->getSuperScript()) {
1067							$cellData = '<sup>'.$cellData.'</sup>';
1068						} elseif ($pSheet->getParent()->getCellXfByIndex( $cell->getXfIndex() )->getFont()->getSubScript()) {
1069							$cellData = '<sub>'.$cellData.'</sub>';
1070						}
1071					}
1072
1073					// Converts the cell content so that spaces occuring at beginning of each new line are replaced by &nbsp;
1074					// Example: "  Hello\n to the world" is converted to "&nbsp;&nbsp;Hello\n&nbsp;to the world"
1075					$cellData = preg_replace("/(?m)(?:^|\\G) /", '&nbsp;', $cellData);
1076
1077					// convert newline "\n" to '<br>'
1078					$cellData = nl2br($cellData);
1079
1080					// Extend CSS class?
1081					if (!$this->_useInlineCss) {
1082						$cssClass .= ' style' . $cell->getXfIndex();
1083						$cssClass .= ' ' . $cell->getDataType();
1084					} else {
1085						if (isset($this->_cssStyles['td.style' . $cell->getXfIndex()])) {
1086							$cssClass = array_merge($cssClass, $this->_cssStyles['td.style' . $cell->getXfIndex()]);
1087						}
1088
1089						// General horizontal alignment: Actual horizontal alignment depends on dataType
1090						$sharedStyle = $pSheet->getParent()->getCellXfByIndex( $cell->getXfIndex() );
1091						if ($sharedStyle->getAlignment()->getHorizontal() == PHPExcel_Style_Alignment::HORIZONTAL_GENERAL
1092							&& isset($this->_cssStyles['.' . $cell->getDataType()]['text-align']))
1093						{
1094							$cssClass['text-align'] = $this->_cssStyles['.' . $cell->getDataType()]['text-align'];
1095						}
1096					}
1097				}
1098
1099				// Hyperlink?
1100				if ($pSheet->hyperlinkExists($coordinate) && !$pSheet->getHyperlink($coordinate)->isInternal()) {
1101					$cellData = '<a href="' . htmlspecialchars($pSheet->getHyperlink($coordinate)->getUrl()) . '" title="' . htmlspecialchars($pSheet->getHyperlink($coordinate)->getTooltip()) . '">' . $cellData . '</a>';
1102				}
1103
1104				// Should the cell be written or is it swallowed by a rowspan or colspan?
1105				$writeCell = ! ( isset($this->_isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])
1106							&& $this->_isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum] );
1107
1108				// Colspan and Rowspan
1109				$colspan = 1;
1110				$rowspan = 1;
1111				if (isset($this->_isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])) {
1112					$spans = $this->_isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum];
1113					$rowSpan = $spans['rowspan'];
1114					$colSpan = $spans['colspan'];
1115				}
1116
1117				// Write
1118				if ($writeCell) {
1119					// Column start
1120					$html .= '			<td';
1121						if (!$this->_useInlineCss) {
1122							$html .= ' class="' . $cssClass . '"';
1123						} else {
1124							//** Necessary redundant code for the sake of PHPExcel_Writer_PDF **
1125							// We must explicitly write the width of the <td> element because TCPDF
1126							// does not recognize e.g. <col style="width:42pt">
1127							$width = 0;
1128							$i = $colNum - 1;
1129							$e = $colNum + $colSpan - 1;
1130							while($i++ < $e) {
1131								if (isset($this->_columnWidths[$sheetIndex][$i])) {
1132									$width += $this->_columnWidths[$sheetIndex][$i];
1133								}
1134							}
1135							$cssClass['width'] = $width . 'pt';
1136
1137							// We must also explicitly write the height of the <td> element because TCPDF
1138							// does not recognize e.g. <tr style="height:50pt">
1139							if (isset($this->_cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'])) {
1140								$height = $this->_cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'];
1141								$cssClass['height'] = $height;
1142							}
1143							//** end of redundant code **
1144
1145							$html .= ' style="' . $this->_assembleCSS($cssClass) . '"';
1146						}
1147						if ($colSpan > 1) {
1148							$html .= ' colspan="' . $colSpan . '"';
1149						}
1150						if ($rowSpan > 1) {
1151							$html .= ' rowspan="' . $rowSpan . '"';
1152						}
1153					$html .= '>';
1154
1155					// Image?
1156					$html .= $this->_writeImageTagInCell($pSheet, $coordinate);
1157
1158					// Cell data
1159					$html .= $cellData;
1160
1161					// Column end
1162					$html .= '</td>' . PHP_EOL;
1163				}
1164
1165				// Next column
1166				++$colNum;
1167			}
1168
1169			// Write row end
1170			$html .= '		  </tr>' . PHP_EOL;
1171
1172			// Return
1173			return $html;
1174		} else {
1175			throw new Exception("Invalid parameters passed.");
1176		}
1177	}
1178
1179	/**
1180	 * Takes array where of CSS properties / values and converts to CSS string
1181	 *
1182	 * @param array
1183	 * @return string
1184	 */
1185	private function _assembleCSS($pValue = array())
1186	{
1187		$pairs = array();
1188		foreach ($pValue as $property => $value) {
1189			$pairs[] = $property . ':' . $value;
1190		}
1191		$string = implode('; ', $pairs);
1192
1193		return $string;
1194	}
1195
1196	/**
1197	 * Get Pre-Calculate Formulas
1198	 *
1199	 * @return boolean
1200	 */
1201	public function getPreCalculateFormulas() {
1202		return $this->_preCalculateFormulas;
1203	}
1204
1205	/**
1206	 * Set Pre-Calculate Formulas
1207	 *
1208	 * @param boolean $pValue	Pre-Calculate Formulas?
1209	 * @return PHPExcel_Writer_HTML
1210	 */
1211	public function setPreCalculateFormulas($pValue = true) {
1212		$this->_preCalculateFormulas = $pValue;
1213		return $this;
1214	}
1215
1216	/**
1217	 * Get images root
1218	 *
1219	 * @return string
1220	 */
1221	public function getImagesRoot() {
1222		return $this->_imagesRoot;
1223	}
1224
1225	/**
1226	 * Set images root
1227	 *
1228	 * @param string $pValue
1229	 * @return PHPExcel_Writer_HTML
1230	 */
1231	public function setImagesRoot($pValue = '.') {
1232		$this->_imagesRoot = $pValue;
1233		return $this;
1234	}
1235
1236	/**
1237	 * Get use inline CSS?
1238	 *
1239	 * @return boolean
1240	 */
1241	public function getUseInlineCss() {
1242		return $this->_useInlineCss;
1243	}
1244
1245	/**
1246	 * Set use inline CSS?
1247	 *
1248	 * @param boolean $pValue
1249	 * @return PHPExcel_Writer_HTML
1250	 */
1251	public function setUseInlineCss($pValue = false) {
1252		$this->_useInlineCss = $pValue;
1253		return $this;
1254	}
1255
1256	/**
1257	 * Add color to formatted string as inline style
1258	 *
1259	 * @param string $pValue Plain formatted value without color
1260	 * @param string $pFormat Format code
1261	 * @return string
1262	 */
1263	public function formatColor($pValue, $pFormat)
1264	{
1265		// Color information, e.g. [Red] is always at the beginning
1266		$color = null; // initialize
1267		$matches = array();
1268
1269		$color_regex = '/^\\[[a-zA-Z]+\\]/';
1270		if (preg_match($color_regex, $pFormat, $matches)) {
1271			$color = str_replace('[', '', $matches[0]);
1272			$color = str_replace(']', '', $color);
1273			$color = strtolower($color);
1274		}
1275
1276		// convert to PCDATA
1277		$value = htmlspecialchars($pValue);
1278
1279		// color span tag
1280		if ($color !== null) {
1281			$value = '<span style="color:' . $color . '">' . $value . '</span>';
1282		}
1283
1284		return $value;
1285	}
1286
1287	/**
1288	 * Calculate information about HTML colspan and rowspan which is not always the same as Excel's
1289	 */
1290	private function _calculateSpans()
1291	{
1292		// Identify all cells that should be omitted in HTML due to cell merge.
1293		// In HTML only the upper-left cell should be written and it should have
1294		//   appropriate rowspan / colspan attribute
1295		$sheetIndexes = $this->_sheetIndex !== null ?
1296			array($this->_sheetIndex) : range(0, $this->_phpExcel->getSheetCount() - 1);
1297
1298		foreach ($sheetIndexes as $sheetIndex) {
1299			$sheet = $this->_phpExcel->getSheet($sheetIndex);
1300
1301			$candidateSpannedRow  = array();
1302
1303			// loop through all Excel merged cells
1304			foreach ($sheet->getMergeCells() as $cells) {
1305				list($cells, ) = PHPExcel_Cell::splitRange($cells);
1306				$first = $cells[0];
1307				$last  = $cells[1];
1308
1309				list($fc, $fr) = PHPExcel_Cell::coordinateFromString($first);
1310				$fc = PHPExcel_Cell::columnIndexFromString($fc) - 1;
1311
1312				list($lc, $lr) = PHPExcel_Cell::coordinateFromString($last);
1313				$lc = PHPExcel_Cell::columnIndexFromString($lc) - 1;
1314
1315				// loop through the individual cells in the individual merge
1316				$r = $fr - 1;
1317				while($r++ < $lr) {
1318					// also, flag this row as a HTML row that is candidate to be omitted
1319					$candidateSpannedRow[$r] = $r;
1320
1321					$c = $fc - 1;
1322					while($c++ < $lc) {
1323						if ( !($c == $fc && $r == $fr) ) {
1324							// not the upper-left cell (should not be written in HTML)
1325							$this->_isSpannedCell[$sheetIndex][$r][$c] = array(
1326								'baseCell' => array($fr, $fc),
1327							);
1328						} else {
1329							// upper-left is the base cell that should hold the colspan/rowspan attribute
1330							$this->_isBaseCell[$sheetIndex][$r][$c] = array(
1331								'xlrowspan' => $lr - $fr + 1, // Excel rowspan
1332								'rowspan'   => $lr - $fr + 1, // HTML rowspan, value may change
1333								'xlcolspan' => $lc - $fc + 1, // Excel colspan
1334								'colspan'   => $lc - $fc + 1, // HTML colspan, value may change
1335							);
1336						}
1337					}
1338				}
1339			}
1340
1341			// Identify which rows should be omitted in HTML. These are the rows where all the cells
1342			//   participate in a merge and the where base cells are somewhere above.
1343			$countColumns = PHPExcel_Cell::columnIndexFromString($sheet->getHighestColumn());
1344			foreach ($candidateSpannedRow as $rowIndex) {
1345				if (isset($this->_isSpannedCell[$sheetIndex][$rowIndex])) {
1346					if (count($this->_isSpannedCell[$sheetIndex][$rowIndex]) == $countColumns) {
1347						$this->_isSpannedRow[$sheetIndex][$rowIndex] = $rowIndex;
1348					};
1349				}
1350			}
1351
1352			// For each of the omitted rows we found above, the affected rowspans should be subtracted by 1
1353			if ( isset($this->_isSpannedRow[$sheetIndex]) ) {
1354				foreach ($this->_isSpannedRow[$sheetIndex] as $rowIndex) {
1355					$adjustedBaseCells = array();
1356					$c = -1;
1357					$e = $countColumns - 1;
1358					while($c++ < $e) {
1359						$baseCell = $this->_isSpannedCell[$sheetIndex][$rowIndex][$c]['baseCell'];
1360
1361						if ( !in_array($baseCell, $adjustedBaseCells) ) {
1362							// subtract rowspan by 1
1363							--$this->_isBaseCell[$sheetIndex][ $baseCell[0] ][ $baseCell[1] ]['rowspan'];
1364							$adjustedBaseCells[] = $baseCell;
1365						}
1366					}
1367				}
1368			}
1369
1370			// TODO: Same for columns
1371		}
1372
1373		// We have calculated the spans
1374		$this->_spansAreCalculated = true;
1375	}
1376
1377}