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.

801 lines
38 KiB

  1. <?php
  2. /** PHPExcel root directory */
  3. if (!defined('PHPEXCEL_ROOT')) {
  4. /**
  5. * @ignore
  6. */
  7. define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../');
  8. require(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
  9. }
  10. /**
  11. * PHPExcel_Reader_Excel2003XML
  12. *
  13. * Copyright (c) 2006 - 2015 PHPExcel
  14. *
  15. * This library is free software; you can redistribute it and/or
  16. * modify it under the terms of the GNU Lesser General Public
  17. * License as published by the Free Software Foundation; either
  18. * version 2.1 of the License, or (at your option) any later version.
  19. *
  20. * This library is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  23. * Lesser General Public License for more details.
  24. *
  25. * You should have received a copy of the GNU Lesser General Public
  26. * License along with this library; if not, write to the Free Software
  27. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  28. *
  29. * @category PHPExcel
  30. * @package PHPExcel_Reader
  31. * @copyright Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
  32. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  33. * @version ##VERSION##, ##DATE##
  34. */
  35. class PHPExcel_Reader_Excel2003XML extends PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader
  36. {
  37. /**
  38. * Formats
  39. *
  40. * @var array
  41. */
  42. protected $styles = array();
  43. /**
  44. * Character set used in the file
  45. *
  46. * @var string
  47. */
  48. protected $charSet = 'UTF-8';
  49. /**
  50. * Create a new PHPExcel_Reader_Excel2003XML
  51. */
  52. public function __construct()
  53. {
  54. $this->readFilter = new PHPExcel_Reader_DefaultReadFilter();
  55. }
  56. /**
  57. * Can the current PHPExcel_Reader_IReader read the file?
  58. *
  59. * @param string $pFilename
  60. * @return boolean
  61. * @throws PHPExcel_Reader_Exception
  62. */
  63. public function canRead($pFilename)
  64. {
  65. // Office xmlns:o="urn:schemas-microsoft-com:office:office"
  66. // Excel xmlns:x="urn:schemas-microsoft-com:office:excel"
  67. // XML Spreadsheet xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
  68. // Spreadsheet component xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet"
  69. // XML schema xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
  70. // XML data type xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
  71. // MS-persist recordset xmlns:rs="urn:schemas-microsoft-com:rowset"
  72. // Rowset xmlns:z="#RowsetSchema"
  73. //
  74. $signature = array(
  75. '<?xml version="1.0"',
  76. '<?mso-application progid="Excel.Sheet"?>'
  77. );
  78. // Open file
  79. $this->openFile($pFilename);
  80. $fileHandle = $this->fileHandle;
  81. // Read sample data (first 2 KB will do)
  82. $data = fread($fileHandle, 2048);
  83. fclose($fileHandle);
  84. $valid = true;
  85. foreach ($signature as $match) {
  86. // every part of the signature must be present
  87. if (strpos($data, $match) === false) {
  88. $valid = false;
  89. break;
  90. }
  91. }
  92. // Retrieve charset encoding
  93. if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/um', $data, $matches)) {
  94. $this->charSet = strtoupper($matches[1]);
  95. }
  96. // echo 'Character Set is ', $this->charSet,'<br />';
  97. return $valid;
  98. }
  99. /**
  100. * Reads names of the worksheets from a file, without parsing the whole file to a PHPExcel object
  101. *
  102. * @param string $pFilename
  103. * @throws PHPExcel_Reader_Exception
  104. */
  105. public function listWorksheetNames($pFilename)
  106. {
  107. // Check if file exists
  108. if (!file_exists($pFilename)) {
  109. throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist.");
  110. }
  111. if (!$this->canRead($pFilename)) {
  112. throw new PHPExcel_Reader_Exception($pFilename . " is an Invalid Spreadsheet file.");
  113. }
  114. $worksheetNames = array();
  115. $xml = simplexml_load_string($this->securityScan(file_get_contents($pFilename)), 'SimpleXMLElement', PHPExcel_Settings::getLibXmlLoaderOptions());
  116. $namespaces = $xml->getNamespaces(true);
  117. $xml_ss = $xml->children($namespaces['ss']);
  118. foreach ($xml_ss->Worksheet as $worksheet) {
  119. $worksheet_ss = $worksheet->attributes($namespaces['ss']);
  120. $worksheetNames[] = self::convertStringEncoding((string) $worksheet_ss['Name'], $this->charSet);
  121. }
  122. return $worksheetNames;
  123. }
  124. /**
  125. * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns)
  126. *
  127. * @param string $pFilename
  128. * @throws PHPExcel_Reader_Exception
  129. */
  130. public function listWorksheetInfo($pFilename)
  131. {
  132. // Check if file exists
  133. if (!file_exists($pFilename)) {
  134. throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist.");
  135. }
  136. $worksheetInfo = array();
  137. $xml = simplexml_load_string($this->securityScan(file_get_contents($pFilename)), 'SimpleXMLElement', PHPExcel_Settings::getLibXmlLoaderOptions());
  138. $namespaces = $xml->getNamespaces(true);
  139. $worksheetID = 1;
  140. $xml_ss = $xml->children($namespaces['ss']);
  141. foreach ($xml_ss->Worksheet as $worksheet) {
  142. $worksheet_ss = $worksheet->attributes($namespaces['ss']);
  143. $tmpInfo = array();
  144. $tmpInfo['worksheetName'] = '';
  145. $tmpInfo['lastColumnLetter'] = 'A';
  146. $tmpInfo['lastColumnIndex'] = 0;
  147. $tmpInfo['totalRows'] = 0;
  148. $tmpInfo['totalColumns'] = 0;
  149. if (isset($worksheet_ss['Name'])) {
  150. $tmpInfo['worksheetName'] = (string) $worksheet_ss['Name'];
  151. } else {
  152. $tmpInfo['worksheetName'] = "Worksheet_{$worksheetID}";
  153. }
  154. if (isset($worksheet->Table->Row)) {
  155. $rowIndex = 0;
  156. foreach ($worksheet->Table->Row as $rowData) {
  157. $columnIndex = 0;
  158. $rowHasData = false;
  159. foreach ($rowData->Cell as $cell) {
  160. if (isset($cell->Data)) {
  161. $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
  162. $rowHasData = true;
  163. }
  164. ++$columnIndex;
  165. }
  166. ++$rowIndex;
  167. if ($rowHasData) {
  168. $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
  169. }
  170. }
  171. }
  172. $tmpInfo['lastColumnLetter'] = PHPExcel_Cell::stringFromColumnIndex($tmpInfo['lastColumnIndex']);
  173. $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
  174. $worksheetInfo[] = $tmpInfo;
  175. ++$worksheetID;
  176. }
  177. return $worksheetInfo;
  178. }
  179. /**
  180. * Loads PHPExcel from file
  181. *
  182. * @param string $pFilename
  183. * @return PHPExcel
  184. * @throws PHPExcel_Reader_Exception
  185. */
  186. public function load($pFilename)
  187. {
  188. // Create new PHPExcel
  189. $objPHPExcel = new PHPExcel();
  190. $objPHPExcel->removeSheetByIndex(0);
  191. // Load into this instance
  192. return $this->loadIntoExisting($pFilename, $objPHPExcel);
  193. }
  194. protected static function identifyFixedStyleValue($styleList, &$styleAttributeValue)
  195. {
  196. $styleAttributeValue = strtolower($styleAttributeValue);
  197. foreach ($styleList as $style) {
  198. if ($styleAttributeValue == strtolower($style)) {
  199. $styleAttributeValue = $style;
  200. return true;
  201. }
  202. }
  203. return false;
  204. }
  205. /**
  206. * pixel units to excel width units(units of 1/256th of a character width)
  207. * @param pxs
  208. * @return
  209. */
  210. protected static function pixel2WidthUnits($pxs)
  211. {
  212. $UNIT_OFFSET_MAP = array(0, 36, 73, 109, 146, 182, 219);
  213. $widthUnits = 256 * ($pxs / 7);
  214. $widthUnits += $UNIT_OFFSET_MAP[($pxs % 7)];
  215. return $widthUnits;
  216. }
  217. /**
  218. * excel width units(units of 1/256th of a character width) to pixel units
  219. * @param widthUnits
  220. * @return
  221. */
  222. protected static function widthUnits2Pixel($widthUnits)
  223. {
  224. $pixels = ($widthUnits / 256) * 7;
  225. $offsetWidthUnits = $widthUnits % 256;
  226. $pixels += round($offsetWidthUnits / (256 / 7));
  227. return $pixels;
  228. }
  229. protected static function hex2str($hex)
  230. {
  231. return chr(hexdec($hex[1]));
  232. }
  233. /**
  234. * Loads PHPExcel from file into PHPExcel instance
  235. *
  236. * @param string $pFilename
  237. * @param PHPExcel $objPHPExcel
  238. * @return PHPExcel
  239. * @throws PHPExcel_Reader_Exception
  240. */
  241. public function loadIntoExisting($pFilename, PHPExcel $objPHPExcel)
  242. {
  243. $fromFormats = array('\-', '\ ');
  244. $toFormats = array('-', ' ');
  245. $underlineStyles = array (
  246. PHPExcel_Style_Font::UNDERLINE_NONE,
  247. PHPExcel_Style_Font::UNDERLINE_DOUBLE,
  248. PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING,
  249. PHPExcel_Style_Font::UNDERLINE_SINGLE,
  250. PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING
  251. );
  252. $verticalAlignmentStyles = array (
  253. PHPExcel_Style_Alignment::VERTICAL_BOTTOM,
  254. PHPExcel_Style_Alignment::VERTICAL_TOP,
  255. PHPExcel_Style_Alignment::VERTICAL_CENTER,
  256. PHPExcel_Style_Alignment::VERTICAL_JUSTIFY
  257. );
  258. $horizontalAlignmentStyles = array (
  259. PHPExcel_Style_Alignment::HORIZONTAL_GENERAL,
  260. PHPExcel_Style_Alignment::HORIZONTAL_LEFT,
  261. PHPExcel_Style_Alignment::HORIZONTAL_RIGHT,
  262. PHPExcel_Style_Alignment::HORIZONTAL_CENTER,
  263. PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS,
  264. PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY
  265. );
  266. $timezoneObj = new DateTimeZone('Europe/London');
  267. $GMT = new DateTimeZone('UTC');
  268. // Check if file exists
  269. if (!file_exists($pFilename)) {
  270. throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist.");
  271. }
  272. if (!$this->canRead($pFilename)) {
  273. throw new PHPExcel_Reader_Exception($pFilename . " is an Invalid Spreadsheet file.");
  274. }
  275. $xml = simplexml_load_string($this->securityScan(file_get_contents($pFilename)), 'SimpleXMLElement', PHPExcel_Settings::getLibXmlLoaderOptions());
  276. $namespaces = $xml->getNamespaces(true);
  277. $docProps = $objPHPExcel->getProperties();
  278. if (isset($xml->DocumentProperties[0])) {
  279. foreach ($xml->DocumentProperties[0] as $propertyName => $propertyValue) {
  280. switch ($propertyName) {
  281. case 'Title':
  282. $docProps->setTitle(self::convertStringEncoding($propertyValue, $this->charSet));
  283. break;
  284. case 'Subject':
  285. $docProps->setSubject(self::convertStringEncoding($propertyValue, $this->charSet));
  286. break;
  287. case 'Author':
  288. $docProps->setCreator(self::convertStringEncoding($propertyValue, $this->charSet));
  289. break;
  290. case 'Created':
  291. $creationDate = strtotime($propertyValue);
  292. $docProps->setCreated($creationDate);
  293. break;
  294. case 'LastAuthor':
  295. $docProps->setLastModifiedBy(self::convertStringEncoding($propertyValue, $this->charSet));
  296. break;
  297. case 'LastSaved':
  298. $lastSaveDate = strtotime($propertyValue);
  299. $docProps->setModified($lastSaveDate);
  300. break;
  301. case 'Company':
  302. $docProps->setCompany(self::convertStringEncoding($propertyValue, $this->charSet));
  303. break;
  304. case 'Category':
  305. $docProps->setCategory(self::convertStringEncoding($propertyValue, $this->charSet));
  306. break;
  307. case 'Manager':
  308. $docProps->setManager(self::convertStringEncoding($propertyValue, $this->charSet));
  309. break;
  310. case 'Keywords':
  311. $docProps->setKeywords(self::convertStringEncoding($propertyValue, $this->charSet));
  312. break;
  313. case 'Description':
  314. $docProps->setDescription(self::convertStringEncoding($propertyValue, $this->charSet));
  315. break;
  316. }
  317. }
  318. }
  319. if (isset($xml->CustomDocumentProperties)) {
  320. foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) {
  321. $propertyAttributes = $propertyValue->attributes($namespaces['dt']);
  322. $propertyName = preg_replace_callback('/_x([0-9a-z]{4})_/', 'PHPExcel_Reader_Excel2003XML::hex2str', $propertyName);
  323. $propertyType = PHPExcel_DocumentProperties::PROPERTY_TYPE_UNKNOWN;
  324. switch ((string) $propertyAttributes) {
  325. case 'string':
  326. $propertyType = PHPExcel_DocumentProperties::PROPERTY_TYPE_STRING;
  327. $propertyValue = trim($propertyValue);
  328. break;
  329. case 'boolean':
  330. $propertyType = PHPExcel_DocumentProperties::PROPERTY_TYPE_BOOLEAN;
  331. $propertyValue = (bool) $propertyValue;
  332. break;
  333. case 'integer':
  334. $propertyType = PHPExcel_DocumentProperties::PROPERTY_TYPE_INTEGER;
  335. $propertyValue = intval($propertyValue);
  336. break;
  337. case 'float':
  338. $propertyType = PHPExcel_DocumentProperties::PROPERTY_TYPE_FLOAT;
  339. $propertyValue = floatval($propertyValue);
  340. break;
  341. case 'dateTime.tz':
  342. $propertyType = PHPExcel_DocumentProperties::PROPERTY_TYPE_DATE;
  343. $propertyValue = strtotime(trim($propertyValue));
  344. break;
  345. }
  346. $docProps->setCustomProperty($propertyName, $propertyValue, $propertyType);
  347. }
  348. }
  349. foreach ($xml->Styles[0] as $style) {
  350. $style_ss = $style->attributes($namespaces['ss']);
  351. $styleID = (string) $style_ss['ID'];
  352. // echo 'Style ID = '.$styleID.'<br />';
  353. $this->styles[$styleID] = (isset($this->styles['Default'])) ? $this->styles['Default'] : array();
  354. foreach ($style as $styleType => $styleData) {
  355. $styleAttributes = $styleData->attributes($namespaces['ss']);
  356. // echo $styleType.'<br />';
  357. switch ($styleType) {
  358. case 'Alignment':
  359. foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
  360. // echo $styleAttributeKey.' = '.$styleAttributeValue.'<br />';
  361. $styleAttributeValue = (string) $styleAttributeValue;
  362. switch ($styleAttributeKey) {
  363. case 'Vertical':
  364. if (self::identifyFixedStyleValue($verticalAlignmentStyles, $styleAttributeValue)) {
  365. $this->styles[$styleID]['alignment']['vertical'] = $styleAttributeValue;
  366. }
  367. break;
  368. case 'Horizontal':
  369. if (self::identifyFixedStyleValue($horizontalAlignmentStyles, $styleAttributeValue)) {
  370. $this->styles[$styleID]['alignment']['horizontal'] = $styleAttributeValue;
  371. }
  372. break;
  373. case 'WrapText':
  374. $this->styles[$styleID]['alignment']['wrap'] = true;
  375. break;
  376. }
  377. }
  378. break;
  379. case 'Borders':
  380. foreach ($styleData->Border as $borderStyle) {
  381. $borderAttributes = $borderStyle->attributes($namespaces['ss']);
  382. $thisBorder = array();
  383. foreach ($borderAttributes as $borderStyleKey => $borderStyleValue) {
  384. // echo $borderStyleKey.' = '.$borderStyleValue.'<br />';
  385. switch ($borderStyleKey) {
  386. case 'LineStyle':
  387. $thisBorder['style'] = PHPExcel_Style_Border::BORDER_MEDIUM;
  388. // $thisBorder['style'] = $borderStyleValue;
  389. break;
  390. case 'Weight':
  391. // $thisBorder['style'] = $borderStyleValue;
  392. break;
  393. case 'Position':
  394. $borderPosition = strtolower($borderStyleValue);
  395. break;
  396. case 'Color':
  397. $borderColour = substr($borderStyleValue, 1);
  398. $thisBorder['color']['rgb'] = $borderColour;
  399. break;
  400. }
  401. }
  402. if (!empty($thisBorder)) {
  403. if (($borderPosition == 'left') || ($borderPosition == 'right') || ($borderPosition == 'top') || ($borderPosition == 'bottom')) {
  404. $this->styles[$styleID]['borders'][$borderPosition] = $thisBorder;
  405. }
  406. }
  407. }
  408. break;
  409. case 'Font':
  410. foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
  411. // echo $styleAttributeKey.' = '.$styleAttributeValue.'<br />';
  412. $styleAttributeValue = (string) $styleAttributeValue;
  413. switch ($styleAttributeKey) {
  414. case 'FontName':
  415. $this->styles[$styleID]['font']['name'] = $styleAttributeValue;
  416. break;
  417. case 'Size':
  418. $this->styles[$styleID]['font']['size'] = $styleAttributeValue;
  419. break;
  420. case 'Color':
  421. $this->styles[$styleID]['font']['color']['rgb'] = substr($styleAttributeValue, 1);
  422. break;
  423. case 'Bold':
  424. $this->styles[$styleID]['font']['bold'] = true;
  425. break;
  426. case 'Italic':
  427. $this->styles[$styleID]['font']['italic'] = true;
  428. break;
  429. case 'Underline':
  430. if (self::identifyFixedStyleValue($underlineStyles, $styleAttributeValue)) {
  431. $this->styles[$styleID]['font']['underline'] = $styleAttributeValue;
  432. }
  433. break;
  434. }
  435. }
  436. break;
  437. case 'Interior':
  438. foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
  439. // echo $styleAttributeKey.' = '.$styleAttributeValue.'<br />';
  440. switch ($styleAttributeKey) {
  441. case 'Color':
  442. $this->styles[$styleID]['fill']['color']['rgb'] = substr($styleAttributeValue, 1);
  443. break;
  444. }
  445. }
  446. break;
  447. case 'NumberFormat':
  448. foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
  449. // echo $styleAttributeKey.' = '.$styleAttributeValue.'<br />';
  450. $styleAttributeValue = str_replace($fromFormats, $toFormats, $styleAttributeValue);
  451. switch ($styleAttributeValue) {
  452. case 'Short Date':
  453. $styleAttributeValue = 'dd/mm/yyyy';
  454. break;
  455. }
  456. if ($styleAttributeValue > '') {
  457. $this->styles[$styleID]['numberformat']['code'] = $styleAttributeValue;
  458. }
  459. }
  460. break;
  461. case 'Protection':
  462. foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
  463. // echo $styleAttributeKey.' = '.$styleAttributeValue.'<br />';
  464. }
  465. break;
  466. }
  467. }
  468. // print_r($this->styles[$styleID]);
  469. // echo '<hr />';
  470. }
  471. // echo '<hr />';
  472. $worksheetID = 0;
  473. $xml_ss = $xml->children($namespaces['ss']);
  474. foreach ($xml_ss->Worksheet as $worksheet) {
  475. $worksheet_ss = $worksheet->attributes($namespaces['ss']);
  476. if ((isset($this->loadSheetsOnly)) && (isset($worksheet_ss['Name'])) &&
  477. (!in_array($worksheet_ss['Name'], $this->loadSheetsOnly))) {
  478. continue;
  479. }
  480. // echo '<h3>Worksheet: ', $worksheet_ss['Name'],'<h3>';
  481. //
  482. // Create new Worksheet
  483. $objPHPExcel->createSheet();
  484. $objPHPExcel->setActiveSheetIndex($worksheetID);
  485. if (isset($worksheet_ss['Name'])) {
  486. $worksheetName = self::convertStringEncoding((string) $worksheet_ss['Name'], $this->charSet);
  487. // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in
  488. // formula cells... during the load, all formulae should be correct, and we're simply bringing
  489. // the worksheet name in line with the formula, not the reverse
  490. $objPHPExcel->getActiveSheet()->setTitle($worksheetName, false);
  491. }
  492. $columnID = 'A';
  493. if (isset($worksheet->Table->Column)) {
  494. foreach ($worksheet->Table->Column as $columnData) {
  495. $columnData_ss = $columnData->attributes($namespaces['ss']);
  496. if (isset($columnData_ss['Index'])) {
  497. $columnID = PHPExcel_Cell::stringFromColumnIndex($columnData_ss['Index']-1);
  498. }
  499. if (isset($columnData_ss['Width'])) {
  500. $columnWidth = $columnData_ss['Width'];
  501. // echo '<b>Setting column width for '.$columnID.' to '.$columnWidth.'</b><br />';
  502. $objPHPExcel->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4);
  503. }
  504. ++$columnID;
  505. }
  506. }
  507. $rowID = 1;
  508. if (isset($worksheet->Table->Row)) {
  509. $additionalMergedCells = 0;
  510. foreach ($worksheet->Table->Row as $rowData) {
  511. $rowHasData = false;
  512. $row_ss = $rowData->attributes($namespaces['ss']);
  513. if (isset($row_ss['Index'])) {
  514. $rowID = (integer) $row_ss['Index'];
  515. }
  516. // echo '<b>Row '.$rowID.'</b><br />';
  517. $columnID = 'A';
  518. foreach ($rowData->Cell as $cell) {
  519. $cell_ss = $cell->attributes($namespaces['ss']);
  520. if (isset($cell_ss['Index'])) {
  521. $columnID = PHPExcel_Cell::stringFromColumnIndex($cell_ss['Index']-1);
  522. }
  523. $cellRange = $columnID.$rowID;
  524. if ($this->getReadFilter() !== null) {
  525. if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
  526. continue;
  527. }
  528. }
  529. if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) {
  530. $columnTo = $columnID;
  531. if (isset($cell_ss['MergeAcross'])) {
  532. $additionalMergedCells += (int)$cell_ss['MergeAcross'];
  533. $columnTo = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::columnIndexFromString($columnID) + $cell_ss['MergeAcross'] -1);
  534. }
  535. $rowTo = $rowID;
  536. if (isset($cell_ss['MergeDown'])) {
  537. $rowTo = $rowTo + $cell_ss['MergeDown'];
  538. }
  539. $cellRange .= ':'.$columnTo.$rowTo;
  540. $objPHPExcel->getActiveSheet()->mergeCells($cellRange);
  541. }
  542. $cellIsSet = $hasCalculatedValue = false;
  543. $cellDataFormula = '';
  544. if (isset($cell_ss['Formula'])) {
  545. $cellDataFormula = $cell_ss['Formula'];
  546. // added this as a check for array formulas
  547. if (isset($cell_ss['ArrayRange'])) {
  548. $cellDataCSEFormula = $cell_ss['ArrayRange'];
  549. // echo "found an array formula at ".$columnID.$rowID."<br />";
  550. }
  551. $hasCalculatedValue = true;
  552. }
  553. if (isset($cell->Data)) {
  554. $cellValue = $cellData = $cell->Data;
  555. $type = PHPExcel_Cell_DataType::TYPE_NULL;
  556. $cellData_ss = $cellData->attributes($namespaces['ss']);
  557. if (isset($cellData_ss['Type'])) {
  558. $cellDataType = $cellData_ss['Type'];
  559. switch ($cellDataType) {
  560. /*
  561. const TYPE_STRING = 's';
  562. const TYPE_FORMULA = 'f';
  563. const TYPE_NUMERIC = 'n';
  564. const TYPE_BOOL = 'b';
  565. const TYPE_NULL = 'null';
  566. const TYPE_INLINE = 'inlineStr';
  567. const TYPE_ERROR = 'e';
  568. */
  569. case 'String':
  570. $cellValue = self::convertStringEncoding($cellValue, $this->charSet);
  571. $type = PHPExcel_Cell_DataType::TYPE_STRING;
  572. break;
  573. case 'Number':
  574. $type = PHPExcel_Cell_DataType::TYPE_NUMERIC;
  575. $cellValue = (float) $cellValue;
  576. if (floor($cellValue) == $cellValue) {
  577. $cellValue = (integer) $cellValue;
  578. }
  579. break;
  580. case 'Boolean':
  581. $type = PHPExcel_Cell_DataType::TYPE_BOOL;
  582. $cellValue = ($cellValue != 0);
  583. break;
  584. case 'DateTime':
  585. $type = PHPExcel_Cell_DataType::TYPE_NUMERIC;
  586. $cellValue = PHPExcel_Shared_Date::PHPToExcel(strtotime($cellValue));
  587. break;
  588. case 'Error':
  589. $type = PHPExcel_Cell_DataType::TYPE_ERROR;
  590. break;
  591. }
  592. }
  593. if ($hasCalculatedValue) {
  594. // echo 'FORMULA<br />';
  595. $type = PHPExcel_Cell_DataType::TYPE_FORMULA;
  596. $columnNumber = PHPExcel_Cell::columnIndexFromString($columnID);
  597. if (substr($cellDataFormula, 0, 3) == 'of:') {
  598. $cellDataFormula = substr($cellDataFormula, 3);
  599. // echo 'Before: ', $cellDataFormula,'<br />';
  600. $temp = explode('"', $cellDataFormula);
  601. $key = false;
  602. foreach ($temp as &$value) {
  603. // Only replace in alternate array entries (i.e. non-quoted blocks)
  604. if ($key = !$key) {
  605. $value = str_replace(array('[.', '.', ']'), '', $value);
  606. }
  607. }
  608. } else {
  609. // Convert R1C1 style references to A1 style references (but only when not quoted)
  610. // echo 'Before: ', $cellDataFormula,'<br />';
  611. $temp = explode('"', $cellDataFormula);
  612. $key = false;
  613. foreach ($temp as &$value) {
  614. // Only replace in alternate array entries (i.e. non-quoted blocks)
  615. if ($key = !$key) {
  616. preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
  617. // Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way
  618. // through the formula from left to right. Reversing means that we work right to left.through
  619. // the formula
  620. $cellReferences = array_reverse($cellReferences);
  621. // Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent,
  622. // then modify the formula to use that new reference
  623. foreach ($cellReferences as $cellReference) {
  624. $rowReference = $cellReference[2][0];
  625. // Empty R reference is the current row
  626. if ($rowReference == '') {
  627. $rowReference = $rowID;
  628. }
  629. // Bracketed R references are relative to the current row
  630. if ($rowReference{0} == '[') {
  631. $rowReference = $rowID + trim($rowReference, '[]');
  632. }
  633. $columnReference = $cellReference[4][0];
  634. // Empty C reference is the current column
  635. if ($columnReference == '') {
  636. $columnReference = $columnNumber;
  637. }
  638. // Bracketed C references are relative to the current column
  639. if ($columnReference{0} == '[') {
  640. $columnReference = $columnNumber + trim($columnReference, '[]');
  641. }
  642. $A1CellReference = PHPExcel_Cell::stringFromColumnIndex($columnReference-1).$rowReference;
  643. $value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0]));
  644. }
  645. }
  646. }
  647. }
  648. unset($value);
  649. // Then rebuild the formula string
  650. $cellDataFormula = implode('"', $temp);
  651. // echo 'After: ', $cellDataFormula,'<br />';
  652. }
  653. // echo 'Cell '.$columnID.$rowID.' is a '.$type.' with a value of '.(($hasCalculatedValue) ? $cellDataFormula : $cellValue).'<br />';
  654. //
  655. $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue), $type);
  656. if ($hasCalculatedValue) {
  657. // echo 'Formula result is '.$cellValue.'<br />';
  658. $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setCalculatedValue($cellValue);
  659. }
  660. $cellIsSet = $rowHasData = true;
  661. }
  662. if (isset($cell->Comment)) {
  663. // echo '<b>comment found</b><br />';
  664. $commentAttributes = $cell->Comment->attributes($namespaces['ss']);
  665. $author = 'unknown';
  666. if (isset($commentAttributes->Author)) {
  667. $author = (string)$commentAttributes->Author;
  668. // echo 'Author: ', $author,'<br />';
  669. }
  670. $node = $cell->Comment->Data->asXML();
  671. // $annotation = str_replace('html:','',substr($node,49,-10));
  672. // echo $annotation,'<br />';
  673. $annotation = strip_tags($node);
  674. // echo 'Annotation: ', $annotation,'<br />';
  675. $objPHPExcel->getActiveSheet()->getComment($columnID.$rowID)->setAuthor(self::convertStringEncoding($author, $this->charSet))->setText($this->parseRichText($annotation));
  676. }
  677. if (($cellIsSet) && (isset($cell_ss['StyleID']))) {
  678. $style = (string) $cell_ss['StyleID'];
  679. // echo 'Cell style for '.$columnID.$rowID.' is '.$style.'<br />';
  680. if ((isset($this->styles[$style])) && (!empty($this->styles[$style]))) {
  681. // echo 'Cell '.$columnID.$rowID.'<br />';
  682. // print_r($this->styles[$style]);
  683. // echo '<br />';
  684. if (!$objPHPExcel->getActiveSheet()->cellExists($columnID.$rowID)) {
  685. $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setValue(null);
  686. }
  687. $objPHPExcel->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->styles[$style]);
  688. }
  689. }
  690. ++$columnID;
  691. while ($additionalMergedCells > 0) {
  692. ++$columnID;
  693. $additionalMergedCells--;
  694. }
  695. }
  696. if ($rowHasData) {
  697. if (isset($row_ss['StyleID'])) {
  698. $rowStyle = $row_ss['StyleID'];
  699. }
  700. if (isset($row_ss['Height'])) {
  701. $rowHeight = $row_ss['Height'];
  702. // echo '<b>Setting row height to '.$rowHeight.'</b><br />';
  703. $objPHPExcel->getActiveSheet()->getRowDimension($rowID)->setRowHeight($rowHeight);
  704. }
  705. }
  706. ++$rowID;
  707. }
  708. }
  709. ++$worksheetID;
  710. }
  711. // Return
  712. return $objPHPExcel;
  713. }
  714. protected static function convertStringEncoding($string, $charset)
  715. {
  716. if ($charset != 'UTF-8') {
  717. return PHPExcel_Shared_String::ConvertEncoding($string, 'UTF-8', $charset);
  718. }
  719. return $string;
  720. }
  721. protected function parseRichText($is = '')
  722. {
  723. $value = new PHPExcel_RichText();
  724. $value->createText(self::convertStringEncoding($is, $this->charSet));
  725. return $value;
  726. }
  727. }