PageRenderTime 74ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 2ms

/lib/PHPExcel/Classes/PHPExcel/Reader/Excel5.php

https://bitbucket.org/georgikirow/ksk
PHP | 6845 lines | 3873 code | 1136 blank | 1836 comment | 495 complexity | e6e72c7e1aef0f9fe07bf3b3fc309c2e MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-2.0, LGPL-3.0

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

  1. <?php
  2. /**
  3. * PHPExcel
  4. *
  5. * Copyright (c) 2006 - 2013 PHPExcel
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * @category PHPExcel
  22. * @package PHPExcel_Reader_Excel5
  23. * @copyright Copyright (c) 2006 - 2013 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version 1.7.9, 2013-06-02
  26. */
  27. // Original file header of ParseXL (used as the base for this class):
  28. // --------------------------------------------------------------------------------
  29. // Adapted from Excel_Spreadsheet_Reader developed by users bizon153,
  30. // trex005, and mmp11 (SourceForge.net)
  31. // http://sourceforge.net/projects/phpexcelreader/
  32. // Primary changes made by canyoncasa (dvc) for ParseXL 1.00 ...
  33. // Modelled moreso after Perl Excel Parse/Write modules
  34. // Added Parse_Excel_Spreadsheet object
  35. // Reads a whole worksheet or tab as row,column array or as
  36. // associated hash of indexed rows and named column fields
  37. // Added variables for worksheet (tab) indexes and names
  38. // Added an object call for loading individual woorksheets
  39. // Changed default indexing defaults to 0 based arrays
  40. // Fixed date/time and percent formats
  41. // Includes patches found at SourceForge...
  42. // unicode patch by nobody
  43. // unpack("d") machine depedency patch by matchy
  44. // boundsheet utf16 patch by bjaenichen
  45. // Renamed functions for shorter names
  46. // General code cleanup and rigor, including <80 column width
  47. // Included a testcase Excel file and PHP example calls
  48. // Code works for PHP 5.x
  49. // Primary changes made by canyoncasa (dvc) for ParseXL 1.10 ...
  50. // http://sourceforge.net/tracker/index.php?func=detail&aid=1466964&group_id=99160&atid=623334
  51. // Decoding of formula conditions, results, and tokens.
  52. // Support for user-defined named cells added as an array "namedcells"
  53. // Patch code for user-defined named cells supports single cells only.
  54. // NOTE: this patch only works for BIFF8 as BIFF5-7 use a different
  55. // external sheet reference structure
  56. /** PHPExcel root directory */
  57. if (!defined('PHPEXCEL_ROOT')) {
  58. /**
  59. * @ignore
  60. */
  61. define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../');
  62. require(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
  63. }
  64. /**
  65. * PHPExcel_Reader_Excel5
  66. *
  67. * This class uses {@link http://sourceforge.net/projects/phpexcelreader/parseXL}
  68. *
  69. * @category PHPExcel
  70. * @package PHPExcel_Reader_Excel5
  71. * @copyright Copyright (c) 2006 - 2013 PHPExcel (http://www.codeplex.com/PHPExcel)
  72. */
  73. class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader
  74. {
  75. // ParseXL definitions
  76. const XLS_BIFF8 = 0x0600;
  77. const XLS_BIFF7 = 0x0500;
  78. const XLS_WorkbookGlobals = 0x0005;
  79. const XLS_Worksheet = 0x0010;
  80. // record identifiers
  81. const XLS_Type_FORMULA = 0x0006;
  82. const XLS_Type_EOF = 0x000a;
  83. const XLS_Type_PROTECT = 0x0012;
  84. const XLS_Type_OBJECTPROTECT = 0x0063;
  85. const XLS_Type_SCENPROTECT = 0x00dd;
  86. const XLS_Type_PASSWORD = 0x0013;
  87. const XLS_Type_HEADER = 0x0014;
  88. const XLS_Type_FOOTER = 0x0015;
  89. const XLS_Type_EXTERNSHEET = 0x0017;
  90. const XLS_Type_DEFINEDNAME = 0x0018;
  91. const XLS_Type_VERTICALPAGEBREAKS = 0x001a;
  92. const XLS_Type_HORIZONTALPAGEBREAKS = 0x001b;
  93. const XLS_Type_NOTE = 0x001c;
  94. const XLS_Type_SELECTION = 0x001d;
  95. const XLS_Type_DATEMODE = 0x0022;
  96. const XLS_Type_EXTERNNAME = 0x0023;
  97. const XLS_Type_LEFTMARGIN = 0x0026;
  98. const XLS_Type_RIGHTMARGIN = 0x0027;
  99. const XLS_Type_TOPMARGIN = 0x0028;
  100. const XLS_Type_BOTTOMMARGIN = 0x0029;
  101. const XLS_Type_PRINTGRIDLINES = 0x002b;
  102. const XLS_Type_FILEPASS = 0x002f;
  103. const XLS_Type_FONT = 0x0031;
  104. const XLS_Type_CONTINUE = 0x003c;
  105. const XLS_Type_PANE = 0x0041;
  106. const XLS_Type_CODEPAGE = 0x0042;
  107. const XLS_Type_DEFCOLWIDTH = 0x0055;
  108. const XLS_Type_OBJ = 0x005d;
  109. const XLS_Type_COLINFO = 0x007d;
  110. const XLS_Type_IMDATA = 0x007f;
  111. const XLS_Type_SHEETPR = 0x0081;
  112. const XLS_Type_HCENTER = 0x0083;
  113. const XLS_Type_VCENTER = 0x0084;
  114. const XLS_Type_SHEET = 0x0085;
  115. const XLS_Type_PALETTE = 0x0092;
  116. const XLS_Type_SCL = 0x00a0;
  117. const XLS_Type_PAGESETUP = 0x00a1;
  118. const XLS_Type_MULRK = 0x00bd;
  119. const XLS_Type_MULBLANK = 0x00be;
  120. const XLS_Type_DBCELL = 0x00d7;
  121. const XLS_Type_XF = 0x00e0;
  122. const XLS_Type_MERGEDCELLS = 0x00e5;
  123. const XLS_Type_MSODRAWINGGROUP = 0x00eb;
  124. const XLS_Type_MSODRAWING = 0x00ec;
  125. const XLS_Type_SST = 0x00fc;
  126. const XLS_Type_LABELSST = 0x00fd;
  127. const XLS_Type_EXTSST = 0x00ff;
  128. const XLS_Type_EXTERNALBOOK = 0x01ae;
  129. const XLS_Type_DATAVALIDATIONS = 0x01b2;
  130. const XLS_Type_TXO = 0x01b6;
  131. const XLS_Type_HYPERLINK = 0x01b8;
  132. const XLS_Type_DATAVALIDATION = 0x01be;
  133. const XLS_Type_DIMENSION = 0x0200;
  134. const XLS_Type_BLANK = 0x0201;
  135. const XLS_Type_NUMBER = 0x0203;
  136. const XLS_Type_LABEL = 0x0204;
  137. const XLS_Type_BOOLERR = 0x0205;
  138. const XLS_Type_STRING = 0x0207;
  139. const XLS_Type_ROW = 0x0208;
  140. const XLS_Type_INDEX = 0x020b;
  141. const XLS_Type_ARRAY = 0x0221;
  142. const XLS_Type_DEFAULTROWHEIGHT = 0x0225;
  143. const XLS_Type_WINDOW2 = 0x023e;
  144. const XLS_Type_RK = 0x027e;
  145. const XLS_Type_STYLE = 0x0293;
  146. const XLS_Type_FORMAT = 0x041e;
  147. const XLS_Type_SHAREDFMLA = 0x04bc;
  148. const XLS_Type_BOF = 0x0809;
  149. const XLS_Type_SHEETPROTECTION = 0x0867;
  150. const XLS_Type_RANGEPROTECTION = 0x0868;
  151. const XLS_Type_SHEETLAYOUT = 0x0862;
  152. const XLS_Type_XFEXT = 0x087d;
  153. const XLS_Type_PAGELAYOUTVIEW = 0x088b;
  154. const XLS_Type_UNKNOWN = 0xffff;
  155. /**
  156. * Summary Information stream data.
  157. *
  158. * @var string
  159. */
  160. private $_summaryInformation;
  161. /**
  162. * Extended Summary Information stream data.
  163. *
  164. * @var string
  165. */
  166. private $_documentSummaryInformation;
  167. /**
  168. * User-Defined Properties stream data.
  169. *
  170. * @var string
  171. */
  172. private $_userDefinedProperties;
  173. /**
  174. * Workbook stream data. (Includes workbook globals substream as well as sheet substreams)
  175. *
  176. * @var string
  177. */
  178. private $_data;
  179. /**
  180. * Size in bytes of $this->_data
  181. *
  182. * @var int
  183. */
  184. private $_dataSize;
  185. /**
  186. * Current position in stream
  187. *
  188. * @var integer
  189. */
  190. private $_pos;
  191. /**
  192. * Workbook to be returned by the reader.
  193. *
  194. * @var PHPExcel
  195. */
  196. private $_phpExcel;
  197. /**
  198. * Worksheet that is currently being built by the reader.
  199. *
  200. * @var PHPExcel_Worksheet
  201. */
  202. private $_phpSheet;
  203. /**
  204. * BIFF version
  205. *
  206. * @var int
  207. */
  208. private $_version;
  209. /**
  210. * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
  211. * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'
  212. *
  213. * @var string
  214. */
  215. private $_codepage;
  216. /**
  217. * Shared formats
  218. *
  219. * @var array
  220. */
  221. private $_formats;
  222. /**
  223. * Shared fonts
  224. *
  225. * @var array
  226. */
  227. private $_objFonts;
  228. /**
  229. * Color palette
  230. *
  231. * @var array
  232. */
  233. private $_palette;
  234. /**
  235. * Worksheets
  236. *
  237. * @var array
  238. */
  239. private $_sheets;
  240. /**
  241. * External books
  242. *
  243. * @var array
  244. */
  245. private $_externalBooks;
  246. /**
  247. * REF structures. Only applies to BIFF8.
  248. *
  249. * @var array
  250. */
  251. private $_ref;
  252. /**
  253. * External names
  254. *
  255. * @var array
  256. */
  257. private $_externalNames;
  258. /**
  259. * Defined names
  260. *
  261. * @var array
  262. */
  263. private $_definedname;
  264. /**
  265. * Shared strings. Only applies to BIFF8.
  266. *
  267. * @var array
  268. */
  269. private $_sst;
  270. /**
  271. * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
  272. *
  273. * @var boolean
  274. */
  275. private $_frozen;
  276. /**
  277. * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
  278. *
  279. * @var boolean
  280. */
  281. private $_isFitToPages;
  282. /**
  283. * Objects. One OBJ record contributes with one entry.
  284. *
  285. * @var array
  286. */
  287. private $_objs;
  288. /**
  289. * Text Objects. One TXO record corresponds with one entry.
  290. *
  291. * @var array
  292. */
  293. private $_textObjects;
  294. /**
  295. * Cell Annotations (BIFF8)
  296. *
  297. * @var array
  298. */
  299. private $_cellNotes;
  300. /**
  301. * The combined MSODRAWINGGROUP data
  302. *
  303. * @var string
  304. */
  305. private $_drawingGroupData;
  306. /**
  307. * The combined MSODRAWING data (per sheet)
  308. *
  309. * @var string
  310. */
  311. private $_drawingData;
  312. /**
  313. * Keep track of XF index
  314. *
  315. * @var int
  316. */
  317. private $_xfIndex;
  318. /**
  319. * Mapping of XF index (that is a cell XF) to final index in cellXf collection
  320. *
  321. * @var array
  322. */
  323. private $_mapCellXfIndex;
  324. /**
  325. * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection
  326. *
  327. * @var array
  328. */
  329. private $_mapCellStyleXfIndex;
  330. /**
  331. * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
  332. *
  333. * @var array
  334. */
  335. private $_sharedFormulas;
  336. /**
  337. * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
  338. * refers to a shared formula.
  339. *
  340. * @var array
  341. */
  342. private $_sharedFormulaParts;
  343. /**
  344. * Create a new PHPExcel_Reader_Excel5 instance
  345. */
  346. public function __construct() {
  347. $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
  348. }
  349. /**
  350. * Can the current PHPExcel_Reader_IReader read the file?
  351. *
  352. * @param string $pFilename
  353. * @return boolean
  354. * @throws PHPExcel_Reader_Exception
  355. */
  356. public function canRead($pFilename)
  357. {
  358. // Check if file exists
  359. if (!file_exists($pFilename)) {
  360. throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist.");
  361. }
  362. try {
  363. // Use ParseXL for the hard work.
  364. $ole = new PHPExcel_Shared_OLERead();
  365. // get excel data
  366. $res = $ole->read($pFilename);
  367. return true;
  368. } catch (PHPExcel_Exception $e) {
  369. return false;
  370. }
  371. }
  372. /**
  373. * Reads names of the worksheets from a file, without parsing the whole file to a PHPExcel object
  374. *
  375. * @param string $pFilename
  376. * @throws PHPExcel_Reader_Exception
  377. */
  378. public function listWorksheetNames($pFilename)
  379. {
  380. // Check if file exists
  381. if (!file_exists($pFilename)) {
  382. throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist.");
  383. }
  384. $worksheetNames = array();
  385. // Read the OLE file
  386. $this->_loadOLE($pFilename);
  387. // total byte size of Excel data (workbook global substream + sheet substreams)
  388. $this->_dataSize = strlen($this->_data);
  389. $this->_pos = 0;
  390. $this->_sheets = array();
  391. // Parse Workbook Global Substream
  392. while ($this->_pos < $this->_dataSize) {
  393. $code = self::_GetInt2d($this->_data, $this->_pos);
  394. switch ($code) {
  395. case self::XLS_Type_BOF: $this->_readBof(); break;
  396. case self::XLS_Type_SHEET: $this->_readSheet(); break;
  397. case self::XLS_Type_EOF: $this->_readDefault(); break 2;
  398. default: $this->_readDefault(); break;
  399. }
  400. }
  401. foreach ($this->_sheets as $sheet) {
  402. if ($sheet['sheetType'] != 0x00) {
  403. // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
  404. continue;
  405. }
  406. $worksheetNames[] = $sheet['name'];
  407. }
  408. return $worksheetNames;
  409. }
  410. /**
  411. * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns)
  412. *
  413. * @param string $pFilename
  414. * @throws PHPExcel_Reader_Exception
  415. */
  416. public function listWorksheetInfo($pFilename)
  417. {
  418. // Check if file exists
  419. if (!file_exists($pFilename)) {
  420. throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist.");
  421. }
  422. $worksheetInfo = array();
  423. // Read the OLE file
  424. $this->_loadOLE($pFilename);
  425. // total byte size of Excel data (workbook global substream + sheet substreams)
  426. $this->_dataSize = strlen($this->_data);
  427. // initialize
  428. $this->_pos = 0;
  429. $this->_sheets = array();
  430. // Parse Workbook Global Substream
  431. while ($this->_pos < $this->_dataSize) {
  432. $code = self::_GetInt2d($this->_data, $this->_pos);
  433. switch ($code) {
  434. case self::XLS_Type_BOF: $this->_readBof(); break;
  435. case self::XLS_Type_SHEET: $this->_readSheet(); break;
  436. case self::XLS_Type_EOF: $this->_readDefault(); break 2;
  437. default: $this->_readDefault(); break;
  438. }
  439. }
  440. // Parse the individual sheets
  441. foreach ($this->_sheets as $sheet) {
  442. if ($sheet['sheetType'] != 0x00) {
  443. // 0x00: Worksheet
  444. // 0x02: Chart
  445. // 0x06: Visual Basic module
  446. continue;
  447. }
  448. $tmpInfo = array();
  449. $tmpInfo['worksheetName'] = $sheet['name'];
  450. $tmpInfo['lastColumnLetter'] = 'A';
  451. $tmpInfo['lastColumnIndex'] = 0;
  452. $tmpInfo['totalRows'] = 0;
  453. $tmpInfo['totalColumns'] = 0;
  454. $this->_pos = $sheet['offset'];
  455. while ($this->_pos <= $this->_dataSize - 4) {
  456. $code = self::_GetInt2d($this->_data, $this->_pos);
  457. switch ($code) {
  458. case self::XLS_Type_RK:
  459. case self::XLS_Type_LABELSST:
  460. case self::XLS_Type_NUMBER:
  461. case self::XLS_Type_FORMULA:
  462. case self::XLS_Type_BOOLERR:
  463. case self::XLS_Type_LABEL:
  464. $length = self::_GetInt2d($this->_data, $this->_pos + 2);
  465. $recordData = substr($this->_data, $this->_pos + 4, $length);
  466. // move stream pointer to next record
  467. $this->_pos += 4 + $length;
  468. $rowIndex = self::_GetInt2d($recordData, 0) + 1;
  469. $columnIndex = self::_GetInt2d($recordData, 2);
  470. $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
  471. $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
  472. break;
  473. case self::XLS_Type_BOF: $this->_readBof(); break;
  474. case self::XLS_Type_EOF: $this->_readDefault(); break 2;
  475. default: $this->_readDefault(); break;
  476. }
  477. }
  478. $tmpInfo['lastColumnLetter'] = PHPExcel_Cell::stringFromColumnIndex($tmpInfo['lastColumnIndex']);
  479. $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
  480. $worksheetInfo[] = $tmpInfo;
  481. }
  482. return $worksheetInfo;
  483. }
  484. /**
  485. * Loads PHPExcel from file
  486. *
  487. * @param string $pFilename
  488. * @return PHPExcel
  489. * @throws PHPExcel_Reader_Exception
  490. */
  491. public function load($pFilename)
  492. {
  493. // Read the OLE file
  494. $this->_loadOLE($pFilename);
  495. // Initialisations
  496. $this->_phpExcel = new PHPExcel;
  497. $this->_phpExcel->removeSheetByIndex(0); // remove 1st sheet
  498. if (!$this->_readDataOnly) {
  499. $this->_phpExcel->removeCellStyleXfByIndex(0); // remove the default style
  500. $this->_phpExcel->removeCellXfByIndex(0); // remove the default style
  501. }
  502. // Read the summary information stream (containing meta data)
  503. $this->_readSummaryInformation();
  504. // Read the Additional document summary information stream (containing application-specific meta data)
  505. $this->_readDocumentSummaryInformation();
  506. // total byte size of Excel data (workbook global substream + sheet substreams)
  507. $this->_dataSize = strlen($this->_data);
  508. // initialize
  509. $this->_pos = 0;
  510. $this->_codepage = 'CP1252';
  511. $this->_formats = array();
  512. $this->_objFonts = array();
  513. $this->_palette = array();
  514. $this->_sheets = array();
  515. $this->_externalBooks = array();
  516. $this->_ref = array();
  517. $this->_definedname = array();
  518. $this->_sst = array();
  519. $this->_drawingGroupData = '';
  520. $this->_xfIndex = '';
  521. $this->_mapCellXfIndex = array();
  522. $this->_mapCellStyleXfIndex = array();
  523. // Parse Workbook Global Substream
  524. while ($this->_pos < $this->_dataSize) {
  525. $code = self::_GetInt2d($this->_data, $this->_pos);
  526. switch ($code) {
  527. case self::XLS_Type_BOF: $this->_readBof(); break;
  528. case self::XLS_Type_FILEPASS: $this->_readFilepass(); break;
  529. case self::XLS_Type_CODEPAGE: $this->_readCodepage(); break;
  530. case self::XLS_Type_DATEMODE: $this->_readDateMode(); break;
  531. case self::XLS_Type_FONT: $this->_readFont(); break;
  532. case self::XLS_Type_FORMAT: $this->_readFormat(); break;
  533. case self::XLS_Type_XF: $this->_readXf(); break;
  534. case self::XLS_Type_XFEXT: $this->_readXfExt(); break;
  535. case self::XLS_Type_STYLE: $this->_readStyle(); break;
  536. case self::XLS_Type_PALETTE: $this->_readPalette(); break;
  537. case self::XLS_Type_SHEET: $this->_readSheet(); break;
  538. case self::XLS_Type_EXTERNALBOOK: $this->_readExternalBook(); break;
  539. case self::XLS_Type_EXTERNNAME: $this->_readExternName(); break;
  540. case self::XLS_Type_EXTERNSHEET: $this->_readExternSheet(); break;
  541. case self::XLS_Type_DEFINEDNAME: $this->_readDefinedName(); break;
  542. case self::XLS_Type_MSODRAWINGGROUP: $this->_readMsoDrawingGroup(); break;
  543. case self::XLS_Type_SST: $this->_readSst(); break;
  544. case self::XLS_Type_EOF: $this->_readDefault(); break 2;
  545. default: $this->_readDefault(); break;
  546. }
  547. }
  548. // Resolve indexed colors for font, fill, and border colors
  549. // Cannot be resolved already in XF record, because PALETTE record comes afterwards
  550. if (!$this->_readDataOnly) {
  551. foreach ($this->_objFonts as $objFont) {
  552. if (isset($objFont->colorIndex)) {
  553. $color = self::_readColor($objFont->colorIndex,$this->_palette,$this->_version);
  554. $objFont->getColor()->setRGB($color['rgb']);
  555. }
  556. }
  557. foreach ($this->_phpExcel->getCellXfCollection() as $objStyle) {
  558. // fill start and end color
  559. $fill = $objStyle->getFill();
  560. if (isset($fill->startcolorIndex)) {
  561. $startColor = self::_readColor($fill->startcolorIndex,$this->_palette,$this->_version);
  562. $fill->getStartColor()->setRGB($startColor['rgb']);
  563. }
  564. if (isset($fill->endcolorIndex)) {
  565. $endColor = self::_readColor($fill->endcolorIndex,$this->_palette,$this->_version);
  566. $fill->getEndColor()->setRGB($endColor['rgb']);
  567. }
  568. // border colors
  569. $top = $objStyle->getBorders()->getTop();
  570. $right = $objStyle->getBorders()->getRight();
  571. $bottom = $objStyle->getBorders()->getBottom();
  572. $left = $objStyle->getBorders()->getLeft();
  573. $diagonal = $objStyle->getBorders()->getDiagonal();
  574. if (isset($top->colorIndex)) {
  575. $borderTopColor = self::_readColor($top->colorIndex,$this->_palette,$this->_version);
  576. $top->getColor()->setRGB($borderTopColor['rgb']);
  577. }
  578. if (isset($right->colorIndex)) {
  579. $borderRightColor = self::_readColor($right->colorIndex,$this->_palette,$this->_version);
  580. $right->getColor()->setRGB($borderRightColor['rgb']);
  581. }
  582. if (isset($bottom->colorIndex)) {
  583. $borderBottomColor = self::_readColor($bottom->colorIndex,$this->_palette,$this->_version);
  584. $bottom->getColor()->setRGB($borderBottomColor['rgb']);
  585. }
  586. if (isset($left->colorIndex)) {
  587. $borderLeftColor = self::_readColor($left->colorIndex,$this->_palette,$this->_version);
  588. $left->getColor()->setRGB($borderLeftColor['rgb']);
  589. }
  590. if (isset($diagonal->colorIndex)) {
  591. $borderDiagonalColor = self::_readColor($diagonal->colorIndex,$this->_palette,$this->_version);
  592. $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
  593. }
  594. }
  595. }
  596. // treat MSODRAWINGGROUP records, workbook-level Escher
  597. if (!$this->_readDataOnly && $this->_drawingGroupData) {
  598. $escherWorkbook = new PHPExcel_Shared_Escher();
  599. $reader = new PHPExcel_Reader_Excel5_Escher($escherWorkbook);
  600. $escherWorkbook = $reader->load($this->_drawingGroupData);
  601. // debug Escher stream
  602. //$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
  603. //$debug->load($this->_drawingGroupData);
  604. }
  605. // Parse the individual sheets
  606. foreach ($this->_sheets as $sheet) {
  607. if ($sheet['sheetType'] != 0x00) {
  608. // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
  609. continue;
  610. }
  611. // check if sheet should be skipped
  612. if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) {
  613. continue;
  614. }
  615. // add sheet to PHPExcel object
  616. $this->_phpSheet = $this->_phpExcel->createSheet();
  617. // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
  618. // cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
  619. // name in line with the formula, not the reverse
  620. $this->_phpSheet->setTitle($sheet['name'],false);
  621. $this->_phpSheet->setSheetState($sheet['sheetState']);
  622. $this->_pos = $sheet['offset'];
  623. // Initialize isFitToPages. May change after reading SHEETPR record.
  624. $this->_isFitToPages = false;
  625. // Initialize drawingData
  626. $this->_drawingData = '';
  627. // Initialize objs
  628. $this->_objs = array();
  629. // Initialize shared formula parts
  630. $this->_sharedFormulaParts = array();
  631. // Initialize shared formulas
  632. $this->_sharedFormulas = array();
  633. // Initialize text objs
  634. $this->_textObjects = array();
  635. // Initialize cell annotations
  636. $this->_cellNotes = array();
  637. $this->textObjRef = -1;
  638. while ($this->_pos <= $this->_dataSize - 4) {
  639. $code = self::_GetInt2d($this->_data, $this->_pos);
  640. switch ($code) {
  641. case self::XLS_Type_BOF: $this->_readBof(); break;
  642. case self::XLS_Type_PRINTGRIDLINES: $this->_readPrintGridlines(); break;
  643. case self::XLS_Type_DEFAULTROWHEIGHT: $this->_readDefaultRowHeight(); break;
  644. case self::XLS_Type_SHEETPR: $this->_readSheetPr(); break;
  645. case self::XLS_Type_HORIZONTALPAGEBREAKS: $this->_readHorizontalPageBreaks(); break;
  646. case self::XLS_Type_VERTICALPAGEBREAKS: $this->_readVerticalPageBreaks(); break;
  647. case self::XLS_Type_HEADER: $this->_readHeader(); break;
  648. case self::XLS_Type_FOOTER: $this->_readFooter(); break;
  649. case self::XLS_Type_HCENTER: $this->_readHcenter(); break;
  650. case self::XLS_Type_VCENTER: $this->_readVcenter(); break;
  651. case self::XLS_Type_LEFTMARGIN: $this->_readLeftMargin(); break;
  652. case self::XLS_Type_RIGHTMARGIN: $this->_readRightMargin(); break;
  653. case self::XLS_Type_TOPMARGIN: $this->_readTopMargin(); break;
  654. case self::XLS_Type_BOTTOMMARGIN: $this->_readBottomMargin(); break;
  655. case self::XLS_Type_PAGESETUP: $this->_readPageSetup(); break;
  656. case self::XLS_Type_PROTECT: $this->_readProtect(); break;
  657. case self::XLS_Type_SCENPROTECT: $this->_readScenProtect(); break;
  658. case self::XLS_Type_OBJECTPROTECT: $this->_readObjectProtect(); break;
  659. case self::XLS_Type_PASSWORD: $this->_readPassword(); break;
  660. case self::XLS_Type_DEFCOLWIDTH: $this->_readDefColWidth(); break;
  661. case self::XLS_Type_COLINFO: $this->_readColInfo(); break;
  662. case self::XLS_Type_DIMENSION: $this->_readDefault(); break;
  663. case self::XLS_Type_ROW: $this->_readRow(); break;
  664. case self::XLS_Type_DBCELL: $this->_readDefault(); break;
  665. case self::XLS_Type_RK: $this->_readRk(); break;
  666. case self::XLS_Type_LABELSST: $this->_readLabelSst(); break;
  667. case self::XLS_Type_MULRK: $this->_readMulRk(); break;
  668. case self::XLS_Type_NUMBER: $this->_readNumber(); break;
  669. case self::XLS_Type_FORMULA: $this->_readFormula(); break;
  670. case self::XLS_Type_SHAREDFMLA: $this->_readSharedFmla(); break;
  671. case self::XLS_Type_BOOLERR: $this->_readBoolErr(); break;
  672. case self::XLS_Type_MULBLANK: $this->_readMulBlank(); break;
  673. case self::XLS_Type_LABEL: $this->_readLabel(); break;
  674. case self::XLS_Type_BLANK: $this->_readBlank(); break;
  675. case self::XLS_Type_MSODRAWING: $this->_readMsoDrawing(); break;
  676. case self::XLS_Type_OBJ: $this->_readObj(); break;
  677. case self::XLS_Type_WINDOW2: $this->_readWindow2(); break;
  678. case self::XLS_Type_PAGELAYOUTVIEW: $this->_readPageLayoutView(); break;
  679. case self::XLS_Type_SCL: $this->_readScl(); break;
  680. case self::XLS_Type_PANE: $this->_readPane(); break;
  681. case self::XLS_Type_SELECTION: $this->_readSelection(); break;
  682. case self::XLS_Type_MERGEDCELLS: $this->_readMergedCells(); break;
  683. case self::XLS_Type_HYPERLINK: $this->_readHyperLink(); break;
  684. case self::XLS_Type_DATAVALIDATIONS: $this->_readDataValidations(); break;
  685. case self::XLS_Type_DATAVALIDATION: $this->_readDataValidation(); break;
  686. case self::XLS_Type_SHEETLAYOUT: $this->_readSheetLayout(); break;
  687. case self::XLS_Type_SHEETPROTECTION: $this->_readSheetProtection(); break;
  688. case self::XLS_Type_RANGEPROTECTION: $this->_readRangeProtection(); break;
  689. case self::XLS_Type_NOTE: $this->_readNote(); break;
  690. //case self::XLS_Type_IMDATA: $this->_readImData(); break;
  691. case self::XLS_Type_TXO: $this->_readTextObject(); break;
  692. case self::XLS_Type_CONTINUE: $this->_readContinue(); break;
  693. case self::XLS_Type_EOF: $this->_readDefault(); break 2;
  694. default: $this->_readDefault(); break;
  695. }
  696. }
  697. // treat MSODRAWING records, sheet-level Escher
  698. if (!$this->_readDataOnly && $this->_drawingData) {
  699. $escherWorksheet = new PHPExcel_Shared_Escher();
  700. $reader = new PHPExcel_Reader_Excel5_Escher($escherWorksheet);
  701. $escherWorksheet = $reader->load($this->_drawingData);
  702. // debug Escher stream
  703. //$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
  704. //$debug->load($this->_drawingData);
  705. // get all spContainers in one long array, so they can be mapped to OBJ records
  706. $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
  707. }
  708. // treat OBJ records
  709. foreach ($this->_objs as $n => $obj) {
  710. // echo '<hr /><b>Object</b> reference is ',$n,'<br />';
  711. // var_dump($obj);
  712. // echo '<br />';
  713. // the first shape container never has a corresponding OBJ record, hence $n + 1
  714. if (isset($allSpContainers[$n + 1]) && is_object($allSpContainers[$n + 1])) {
  715. $spContainer = $allSpContainers[$n + 1];
  716. // we skip all spContainers that are a part of a group shape since we cannot yet handle those
  717. if ($spContainer->getNestingLevel() > 1) {
  718. continue;
  719. }
  720. // calculate the width and height of the shape
  721. list($startColumn, $startRow) = PHPExcel_Cell::coordinateFromString($spContainer->getStartCoordinates());
  722. list($endColumn, $endRow) = PHPExcel_Cell::coordinateFromString($spContainer->getEndCoordinates());
  723. $startOffsetX = $spContainer->getStartOffsetX();
  724. $startOffsetY = $spContainer->getStartOffsetY();
  725. $endOffsetX = $spContainer->getEndOffsetX();
  726. $endOffsetY = $spContainer->getEndOffsetY();
  727. $width = PHPExcel_Shared_Excel5::getDistanceX($this->_phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
  728. $height = PHPExcel_Shared_Excel5::getDistanceY($this->_phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
  729. // calculate offsetX and offsetY of the shape
  730. $offsetX = $startOffsetX * PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, $startColumn) / 1024;
  731. $offsetY = $startOffsetY * PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $startRow) / 256;
  732. switch ($obj['otObjType']) {
  733. case 0x19:
  734. // Note
  735. // echo 'Cell Annotation Object<br />';
  736. // echo 'Object ID is ',$obj['idObjID'],'<br />';
  737. //
  738. if (isset($this->_cellNotes[$obj['idObjID']])) {
  739. $cellNote = $this->_cellNotes[$obj['idObjID']];
  740. if (isset($this->_textObjects[$obj['idObjID']])) {
  741. $textObject = $this->_textObjects[$obj['idObjID']];
  742. $this->_cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
  743. }
  744. }
  745. break;
  746. case 0x08:
  747. // echo 'Picture Object<br />';
  748. // picture
  749. // get index to BSE entry (1-based)
  750. $BSEindex = $spContainer->getOPT(0x0104);
  751. $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
  752. $BSE = $BSECollection[$BSEindex - 1];
  753. $blipType = $BSE->getBlipType();
  754. // need check because some blip types are not supported by Escher reader such as EMF
  755. if ($blip = $BSE->getBlip()) {
  756. $ih = imagecreatefromstring($blip->getData());
  757. $drawing = new PHPExcel_Worksheet_MemoryDrawing();
  758. $drawing->setImageResource($ih);
  759. // width, height, offsetX, offsetY
  760. $drawing->setResizeProportional(false);
  761. $drawing->setWidth($width);
  762. $drawing->setHeight($height);
  763. $drawing->setOffsetX($offsetX);
  764. $drawing->setOffsetY($offsetY);
  765. switch ($blipType) {
  766. case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG:
  767. $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG);
  768. $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_JPEG);
  769. break;
  770. case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG:
  771. $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG);
  772. $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_PNG);
  773. break;
  774. }
  775. $drawing->setWorksheet($this->_phpSheet);
  776. $drawing->setCoordinates($spContainer->getStartCoordinates());
  777. }
  778. break;
  779. default:
  780. // other object type
  781. break;
  782. }
  783. }
  784. }
  785. // treat SHAREDFMLA records
  786. if ($this->_version == self::XLS_BIFF8) {
  787. foreach ($this->_sharedFormulaParts as $cell => $baseCell) {
  788. list($column, $row) = PHPExcel_Cell::coordinateFromString($cell);
  789. if (($this->getReadFilter() !== NULL) && $this->getReadFilter()->readCell($column, $row, $this->_phpSheet->getTitle()) ) {
  790. $formula = $this->_getFormulaFromStructure($this->_sharedFormulas[$baseCell], $cell);
  791. $this->_phpSheet->getCell($cell)->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA);
  792. }
  793. }
  794. }
  795. if (!empty($this->_cellNotes)) {
  796. foreach($this->_cellNotes as $note => $noteDetails) {
  797. if (!isset($noteDetails['objTextData'])) {
  798. if (isset($this->_textObjects[$note])) {
  799. $textObject = $this->_textObjects[$note];
  800. $noteDetails['objTextData'] = $textObject;
  801. } else {
  802. $noteDetails['objTextData']['text'] = '';
  803. }
  804. }
  805. // echo '<b>Cell annotation ',$note,'</b><br />';
  806. // var_dump($noteDetails);
  807. // echo '<br />';
  808. $cellAddress = str_replace('$','',$noteDetails['cellRef']);
  809. $this->_phpSheet->getComment( $cellAddress )
  810. ->setAuthor( $noteDetails['author'] )
  811. ->setText($this->_parseRichText($noteDetails['objTextData']['text']) );
  812. }
  813. }
  814. }
  815. // add the named ranges (defined names)
  816. foreach ($this->_definedname as $definedName) {
  817. if ($definedName['isBuiltInName']) {
  818. switch ($definedName['name']) {
  819. case pack('C', 0x06):
  820. // print area
  821. // in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
  822. $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
  823. $extractedRanges = array();
  824. foreach ($ranges as $range) {
  825. // $range should look like one of these
  826. // Foo!$C$7:$J$66
  827. // Bar!$A$1:$IV$2
  828. $explodes = explode('!', $range); // FIXME: what if sheetname contains exclamation mark?
  829. $sheetName = trim($explodes[0], "'");
  830. if (count($explodes) == 2) {
  831. if (strpos($explodes[1], ':') === FALSE) {
  832. $explodes[1] = $explodes[1] . ':' . $explodes[1];
  833. }
  834. $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
  835. }
  836. }
  837. if ($docSheet = $this->_phpExcel->getSheetByName($sheetName)) {
  838. $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
  839. }
  840. break;
  841. case pack('C', 0x07):
  842. // print titles (repeating rows)
  843. // Assuming BIFF8, there are 3 cases
  844. // 1. repeating rows
  845. // formula looks like this: Sheet!$A$1:$IV$2
  846. // rows 1-2 repeat
  847. // 2. repeating columns
  848. // formula looks like this: Sheet!$A$1:$B$65536
  849. // columns A-B repeat
  850. // 3. both repeating rows and repeating columns
  851. // formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
  852. $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
  853. foreach ($ranges as $range) {
  854. // $range should look like this one of these
  855. // Sheet!$A$1:$B$65536
  856. // Sheet!$A$1:$IV$2
  857. $explodes = explode('!', $range);
  858. if (count($explodes) == 2) {
  859. if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) {
  860. $extractedRange = $explodes[1];
  861. $extractedRange = str_replace('$', '', $extractedRange);
  862. $coordinateStrings = explode(':', $extractedRange);
  863. if (count($coordinateStrings) == 2) {
  864. list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[0]);
  865. list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[1]);
  866. if ($firstColumn == 'A' and $lastColumn == 'IV') {
  867. // then we have repeating rows
  868. $docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow, $lastRow));
  869. } elseif ($firstRow == 1 and $lastRow == 65536) {
  870. // then we have repeating columns
  871. $docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn, $lastColumn));
  872. }
  873. }
  874. }
  875. }
  876. }
  877. break;
  878. }
  879. } else {
  880. // Extract range
  881. $explodes = explode('!', $definedName['formula']);
  882. if (count($explodes) == 2) {
  883. if (($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) ||
  884. ($docSheet = $this->_phpExcel->getSheetByName(trim($explodes[0],"'")))) {
  885. $extractedRange = $explodes[1];
  886. $extractedRange = str_replace('$', '', $extractedRange);
  887. $localOnly = ($definedName['scope'] == 0) ? false : true;
  888. $scope = ($definedName['scope'] == 0) ?
  889. null : $this->_phpExcel->getSheetByName($this->_sheets[$definedName['scope'] - 1]['name']);
  890. $this->_phpExcel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $docSheet, $extractedRange, $localOnly, $scope) );
  891. }
  892. } else {
  893. // Named Value
  894. // TODO Provide support for named values
  895. }
  896. }
  897. }
  898. return $this->_phpExcel;
  899. }
  900. /**
  901. * Use OLE reader to extract the relevant data streams from the OLE file
  902. *
  903. * @param string $pFilename
  904. */
  905. private function _loadOLE($pFilename)
  906. {
  907. // OLE reader
  908. $ole = new PHPExcel_Shared_OLERead();
  909. // get excel data,
  910. $res = $ole->read($pFilename);
  911. // Get workbook data: workbook stream + sheet streams
  912. $this->_data = $ole->getStream($ole->wrkbook);
  913. // Get summary information data
  914. $this->_summaryInformation = $ole->getStream($ole->summaryInformation);
  915. // Get additional document summary information data
  916. $this->_documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation);
  917. // Get user-defined property data
  918. // $this->_userDefinedProperties = $ole->getUserDefinedProperties();
  919. }
  920. /**
  921. * Read summary information
  922. */
  923. private function _readSummaryInformation()
  924. {
  925. if (!isset($this->_summaryInformation)) {
  926. return;
  927. }
  928. // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
  929. // offset: 2; size: 2;
  930. // offset: 4; size: 2; OS version
  931. // offset: 6; size: 2; OS indicator
  932. // offset: 8; size: 16
  933. // offset: 24; size: 4; section count
  934. $secCount = self::_GetInt4d($this->_summaryInformation, 24);
  935. // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
  936. // offset: 44; size: 4
  937. $secOffset = self::_GetInt4d($this->_summaryInformation, 44);
  938. // section header
  939. // offset: $secOffset; size: 4; section length
  940. $secLength = self::_GetInt4d($this->_summaryInformation, $secOffset);
  941. // offset: $secOffset+4; size: 4; property count
  942. $countProperties = self::_GetInt4d($this->_summaryInformation, $secOffset+4);
  943. // initialize code page (used to resolve string values)
  944. $codePage = 'CP1252';
  945. // offset: ($secOffset+8); size: var
  946. // loop through property decarations and properties
  947. for ($i = 0; $i < $countProperties; ++$i) {
  948. // offset: ($secOffset+8) + (8 * $i); size: 4; property ID
  949. $id = self::_GetInt4d($this->_summaryInformation, ($secOffset+8) + (8 * $i));
  950. // Use value of property id as appropriate
  951. // offset: ($secOffset+12) + (8 * $i); size: 4; offset from beginning of section (48)
  952. $offset = self::_GetInt4d($this->_summaryInformation, ($secOffset+12) + (8 * $i));
  953. $type = self::_GetInt4d($this->_summaryInformation, $secOffset + $offset);
  954. // initialize property value
  955. $value = null;
  956. // extract property value based on property type
  957. switch ($type) {
  958. case 0x02: // 2 byte signed integer
  959. $value = self::_GetInt2d($this->_summaryInformation, $secOffset + 4 + $offset);
  960. break;
  961. case 0x03: // 4 byte signed integer
  962. $value = self::_GetInt4d($this->_summaryInformation, $secOffset + 4 + $offset);
  963. break;
  964. case 0x13: // 4 byte unsigned integer
  965. // not needed yet, fix later if necessary
  966. break;
  967. case 0x1E: // null-terminated string prepended by dword string length
  968. $byteLength = self::_GetInt4d($this->_summaryInformation, $secOffset + 4 + $offset);
  969. $value = substr($this->_summaryInformation, $secOffset + 8 + $offset, $byteLength);
  970. $value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage);
  971. $value = rtrim($value);
  972. break;
  973. case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
  974. // PHP-time
  975. $value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_summaryInformation, $secOffset + 4 + $offset, 8));
  976. break;
  977. case 0x47: // Clipboard format
  978. // not needed yet, fix later if necessary
  979. break;
  980. }
  981. switch ($id) {
  982. case 0x01: // Code Page
  983. $codePage = PHPExcel_Shared_CodePage::NumberToName($value);
  984. break;
  985. case 0x02: // Title
  986. $this->_phpExcel->getProperties()->setTitle($value);
  987. break;
  988. case 0x03: // Subject
  989. $this->_phpExcel->getProperties()->setSubject($value);
  990. break;
  991. case 0x04: // Author (Creator)
  992. $this->_phpExcel->getProperties()->setCreator($value);
  993. break;
  994. case 0x05: // Keywords
  995. $this->_phpExcel->getProperties()->setKeywords($value);
  996. break;
  997. case 0x06: // Comments (Description)
  998. $this->_phpExcel->getProperties()->setDescription($value);
  999. break;
  1000. case 0x07: // Template
  1001. // Not supported by PHPExcel
  1002. break;
  1003. case 0x08: // Last Saved By (LastModifiedBy)
  1004. $this->_phpExcel->getProperties()->setLastModifiedBy($value);
  1005. break;
  1006. case 0x09: // Revision
  1007. // Not supported by PHPExcel
  1008. break;
  1009. case 0x0A: // Total Editing Time
  1010. // Not supported by PHPExcel
  1011. break;
  1012. case 0x0B: // Last Printed
  1013. // Not supported by PHPExcel
  1014. break;
  1015. case 0x0C: // Created Date/Time
  1016. $this->_phpExcel->getProperties()->setCreated($value);
  1017. break;
  1018. case 0x0D: // Modified Date/Time
  1019. $this->_phpExcel->getProperties()->setModified($value);
  1020. break;
  1021. case 0x0E: // Number of Pages
  1022. // Not supported by PHPExcel
  1023. break;
  1024. case 0x0F: // Number of Words
  1025. // Not supported by PHPExcel
  1026. break;
  1027. case 0x10: // Number of Characters
  1028. // Not supported by PHPExcel
  1029. break;
  1030. case 0x11: // Thumbnail
  1031. // Not supported by PHPExcel
  1032. break;
  1033. case 0x12: // Name of creating application
  1034. // Not supported by PHPExcel
  1035. break;
  1036. case 0x13: // Security
  1037. // Not supported by PHPExcel
  1038. break;
  1039. }
  1040. }
  1041. }
  1042. /**
  1043. * Read additional document summary information
  1044. */
  1045. private function _readDocumentSummaryInformation()
  1046. {
  1047. if (!isset($this->_documentSummaryInformation)) {
  1048. return;
  1049. }
  1050. // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
  1051. // offset: 2; size: 2;
  1052. // offset: 4; size: 2; OS version
  1053. // offset: 6; size: 2; OS indicator
  1054. // offset: 8; size: 16
  1055. // offset: 24; size: 4; section count
  1056. $secCount = self::_GetInt4d($this->_documentSummaryInformation, 24);
  1057. // echo '$secCount = ',$secCount,'<br />';
  1058. // offset: 28; size: 16; first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
  1059. // offset: 44; size: 4; first section offset
  1060. $secOffset = self::_GetInt4d($this->_documentSummaryInformation, 44);
  1061. // echo '$secOffset = ',$secOffset,'<br />';
  1062. // section header
  1063. // offset: $secOffset; size: 4; section length
  1064. $secLength = self::_GetInt4d($this->_documentSummaryInformation, $secOffset);
  1065. // echo '$secLength = ',$secLength,'<br />';
  1066. // offset: $secOffset+4; size: 4; property count
  1067. $countProperties = self::_GetInt4d($this->_documentSummaryInformation, $secOffset+4);
  1068. // echo '$countProperties = ',$countProperties,'<br />';
  1069. // initialize code page (used to resolve string values)
  1070. $codePage = 'CP1252';
  1071. // offset: ($secOffset+8); size: var
  1072. // loop through property decarations and properties
  1073. for ($i = 0; $i < $countProperties; ++$i) {
  1074. // echo 'Property ',$i,'<br />';
  1075. // offset: ($secOffset+8) + (8 * $i); size: 4; property ID
  1076. $id = self::_GetInt4d($this->_documentSummaryInformation, ($secOffset+8) + (8 * $i));
  1077. // echo 'ID is ',$id,'<br />';
  1078. // Use value of property id as appropriate
  1079. // offset: 60 + 8 * $i; size: 4; offset from beginning of section (48)
  1080. $offset = self::_GetInt4d($this->_documentSummaryInformation, ($secOffset+12) + (8 * $i));
  1081. $type = self::_GetInt4d($this->_documentSummaryInformation, $secOffset + $offset);
  1082. // echo 'Type is ',$type,', ';
  1083. // initialize property value
  1084. $value = null;
  1085. // extract property value based on property type
  1086. switch ($type) {
  1087. case 0x02: // 2 byte signed integer
  1088. $value = self::_GetInt2d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
  1089. break;
  1090. case 0x03: // 4 byte signed integer
  1091. $value = self::_GetInt4d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
  1092. break;
  1093. case 0x0B: // Boolean
  1094. $value = self::_GetInt2d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
  1095. $value = ($value == 0 ? false : true);
  1096. break;
  1097. case 0x13: // 4 byte unsigned integer
  1098. // not needed yet, fix later if necessary
  1099. break;
  1100. case 0x1E: // null-terminated string prepended by dword string length
  1101. $byteLength = self::_GetInt4d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
  1102. $value = substr($this->_documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
  1103. $value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage);
  1104. $value = rtrim($value);
  1105. break;
  1106. case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
  1107. // PHP-Time
  1108. $value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_documentSummaryInformation, $secOffset + 4 + $offset, 8));
  1109. break;
  1110. case 0x47: // Clipboard format
  1111. // not needed yet, fix later if necessary
  1112. break;
  1113. }
  1114. switch ($id) {
  1115. case 0x01: // Code Page
  1116. $codePage = PHPExcel_Shared_CodePage::NumberToName($value);
  1117. break;
  1118. case 0x02: // Category
  1119. $this->_phpExcel->getProperties()->setCategory($value);
  1120. break;
  1121. case 0x03: // Presentation Target
  1122. // Not supported by PHPExcel
  1123. break;
  1124. case 0x04: // Bytes
  1125. // Not supported by PHPExcel
  1126. break;
  1127. case 0x05: // Lines
  1128. // Not supported by PHPExcel
  1129. break;
  1130. case 0x06: // Paragraphs
  1131. // Not supported by PHPExcel
  1132. break;
  1133. case 0x07: // Slides
  1134. // Not supported by PHPExcel
  1135. break;
  1136. case 0x08: // Notes
  1137. // Not supported by PHPExcel
  1138. break;
  1139. case 0x09: // Hidden Slides
  1140. // Not supported by PHPExcel
  1141. break;
  1142. case 0x0A: // MM Clips
  1143. // Not supported by PHPExcel
  1144. break;
  1145. case 0x0B: // Scale Crop
  1146. // Not supported by PHPExcel
  1147. break;
  1148. case 0x0C: // Heading Pairs
  1149. // Not supported by PHPExcel
  1150. break;
  1151. case 0x0D: // Titles of Parts
  1152. // Not supported by PHPExcel
  1153. break;
  1154. case 0x0E: // Manager
  1155. $this->_phpExcel->getProperties()->setManager($value);
  1156. break;
  1157. case 0x0F: // Company
  1158. $this->_phpExcel->getProperties()->setCompany($value);
  1159. break;
  1160. case 0x10: // Links up-to-date
  1161. // Not supported by PHPExcel
  1162. break;
  1163. }
  1164. }
  1165. }
  1166. /**
  1167. * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
  1168. */
  1169. private function _readDefault()
  1170. {
  1171. $length = self::_GetInt2d($this->_data, $this->_pos + 2);
  1172. // $recordData = substr($this->_data, $this->_pos + 4, $length);
  1173. // move stream pointer to next record
  1174. $this->_pos += 4 + $length;
  1175. }
  1176. /**
  1177. * The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
  1178. * this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
  1179. */
  1180. private function _readNote()
  1181. {
  1182. // echo '<b>Read Cell Annotation</b><br />';
  1183. $length = self::_GetInt2d($this->_data, $this->_pos + 2);
  1184. $recordData = substr($this->_data, $this->_pos + 4, $length);
  1185. // move stream pointer to next record
  1186. $this->_pos += 4 + $length;
  1187. if ($this->_readDataOnly) {
  1188. return;
  1189. }
  1190. $cellAddress = $this->_readBIFF8CellAddress(substr($recordData, 0, 4));
  1191. if ($this->_version == self::XLS_BIFF8) {
  1192. $noteObjID = self::_GetInt2d($recordData, 6);
  1193. $noteAuthor = self::_readUnicodeStringLong(substr($recordData, 8));
  1194. $noteAuthor = $noteAuthor['value'];
  1195. // echo 'Note Address=',$cellAddress,'<br />';
  1196. // echo 'Note Object ID=',$noteObjID,'<br />';
  1197. // echo 'Note Author=',$noteAuthor,'<hr />';
  1198. //
  1199. $this->_cellNotes[$noteObjID] = array('cellRef' => $cellAddress,
  1200. 'objectID' => $noteObjID,
  1201. 'author' => $noteAuthor
  1202. );
  1203. } else {
  1204. $extension = false;
  1205. if ($cellAddress == '$B$65536') {
  1206. // If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
  1207. // note from the previous cell annotation. We're not yet handling this, so annotations longer than the
  1208. // max 2048 bytes will probably throw a wobbly.
  1209. $row = self::_GetInt2d($recordData, 0);
  1210. $extension = true;
  1211. $cellAddress = array_pop(array_keys($this->_phpSheet->getComments()));
  1212. }
  1213. // echo 'Note Address=',$cellAddress,'<br />';
  1214. $cellAddress = str_replace('$','',$cellAddress);
  1215. $noteLength = self::_GetInt2d($recordData, 4);
  1216. $noteText = trim(substr($recordData, 6));
  1217. // echo 'Note Length=',$noteLength,'<br />';
  1218. // echo 'Note Text=',$noteText,'<br />';
  1219. if ($extension) {
  1220. // Concatenate this extension with the currently set comment for the cell
  1221. $comment = $this->_phpSheet->getComment( $cellAddress );
  1222. $commentText = $comment->getText()->getPlainText();
  1223. $comment->setText($this->_parseRichText($commentText.$noteText) );
  1224. } else {
  1225. // Set comment for the cell
  1226. $this->_phpSheet->getComment( $cellAddress )
  1227. // ->setAuthor( $author )
  1228. ->setText($this->_parseRichText($noteText) );
  1229. }
  1230. }
  1231. }
  1232. /**
  1233. * The TEXT Object record contains the text associated with a cell annotation.
  1234. */
  1235. private function _readTextObject()
  1236. {
  1237. $length = self::_GetInt2d($this->_data, $this->_pos + 2);
  1238. $recordData = substr($this->_data, $this->_pos + 4, $length);
  1239. // move stream pointer to next record
  1240. $this->_pos += 4 + $length;
  1241. if ($this->_readDataOnly) {
  1242. return;
  1243. }
  1244. // recordData consists of an array of subrecords looking like this:
  1245. // grbit: 2 bytes; Option Flags
  1246. // rot: 2 bytes; rotation
  1247. // cchText: 2 bytes; length of the text (in the first continue record)
  1248. // cbRuns: 2 bytes; length of the formatting (in the second continue record)
  1249. // followed by the continuation records containing the actual text and formatting
  1250. $grbitOpts = self::_GetInt2d($recordData, 0);
  1251. $rot = self::_GetInt2d($recordData, 2);
  1252. $cchText = self::_GetInt2d($recordData, 10);
  1253. $cbRuns = self::_GetInt2d($recordData, 12);
  1254. $text = $this->_getSplicedRecordData();
  1255. $this->_textObjects[$this->textObjRef] = array(
  1256. 'text' => substr($text["recordData"],$text["spliceOffsets"][0]+1,$cchText),
  1257. 'format' => substr($text["recordData"],$text["spliceOffsets"][1],$cbRuns),
  1258. 'alignment' => $grbitOpts,
  1259. 'rotation' => $rot
  1260. );
  1261. // echo '<b>_readTextObject()</b><br />';
  1262. // var_dump($this->_textObjects[$this->textObjRef]);
  1263. // echo '<br />';
  1264. }
  1265. /**
  1266. * Read BOF
  1267. */
  1268. private function _readBof()
  1269. {
  1270. $length = self::_GetInt2d($this->_data, $this->_pos + 2);
  1271. $recordData = substr($this->_data, $this->_pos + 4, $length);
  1272. // move stream pointer to next record
  1273. $this->_pos += 4 + $length;
  1274. // offset: 2; size: 2; type of the following data
  1275. $substreamType = self::_GetInt2d($recordData, 2);
  1276. switch ($substreamType) {
  1277. case self::XLS_WorkbookGlobals:
  1278. $version = self::_GetInt2d($recordData, 0);
  1279. if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
  1280. throw new PHPExcel_Reader_Exception('Cannot read this Excel file. Version is too old.');
  1281. }
  1282. $this->_version = $version;
  1283. break;
  1284. case self::XLS_Worksheet:
  1285. // do not use this version information for anything
  1286. // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
  1287. break;
  1288. default:
  1289. // substream, e.g. chart
  1290. // just skip the entire substream
  1291. do {
  1292. $code = self::_GetInt2d($this->_data, $t

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