SYLK.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  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_SYLK
  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_SYLK extends PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader
  36. {
  37. /**
  38. * Input encoding
  39. *
  40. * @var string
  41. */
  42. private $inputEncoding = 'ANSI';
  43. /**
  44. * Sheet index to read
  45. *
  46. * @var int
  47. */
  48. private $sheetIndex = 0;
  49. /**
  50. * Formats
  51. *
  52. * @var array
  53. */
  54. private $formats = array();
  55. /**
  56. * Format Count
  57. *
  58. * @var int
  59. */
  60. private $format = 0;
  61. /**
  62. * Create a new PHPExcel_Reader_SYLK
  63. */
  64. public function __construct()
  65. {
  66. $this->readFilter = new PHPExcel_Reader_DefaultReadFilter();
  67. }
  68. /**
  69. * Validate that the current file is a SYLK file
  70. *
  71. * @return boolean
  72. */
  73. protected function isValidFormat()
  74. {
  75. // Read sample data (first 2 KB will do)
  76. $data = fread($this->fileHandle, 2048);
  77. // Count delimiters in file
  78. $delimiterCount = substr_count($data, ';');
  79. if ($delimiterCount < 1) {
  80. return false;
  81. }
  82. // Analyze first line looking for ID; signature
  83. $lines = explode("\n", $data);
  84. if (substr($lines[0], 0, 4) != 'ID;P') {
  85. return false;
  86. }
  87. return true;
  88. }
  89. /**
  90. * Set input encoding
  91. *
  92. * @param string $pValue Input encoding
  93. */
  94. public function setInputEncoding($pValue = 'ANSI')
  95. {
  96. $this->inputEncoding = $pValue;
  97. return $this;
  98. }
  99. /**
  100. * Get input encoding
  101. *
  102. * @return string
  103. */
  104. public function getInputEncoding()
  105. {
  106. return $this->inputEncoding;
  107. }
  108. /**
  109. * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns)
  110. *
  111. * @param string $pFilename
  112. * @throws PHPExcel_Reader_Exception
  113. */
  114. public function listWorksheetInfo($pFilename)
  115. {
  116. // Open file
  117. $this->openFile($pFilename);
  118. if (!$this->isValidFormat()) {
  119. fclose($this->fileHandle);
  120. throw new PHPExcel_Reader_Exception($pFilename . " is an Invalid Spreadsheet file.");
  121. }
  122. $fileHandle = $this->fileHandle;
  123. rewind($fileHandle);
  124. $worksheetInfo = array();
  125. $worksheetInfo[0]['worksheetName'] = 'Worksheet';
  126. $worksheetInfo[0]['lastColumnLetter'] = 'A';
  127. $worksheetInfo[0]['lastColumnIndex'] = 0;
  128. $worksheetInfo[0]['totalRows'] = 0;
  129. $worksheetInfo[0]['totalColumns'] = 0;
  130. // Loop through file
  131. $rowData = array();
  132. // loop through one row (line) at a time in the file
  133. $rowIndex = 0;
  134. while (($rowData = fgets($fileHandle)) !== false) {
  135. $columnIndex = 0;
  136. // convert SYLK encoded $rowData to UTF-8
  137. $rowData = PHPExcel_Shared_String::SYLKtoUTF8($rowData);
  138. // explode each row at semicolons while taking into account that literal semicolon (;)
  139. // is escaped like this (;;)
  140. $rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData)))));
  141. $dataType = array_shift($rowData);
  142. if ($dataType == 'C') {
  143. // Read cell value data
  144. foreach ($rowData as $rowDatum) {
  145. switch ($rowDatum{0}) {
  146. case 'C':
  147. case 'X':
  148. $columnIndex = substr($rowDatum, 1) - 1;
  149. break;
  150. case 'R':
  151. case 'Y':
  152. $rowIndex = substr($rowDatum, 1);
  153. break;
  154. }
  155. $worksheetInfo[0]['totalRows'] = max($worksheetInfo[0]['totalRows'], $rowIndex);
  156. $worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], $columnIndex);
  157. }
  158. }
  159. }
  160. $worksheetInfo[0]['lastColumnLetter'] = PHPExcel_Cell::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex']);
  161. $worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1;
  162. // Close file
  163. fclose($fileHandle);
  164. return $worksheetInfo;
  165. }
  166. /**
  167. * Loads PHPExcel from file
  168. *
  169. * @param string $pFilename
  170. * @return PHPExcel
  171. * @throws PHPExcel_Reader_Exception
  172. */
  173. public function load($pFilename)
  174. {
  175. // Create new PHPExcel
  176. $objPHPExcel = new PHPExcel();
  177. // Load into this instance
  178. return $this->loadIntoExisting($pFilename, $objPHPExcel);
  179. }
  180. /**
  181. * Loads PHPExcel from file into PHPExcel instance
  182. *
  183. * @param string $pFilename
  184. * @param PHPExcel $objPHPExcel
  185. * @return PHPExcel
  186. * @throws PHPExcel_Reader_Exception
  187. */
  188. public function loadIntoExisting($pFilename, PHPExcel $objPHPExcel)
  189. {
  190. // Open file
  191. $this->openFile($pFilename);
  192. if (!$this->isValidFormat()) {
  193. fclose($this->fileHandle);
  194. throw new PHPExcel_Reader_Exception($pFilename . " is an Invalid Spreadsheet file.");
  195. }
  196. $fileHandle = $this->fileHandle;
  197. rewind($fileHandle);
  198. // Create new PHPExcel
  199. while ($objPHPExcel->getSheetCount() <= $this->sheetIndex) {
  200. $objPHPExcel->createSheet();
  201. }
  202. $objPHPExcel->setActiveSheetIndex($this->sheetIndex);
  203. $fromFormats = array('\-', '\ ');
  204. $toFormats = array('-', ' ');
  205. // Loop through file
  206. $rowData = array();
  207. $column = $row = '';
  208. // loop through one row (line) at a time in the file
  209. while (($rowData = fgets($fileHandle)) !== false) {
  210. // convert SYLK encoded $rowData to UTF-8
  211. $rowData = PHPExcel_Shared_String::SYLKtoUTF8($rowData);
  212. // explode each row at semicolons while taking into account that literal semicolon (;)
  213. // is escaped like this (;;)
  214. $rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData)))));
  215. $dataType = array_shift($rowData);
  216. // Read shared styles
  217. if ($dataType == 'P') {
  218. $formatArray = array();
  219. foreach ($rowData as $rowDatum) {
  220. switch ($rowDatum{0}) {
  221. case 'P':
  222. $formatArray['numberformat']['code'] = str_replace($fromFormats, $toFormats, substr($rowDatum, 1));
  223. break;
  224. case 'E':
  225. case 'F':
  226. $formatArray['font']['name'] = substr($rowDatum, 1);
  227. break;
  228. case 'L':
  229. $formatArray['font']['size'] = substr($rowDatum, 1);
  230. break;
  231. case 'S':
  232. $styleSettings = substr($rowDatum, 1);
  233. for ($i=0; $i<strlen($styleSettings); ++$i) {
  234. switch ($styleSettings{$i}) {
  235. case 'I':
  236. $formatArray['font']['italic'] = true;
  237. break;
  238. case 'D':
  239. $formatArray['font']['bold'] = true;
  240. break;
  241. case 'T':
  242. $formatArray['borders']['top']['style'] = PHPExcel_Style_Border::BORDER_THIN;
  243. break;
  244. case 'B':
  245. $formatArray['borders']['bottom']['style'] = PHPExcel_Style_Border::BORDER_THIN;
  246. break;
  247. case 'L':
  248. $formatArray['borders']['left']['style'] = PHPExcel_Style_Border::BORDER_THIN;
  249. break;
  250. case 'R':
  251. $formatArray['borders']['right']['style'] = PHPExcel_Style_Border::BORDER_THIN;
  252. break;
  253. }
  254. }
  255. break;
  256. }
  257. }
  258. $this->formats['P'.$this->format++] = $formatArray;
  259. // Read cell value data
  260. } elseif ($dataType == 'C') {
  261. $hasCalculatedValue = false;
  262. $cellData = $cellDataFormula = '';
  263. foreach ($rowData as $rowDatum) {
  264. switch ($rowDatum{0}) {
  265. case 'C':
  266. case 'X':
  267. $column = substr($rowDatum, 1);
  268. break;
  269. case 'R':
  270. case 'Y':
  271. $row = substr($rowDatum, 1);
  272. break;
  273. case 'K':
  274. $cellData = substr($rowDatum, 1);
  275. break;
  276. case 'E':
  277. $cellDataFormula = '='.substr($rowDatum, 1);
  278. // Convert R1C1 style references to A1 style references (but only when not quoted)
  279. $temp = explode('"', $cellDataFormula);
  280. $key = false;
  281. foreach ($temp as &$value) {
  282. // Only count/replace in alternate array entries
  283. if ($key = !$key) {
  284. preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER+PREG_OFFSET_CAPTURE);
  285. // Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way
  286. // through the formula from left to right. Reversing means that we work right to left.through
  287. // the formula
  288. $cellReferences = array_reverse($cellReferences);
  289. // Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent,
  290. // then modify the formula to use that new reference
  291. foreach ($cellReferences as $cellReference) {
  292. $rowReference = $cellReference[2][0];
  293. // Empty R reference is the current row
  294. if ($rowReference == '') {
  295. $rowReference = $row;
  296. }
  297. // Bracketed R references are relative to the current row
  298. if ($rowReference{0} == '[') {
  299. $rowReference = $row + trim($rowReference, '[]');
  300. }
  301. $columnReference = $cellReference[4][0];
  302. // Empty C reference is the current column
  303. if ($columnReference == '') {
  304. $columnReference = $column;
  305. }
  306. // Bracketed C references are relative to the current column
  307. if ($columnReference{0} == '[') {
  308. $columnReference = $column + trim($columnReference, '[]');
  309. }
  310. $A1CellReference = PHPExcel_Cell::stringFromColumnIndex($columnReference-1).$rowReference;
  311. $value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0]));
  312. }
  313. }
  314. }
  315. unset($value);
  316. // Then rebuild the formula string
  317. $cellDataFormula = implode('"', $temp);
  318. $hasCalculatedValue = true;
  319. break;
  320. }
  321. }
  322. $columnLetter = PHPExcel_Cell::stringFromColumnIndex($column-1);
  323. $cellData = PHPExcel_Calculation::unwrapResult($cellData);
  324. // Set cell value
  325. $objPHPExcel->getActiveSheet()->getCell($columnLetter.$row)->setValue(($hasCalculatedValue) ? $cellDataFormula : $cellData);
  326. if ($hasCalculatedValue) {
  327. $cellData = PHPExcel_Calculation::unwrapResult($cellData);
  328. $objPHPExcel->getActiveSheet()->getCell($columnLetter.$row)->setCalculatedValue($cellData);
  329. }
  330. // Read cell formatting
  331. } elseif ($dataType == 'F') {
  332. $formatStyle = $columnWidth = $styleSettings = '';
  333. $styleData = array();
  334. foreach ($rowData as $rowDatum) {
  335. switch ($rowDatum{0}) {
  336. case 'C':
  337. case 'X':
  338. $column = substr($rowDatum, 1);
  339. break;
  340. case 'R':
  341. case 'Y':
  342. $row = substr($rowDatum, 1);
  343. break;
  344. case 'P':
  345. $formatStyle = $rowDatum;
  346. break;
  347. case 'W':
  348. list($startCol, $endCol, $columnWidth) = explode(' ', substr($rowDatum, 1));
  349. break;
  350. case 'S':
  351. $styleSettings = substr($rowDatum, 1);
  352. for ($i=0; $i<strlen($styleSettings); ++$i) {
  353. switch ($styleSettings{$i}) {
  354. case 'I':
  355. $styleData['font']['italic'] = true;
  356. break;
  357. case 'D':
  358. $styleData['font']['bold'] = true;
  359. break;
  360. case 'T':
  361. $styleData['borders']['top']['style'] = PHPExcel_Style_Border::BORDER_THIN;
  362. break;
  363. case 'B':
  364. $styleData['borders']['bottom']['style'] = PHPExcel_Style_Border::BORDER_THIN;
  365. break;
  366. case 'L':
  367. $styleData['borders']['left']['style'] = PHPExcel_Style_Border::BORDER_THIN;
  368. break;
  369. case 'R':
  370. $styleData['borders']['right']['style'] = PHPExcel_Style_Border::BORDER_THIN;
  371. break;
  372. }
  373. }
  374. break;
  375. }
  376. }
  377. if (($formatStyle > '') && ($column > '') && ($row > '')) {
  378. $columnLetter = PHPExcel_Cell::stringFromColumnIndex($column-1);
  379. if (isset($this->formats[$formatStyle])) {
  380. $objPHPExcel->getActiveSheet()->getStyle($columnLetter.$row)->applyFromArray($this->formats[$formatStyle]);
  381. }
  382. }
  383. if ((!empty($styleData)) && ($column > '') && ($row > '')) {
  384. $columnLetter = PHPExcel_Cell::stringFromColumnIndex($column-1);
  385. $objPHPExcel->getActiveSheet()->getStyle($columnLetter.$row)->applyFromArray($styleData);
  386. }
  387. if ($columnWidth > '') {
  388. if ($startCol == $endCol) {
  389. $startCol = PHPExcel_Cell::stringFromColumnIndex($startCol-1);
  390. $objPHPExcel->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth);
  391. } else {
  392. $startCol = PHPExcel_Cell::stringFromColumnIndex($startCol-1);
  393. $endCol = PHPExcel_Cell::stringFromColumnIndex($endCol-1);
  394. $objPHPExcel->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth);
  395. do {
  396. $objPHPExcel->getActiveSheet()->getColumnDimension(++$startCol)->setWidth($columnWidth);
  397. } while ($startCol != $endCol);
  398. }
  399. }
  400. } else {
  401. foreach ($rowData as $rowDatum) {
  402. switch ($rowDatum{0}) {
  403. case 'C':
  404. case 'X':
  405. $column = substr($rowDatum, 1);
  406. break;
  407. case 'R':
  408. case 'Y':
  409. $row = substr($rowDatum, 1);
  410. break;
  411. }
  412. }
  413. }
  414. }
  415. // Close file
  416. fclose($fileHandle);
  417. // Return
  418. return $objPHPExcel;
  419. }
  420. /**
  421. * Get sheet index
  422. *
  423. * @return int
  424. */
  425. public function getSheetIndex()
  426. {
  427. return $this->sheetIndex;
  428. }
  429. /**
  430. * Set sheet index
  431. *
  432. * @param int $pValue Sheet index
  433. * @return PHPExcel_Reader_SYLK
  434. */
  435. public function setSheetIndex($pValue = 0)
  436. {
  437. $this->sheetIndex = $pValue;
  438. return $this;
  439. }
  440. }