PageRenderTime 71ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 2ms

/Classes/PHPExcel/Reader/Excel5.php

https://bitbucket.org/amorello/changeover
PHP | 6890 lines | 3878 code | 1141 blank | 1871 comment | 502 complexity | 6aafac239786f82d527d72cb8c0222e4 MD5 | raw file

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

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