For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1612 lines
58 KiB

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