PageRenderTime 58ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 2ms

/heart/protected/vendor/phpexcel/Classes/PHPExcel/Reader/Excel5.php

https://github.com/wawancell/yiiheart
PHP | 7084 lines | 4007 code | 1175 blank | 1902 comment | 522 complexity | 4704228db596fb20e7be1da80ab367c7 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception

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

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