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

/protected/libs/phpexcel/Classes/PHPExcel/Reader/Excel5.php

https://bitbucket.org/graaaf/erso
PHP | 6667 lines | 3767 code | 1018 blank | 1882 comment | 492 complexity | 1bc391fe75066fa582fcf2c0d4fefa53 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0, LGPL-2.1, BSD-3-Clause, BSD-2-Clause

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 - 2011 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 - 2011 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version 1.7.6, 2011-02-27
  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 - 2011 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. * Read data only?
  365. * If this is true, then the Reader will only read data values for cells, it will not read any formatting information.
  366. * If false (the default) it will read data and formatting.
  367. *
  368. * @return boolean
  369. */
  370. public function getReadDataOnly()
  371. {
  372. return $this->_readDataOnly;
  373. }
  374. /**
  375. * Set read data only
  376. * Set to true, to advise the Reader only to read data values for cells, and to ignore any formatting information.
  377. * Set to false (the default) to advise the Reader to read both data and formatting for cells.
  378. *
  379. * @param boolean $pValue
  380. *
  381. * @return PHPExcel_Reader_Excel5
  382. */
  383. public function setReadDataOnly($pValue = false)
  384. {
  385. $this->_readDataOnly = $pValue;
  386. return $this;
  387. }
  388. /**
  389. * Get which sheets to load
  390. * Returns either an array of worksheet names (the list of worksheets that should be loaded), or a null
  391. * indicating that all worksheets in the workbook should be loaded.
  392. *
  393. * @return mixed
  394. */
  395. public function getLoadSheetsOnly()
  396. {
  397. return $this->_loadSheetsOnly;
  398. }
  399. /**
  400. * Set which sheets to load
  401. *
  402. * @param mixed $value
  403. * This should be either an array of worksheet names to be loaded, or a string containing a single worksheet name.
  404. * If NULL, then it tells the Reader to read all worksheets in the workbook
  405. *
  406. * @return PHPExcel_Reader_Excel5
  407. */
  408. public function setLoadSheetsOnly($value = null)
  409. {
  410. $this->_loadSheetsOnly = is_array($value) ?
  411. $value : array($value);
  412. return $this;
  413. }
  414. /**
  415. * Set all sheets to load
  416. * Tells the Reader to load all worksheets from the workbook.
  417. *
  418. * @return PHPExcel_Reader_Excel5
  419. */
  420. public function setLoadAllSheets()
  421. {
  422. $this->_loadSheetsOnly = null;
  423. return $this;
  424. }
  425. /**
  426. * Read filter
  427. *
  428. * @return PHPExcel_Reader_IReadFilter
  429. */
  430. public function getReadFilter() {
  431. return $this->_readFilter;
  432. }
  433. /**
  434. * Set read filter
  435. *
  436. * @param PHPExcel_Reader_IReadFilter $pValue
  437. * @return PHPExcel_Reader_Excel5
  438. */
  439. public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
  440. $this->_readFilter = $pValue;
  441. return $this;
  442. }
  443. /**
  444. * Create a new PHPExcel_Reader_Excel5 instance
  445. */
  446. public function __construct() {
  447. $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
  448. }
  449. /**
  450. * Can the current PHPExcel_Reader_IReader read the file?
  451. *
  452. * @param string $pFileName
  453. * @return boolean
  454. */
  455. public function canRead($pFilename)
  456. {
  457. // Check if file exists
  458. if (!file_exists($pFilename)) {
  459. throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
  460. }
  461. try {
  462. // Use ParseXL for the hard work.
  463. $ole = new PHPExcel_Shared_OLERead();
  464. // get excel data
  465. $res = $ole->read($pFilename);
  466. return true;
  467. } catch (Exception $e) {
  468. return false;
  469. }
  470. }
  471. /**
  472. * Reads names of the worksheets from a file, without parsing the whole file to a PHPExcel object
  473. *
  474. * @param string $pFilename
  475. * @throws Exception
  476. */
  477. public function listWorksheetNames($pFilename)
  478. {
  479. // Check if file exists
  480. if (!file_exists($pFilename)) {
  481. throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
  482. }
  483. $worksheetNames = array();
  484. // Read the OLE file
  485. $this->_loadOLE($pFilename);
  486. // total byte size of Excel data (workbook global substream + sheet substreams)
  487. $this->_dataSize = strlen($this->_data);
  488. $this->_pos = 0;
  489. $this->_sheets = array();
  490. // Parse Workbook Global Substream
  491. while ($this->_pos < $this->_dataSize) {
  492. $code = self::_GetInt2d($this->_data, $this->_pos);
  493. switch ($code) {
  494. case self::XLS_Type_BOF: $this->_readBof(); break;
  495. case self::XLS_Type_SHEET: $this->_readSheet(); break;
  496. case self::XLS_Type_EOF: $this->_readDefault(); break 2;
  497. default: $this->_readDefault(); break;
  498. }
  499. }
  500. foreach ($this->_sheets as $sheet) {
  501. if ($sheet['sheetType'] != 0x00) {
  502. // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
  503. continue;
  504. }
  505. $worksheetNames[] = $sheet['name'];
  506. }
  507. return $worksheetNames;
  508. }
  509. /**
  510. * Loads PHPExcel from file
  511. *
  512. * @param string $pFilename
  513. * @return PHPExcel
  514. * @throws Exception
  515. */
  516. public function load($pFilename)
  517. {
  518. // Read the OLE file
  519. $this->_loadOLE($pFilename);
  520. // Initialisations
  521. $this->_phpExcel = new PHPExcel;
  522. $this->_phpExcel->removeSheetByIndex(0); // remove 1st sheet
  523. if (!$this->_readDataOnly) {
  524. $this->_phpExcel->removeCellStyleXfByIndex(0); // remove the default style
  525. $this->_phpExcel->removeCellXfByIndex(0); // remove the default style
  526. }
  527. // Read the summary information stream (containing meta data)
  528. $this->_readSummaryInformation();
  529. // Read the Additional document summary information stream (containing application-specific meta data)
  530. $this->_readDocumentSummaryInformation();
  531. // total byte size of Excel data (workbook global substream + sheet substreams)
  532. $this->_dataSize = strlen($this->_data);
  533. // initialize
  534. $this->_pos = 0;
  535. $this->_codepage = 'CP1252';
  536. $this->_formats = array();
  537. $this->_objFonts = array();
  538. $this->_palette = array();
  539. $this->_sheets = array();
  540. $this->_externalBooks = array();
  541. $this->_ref = array();
  542. $this->_definedname = array();
  543. $this->_sst = array();
  544. $this->_drawingGroupData = '';
  545. $this->_xfIndex = '';
  546. $this->_mapCellXfIndex = array();
  547. $this->_mapCellStyleXfIndex = array();
  548. // Parse Workbook Global Substream
  549. while ($this->_pos < $this->_dataSize) {
  550. $code = self::_GetInt2d($this->_data, $this->_pos);
  551. switch ($code) {
  552. case self::XLS_Type_BOF: $this->_readBof(); break;
  553. case self::XLS_Type_FILEPASS: $this->_readFilepass(); break;
  554. case self::XLS_Type_CODEPAGE: $this->_readCodepage(); break;
  555. case self::XLS_Type_DATEMODE: $this->_readDateMode(); break;
  556. case self::XLS_Type_FONT: $this->_readFont(); break;
  557. case self::XLS_Type_FORMAT: $this->_readFormat(); break;
  558. case self::XLS_Type_XF: $this->_readXf(); break;
  559. case self::XLS_Type_XFEXT: $this->_readXfExt(); break;
  560. case self::XLS_Type_STYLE: $this->_readStyle(); break;
  561. case self::XLS_Type_PALETTE: $this->_readPalette(); break;
  562. case self::XLS_Type_SHEET: $this->_readSheet(); break;
  563. case self::XLS_Type_EXTERNALBOOK: $this->_readExternalBook(); break;
  564. case self::XLS_Type_EXTERNNAME: $this->_readExternName(); break;
  565. case self::XLS_Type_EXTERNSHEET: $this->_readExternSheet(); break;
  566. case self::XLS_Type_DEFINEDNAME: $this->_readDefinedName(); break;
  567. case self::XLS_Type_MSODRAWINGGROUP: $this->_readMsoDrawingGroup(); break;
  568. case self::XLS_Type_SST: $this->_readSst(); break;
  569. case self::XLS_Type_EOF: $this->_readDefault(); break 2;
  570. default: $this->_readDefault(); break;
  571. }
  572. }
  573. // Resolve indexed colors for font, fill, and border colors
  574. // Cannot be resolved already in XF record, because PALETTE record comes afterwards
  575. if (!$this->_readDataOnly) {
  576. foreach ($this->_objFonts as $objFont) {
  577. if (isset($objFont->colorIndex)) {
  578. $color = self::_readColor($objFont->colorIndex,$this->_palette,$this->_version);
  579. $objFont->getColor()->setRGB($color['rgb']);
  580. }
  581. }
  582. foreach ($this->_phpExcel->getCellXfCollection() as $objStyle) {
  583. // fill start and end color
  584. $fill = $objStyle->getFill();
  585. if (isset($fill->startcolorIndex)) {
  586. $startColor = self::_readColor($fill->startcolorIndex,$this->_palette,$this->_version);
  587. $fill->getStartColor()->setRGB($startColor['rgb']);
  588. }
  589. if (isset($fill->endcolorIndex)) {
  590. $endColor = self::_readColor($fill->endcolorIndex,$this->_palette,$this->_version);
  591. $fill->getEndColor()->setRGB($endColor['rgb']);
  592. }
  593. // border colors
  594. $top = $objStyle->getBorders()->getTop();
  595. $right = $objStyle->getBorders()->getRight();
  596. $bottom = $objStyle->getBorders()->getBottom();
  597. $left = $objStyle->getBorders()->getLeft();
  598. $diagonal = $objStyle->getBorders()->getDiagonal();
  599. if (isset($top->colorIndex)) {
  600. $borderTopColor = self::_readColor($top->colorIndex,$this->_palette,$this->_version);
  601. $top->getColor()->setRGB($borderTopColor['rgb']);
  602. }
  603. if (isset($right->colorIndex)) {
  604. $borderRightColor = self::_readColor($right->colorIndex,$this->_palette,$this->_version);
  605. $right->getColor()->setRGB($borderRightColor['rgb']);
  606. }
  607. if (isset($bottom->colorIndex)) {
  608. $borderBottomColor = self::_readColor($bottom->colorIndex,$this->_palette,$this->_version);
  609. $bottom->getColor()->setRGB($borderBottomColor['rgb']);
  610. }
  611. if (isset($left->colorIndex)) {
  612. $borderLeftColor = self::_readColor($left->colorIndex,$this->_palette,$this->_version);
  613. $left->getColor()->setRGB($borderLeftColor['rgb']);
  614. }
  615. if (isset($diagonal->colorIndex)) {
  616. $borderDiagonalColor = self::_readColor($diagonal->colorIndex,$this->_palette,$this->_version);
  617. $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
  618. }
  619. }
  620. }
  621. // treat MSODRAWINGGROUP records, workbook-level Escher
  622. if (!$this->_readDataOnly && $this->_drawingGroupData) {
  623. $escherWorkbook = new PHPExcel_Shared_Escher();
  624. $reader = new PHPExcel_Reader_Excel5_Escher($escherWorkbook);
  625. $escherWorkbook = $reader->load($this->_drawingGroupData);
  626. // debug Escher stream
  627. //$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
  628. //$debug->load($this->_drawingGroupData);
  629. }
  630. // Parse the individual sheets
  631. foreach ($this->_sheets as $sheet) {
  632. if ($sheet['sheetType'] != 0x00) {
  633. // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
  634. continue;
  635. }
  636. // check if sheet should be skipped
  637. if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) {
  638. continue;
  639. }
  640. // add sheet to PHPExcel object
  641. $this->_phpSheet = $this->_phpExcel->createSheet();
  642. $this->_phpSheet->setTitle($sheet['name']);
  643. $this->_phpSheet->setSheetState($sheet['sheetState']);
  644. $this->_pos = $sheet['offset'];
  645. // Initialize isFitToPages. May change after reading SHEETPR record.
  646. $this->_isFitToPages = false;
  647. // Initialize drawingData
  648. $this->_drawingData = '';
  649. // Initialize objs
  650. $this->_objs = array();
  651. // Initialize shared formula parts
  652. $this->_sharedFormulaParts = array();
  653. // Initialize shared formulas
  654. $this->_sharedFormulas = array();
  655. // Initialize text objs
  656. $this->_textObjects = array();
  657. // Initialize cell annotations
  658. $this->_cellNotes = array();
  659. $this->textObjRef = -1;
  660. while ($this->_pos <= $this->_dataSize - 4) {
  661. $code = self::_GetInt2d($this->_data, $this->_pos);
  662. switch ($code) {
  663. case self::XLS_Type_BOF: $this->_readBof(); break;
  664. case self::XLS_Type_PRINTGRIDLINES: $this->_readPrintGridlines(); break;
  665. case self::XLS_Type_DEFAULTROWHEIGHT: $this->_readDefaultRowHeight(); break;
  666. case self::XLS_Type_SHEETPR: $this->_readSheetPr(); break;
  667. case self::XLS_Type_HORIZONTALPAGEBREAKS: $this->_readHorizontalPageBreaks(); break;
  668. case self::XLS_Type_VERTICALPAGEBREAKS: $this->_readVerticalPageBreaks(); break;
  669. case self::XLS_Type_HEADER: $this->_readHeader(); break;
  670. case self::XLS_Type_FOOTER: $this->_readFooter(); break;
  671. case self::XLS_Type_HCENTER: $this->_readHcenter(); break;
  672. case self::XLS_Type_VCENTER: $this->_readVcenter(); break;
  673. case self::XLS_Type_LEFTMARGIN: $this->_readLeftMargin(); break;
  674. case self::XLS_Type_RIGHTMARGIN: $this->_readRightMargin(); break;
  675. case self::XLS_Type_TOPMARGIN: $this->_readTopMargin(); break;
  676. case self::XLS_Type_BOTTOMMARGIN: $this->_readBottomMargin(); break;
  677. case self::XLS_Type_PAGESETUP: $this->_readPageSetup(); break;
  678. case self::XLS_Type_PROTECT: $this->_readProtect(); break;
  679. case self::XLS_Type_SCENPROTECT: $this->_readScenProtect(); break;
  680. case self::XLS_Type_OBJECTPROTECT: $this->_readObjectProtect(); break;
  681. case self::XLS_Type_PASSWORD: $this->_readPassword(); break;
  682. case self::XLS_Type_DEFCOLWIDTH: $this->_readDefColWidth(); break;
  683. case self::XLS_Type_COLINFO: $this->_readColInfo(); break;
  684. case self::XLS_Type_DIMENSION: $this->_readDefault(); break;
  685. case self::XLS_Type_ROW: $this->_readRow(); break;
  686. case self::XLS_Type_DBCELL: $this->_readDefault(); break;
  687. case self::XLS_Type_RK: $this->_readRk(); break;
  688. case self::XLS_Type_LABELSST: $this->_readLabelSst(); break;
  689. case self::XLS_Type_MULRK: $this->_readMulRk(); break;
  690. case self::XLS_Type_NUMBER: $this->_readNumber(); break;
  691. case self::XLS_Type_FORMULA: $this->_readFormula(); break;
  692. case self::XLS_Type_SHAREDFMLA: $this->_readSharedFmla(); break;
  693. case self::XLS_Type_BOOLERR: $this->_readBoolErr(); break;
  694. case self::XLS_Type_MULBLANK: $this->_readMulBlank(); break;
  695. case self::XLS_Type_LABEL: $this->_readLabel(); break;
  696. case self::XLS_Type_BLANK: $this->_readBlank(); break;
  697. case self::XLS_Type_MSODRAWING: $this->_readMsoDrawing(); break;
  698. case self::XLS_Type_OBJ: $this->_readObj(); break;
  699. case self::XLS_Type_WINDOW2: $this->_readWindow2(); break;
  700. case self::XLS_Type_SCL: $this->_readScl(); break;
  701. case self::XLS_Type_PANE: $this->_readPane(); break;
  702. case self::XLS_Type_SELECTION: $this->_readSelection(); break;
  703. case self::XLS_Type_MERGEDCELLS: $this->_readMergedCells(); break;
  704. case self::XLS_Type_HYPERLINK: $this->_readHyperLink(); break;
  705. case self::XLS_Type_DATAVALIDATIONS: $this->_readDataValidations(); break;
  706. case self::XLS_Type_DATAVALIDATION: $this->_readDataValidation(); break;
  707. case self::XLS_Type_SHEETLAYOUT: $this->_readSheetLayout(); break;
  708. case self::XLS_Type_SHEETPROTECTION: $this->_readSheetProtection(); break;
  709. case self::XLS_Type_RANGEPROTECTION: $this->_readRangeProtection(); break;
  710. case self::XLS_Type_NOTE: $this->_readNote(); break;
  711. //case self::XLS_Type_IMDATA: $this->_readImData(); break;
  712. case self::XLS_Type_TXO: $this->_readTextObject(); break;
  713. case self::XLS_Type_CONTINUE: $this->_readContinue(); break;
  714. case self::XLS_Type_EOF: $this->_readDefault(); break 2;
  715. default: $this->_readDefault(); break;
  716. }
  717. }
  718. // treat MSODRAWING records, sheet-level Escher
  719. if (!$this->_readDataOnly && $this->_drawingData) {
  720. $escherWorksheet = new PHPExcel_Shared_Escher();
  721. $reader = new PHPExcel_Reader_Excel5_Escher($escherWorksheet);
  722. $escherWorksheet = $reader->load($this->_drawingData);
  723. // debug Escher stream
  724. //$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
  725. //$debug->load($this->_drawingData);
  726. // get all spContainers in one long array, so they can be mapped to OBJ records
  727. $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
  728. }
  729. // treat OBJ records
  730. foreach ($this->_objs as $n => $obj) {
  731. // echo '<hr /><b>Object</b> reference is ',$n,'<br />';
  732. // var_dump($obj);
  733. // echo '<br />';
  734. // the first shape container never has a corresponding OBJ record, hence $n + 1
  735. $spContainer = $allSpContainers[$n + 1];
  736. // we skip all spContainers that are a part of a group shape since we cannot yet handle those
  737. if ($spContainer->getNestingLevel() > 1) {
  738. continue;
  739. }
  740. // calculate the width and height of the shape
  741. list($startColumn, $startRow) = PHPExcel_Cell::coordinateFromString($spContainer->getStartCoordinates());
  742. list($endColumn, $endRow) = PHPExcel_Cell::coordinateFromString($spContainer->getEndCoordinates());
  743. $startOffsetX = $spContainer->getStartOffsetX();
  744. $startOffsetY = $spContainer->getStartOffsetY();
  745. $endOffsetX = $spContainer->getEndOffsetX();
  746. $endOffsetY = $spContainer->getEndOffsetY();
  747. $width = PHPExcel_Shared_Excel5::getDistanceX($this->_phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
  748. $height = PHPExcel_Shared_Excel5::getDistanceY($this->_phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
  749. // calculate offsetX and offsetY of the shape
  750. $offsetX = $startOffsetX * PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, $startColumn) / 1024;
  751. $offsetY = $startOffsetY * PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $startRow) / 256;
  752. switch ($obj['otObjType']) {
  753. case 0x19:
  754. // Note
  755. // echo 'Cell Annotation Object<br />';
  756. // echo 'Object ID is ',$obj['idObjID'],'<br />';
  757. //
  758. if (isset($this->_cellNotes[$obj['idObjID']])) {
  759. $cellNote = $this->_cellNotes[$obj['idObjID']];
  760. // echo '_cellNotes[',$obj['idObjID'],']: ';
  761. // var_dump($cellNote);
  762. // echo '<br />';
  763. //
  764. if (isset($this->_textObjects[$obj['idObjID']])) {
  765. $textObject = $this->_textObjects[$obj['idObjID']];
  766. // echo '_textObject: ';
  767. // var_dump($textObject);
  768. // echo '<br />';
  769. //
  770. $this->_cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
  771. $text = $textObject['text'];
  772. }
  773. // echo $text,'<br />';
  774. }
  775. break;
  776. case 0x08:
  777. // echo 'Picture Object<br />';
  778. // picture
  779. // get index to BSE entry (1-based)
  780. $BSEindex = $spContainer->getOPT(0x0104);
  781. $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
  782. $BSE = $BSECollection[$BSEindex - 1];
  783. $blipType = $BSE->getBlipType();
  784. // need check because some blip types are not supported by Escher reader such as EMF
  785. if ($blip = $BSE->getBlip()) {
  786. $ih = imagecreatefromstring($blip->getData());
  787. $drawing = new PHPExcel_Worksheet_MemoryDrawing();
  788. $drawing->setImageResource($ih);
  789. // width, height, offsetX, offsetY
  790. $drawing->setResizeProportional(false);
  791. $drawing->setWidth($width);
  792. $drawing->setHeight($height);
  793. $drawing->setOffsetX($offsetX);
  794. $drawing->setOffsetY($offsetY);
  795. switch ($blipType) {
  796. case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG:
  797. $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG);
  798. $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_JPEG);
  799. break;
  800. case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG:
  801. $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG);
  802. $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_PNG);
  803. break;
  804. }
  805. $drawing->setWorksheet($this->_phpSheet);
  806. $drawing->setCoordinates($spContainer->getStartCoordinates());
  807. }
  808. break;
  809. default:
  810. // other object type
  811. break;
  812. }
  813. }
  814. // treat SHAREDFMLA records
  815. if ($this->_version == self::XLS_BIFF8) {
  816. foreach ($this->_sharedFormulaParts as $cell => $baseCell) {
  817. list($column, $row) = PHPExcel_Cell::coordinateFromString($cell);
  818. if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($column, $row, $this->_phpSheet->getTitle()) ) {
  819. $formula = $this->_getFormulaFromStructure($this->_sharedFormulas[$baseCell], $cell);
  820. $this->_phpSheet->getCell($cell)->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA);
  821. }
  822. }
  823. }
  824. if (count($this->_cellNotes) > 0) {
  825. foreach($this->_cellNotes as $note => $noteDetails) {
  826. // echo '<b>Cell annotation ',$note,'</b><br />';
  827. // var_dump($noteDetails);
  828. // echo '<br />';
  829. $cellAddress = str_replace('$','',$noteDetails['cellRef']);
  830. $this->_phpSheet->getComment( $cellAddress )
  831. ->setAuthor( $noteDetails['author'] )
  832. ->setText($this->_parseRichText($noteDetails['objTextData']['text']) );
  833. }
  834. }
  835. }
  836. // add the named ranges (defined names)
  837. foreach ($this->_definedname as $definedName) {
  838. if ($definedName['isBuiltInName']) {
  839. switch ($definedName['name']) {
  840. case pack('C', 0x06):
  841. // print area
  842. // in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
  843. $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
  844. $extractedRanges = array();
  845. foreach ($ranges as $range) {
  846. // $range should look like one of these
  847. // Foo!$C$7:$J$66
  848. // Bar!$A$1:$IV$2
  849. $explodes = explode('!', $range); // FIXME: what if sheetname contains exclamation mark?
  850. $sheetName = $explodes[0];
  851. if (count($explodes) == 2) {
  852. $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
  853. }
  854. }
  855. if ($docSheet = $this->_phpExcel->getSheetByName($sheetName)) {
  856. $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
  857. }
  858. break;
  859. case pack('C', 0x07):
  860. // print titles (repeating rows)
  861. // Assuming BIFF8, there are 3 cases
  862. // 1. repeating rows
  863. // formula looks like this: Sheet!$A$1:$IV$2
  864. // rows 1-2 repeat
  865. // 2. repeating columns
  866. // formula looks like this: Sheet!$A$1:$B$65536
  867. // columns A-B repeat
  868. // 3. both repeating rows and repeating columns
  869. // formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
  870. $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
  871. foreach ($ranges as $range) {
  872. // $range should look like this one of these
  873. // Sheet!$A$1:$B$65536
  874. // Sheet!$A$1:$IV$2
  875. $explodes = explode('!', $range);
  876. if (count($explodes) == 2) {
  877. if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) {
  878. $extractedRange = $explodes[1];
  879. $extractedRange = str_replace('$', '', $extractedRange);
  880. $coordinateStrings = explode(':', $extractedRange);
  881. if (count($coordinateStrings) == 2) {
  882. list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[0]);
  883. list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[1]);
  884. if ($firstColumn == 'A' and $lastColumn == 'IV') {
  885. // then we have repeating rows
  886. $docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow, $lastRow));
  887. } elseif ($firstRow == 1 and $lastRow == 65536) {
  888. // then we have repeating columns
  889. $docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn, $lastColumn));
  890. }
  891. }
  892. }
  893. }
  894. }
  895. break;
  896. }
  897. } else {
  898. // Extract range
  899. $explodes = explode('!', $definedName['formula']);
  900. if (count($explodes) == 2) {
  901. if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) {
  902. $extractedRange = $explodes[1];
  903. $extractedRange = str_replace('$', '', $extractedRange);
  904. $localOnly = ($definedName['scope'] == 0) ? false : true;
  905. $scope = ($definedName['scope'] == 0) ?
  906. null : $this->_phpExcel->getSheetByName($this->_sheets[$definedName['scope'] - 1]['name']);
  907. $this->_phpExcel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $docSheet, $extractedRange, $localOnly, $scope) );
  908. }
  909. }
  910. }
  911. }
  912. return $this->_phpExcel;
  913. }
  914. /**
  915. * Use OLE reader to extract the relevant data streams from the OLE file
  916. *
  917. * @param string $pFilename
  918. */
  919. private function _loadOLE($pFilename)
  920. {
  921. // OLE reader
  922. $ole = new PHPExcel_Shared_OLERead();
  923. // get excel data,
  924. $res = $ole->read($pFilename);
  925. // Get workbook data: workbook stream + sheet streams
  926. $this->_data = $ole->getStream($ole->wrkbook);
  927. // Get summary information data
  928. $this->_summaryInformation = $ole->getStream($ole->summaryInformation);
  929. // Get additional document summary information data
  930. $this->_documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation);
  931. // Get user-defined property data
  932. // $this->_userDefinedProperties = $ole->getUserDefinedProperties();
  933. }
  934. /**
  935. * Read summary information
  936. */
  937. private function _readSummaryInformation()
  938. {
  939. if (!isset($this->_summaryInformation)) {
  940. return;
  941. }
  942. // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
  943. // offset: 2; size: 2;
  944. // offset: 4; size: 2; OS version
  945. // offset: 6; size: 2; OS indicator
  946. // offset: 8; size: 16
  947. // offset: 24; size: 4; section count
  948. $secCount = self::_GetInt4d($this->_summaryInformation, 24);
  949. // 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
  950. // offset: 44; size: 4
  951. $secOffset = self::_GetInt4d($this->_summaryInformation, 44);
  952. // section header
  953. // offset: $secOffset; size: 4; section length
  954. $secLength = self::_GetInt4d($this->_summaryInformation, $secOffset);
  955. // offset: $secOffset+4; size: 4; property count
  956. $countProperties = self::_GetInt4d($this->_summaryInformation, $secOffset+4);
  957. // initialize code page (used to resolve string values)
  958. $codePage = 'CP1252';
  959. // offset: ($secOffset+8); size: var
  960. // loop through property decarations and properties
  961. for ($i = 0; $i < $countProperties; ++$i) {
  962. // offset: ($secOffset+8) + (8 * $i); size: 4; property ID
  963. $id = self::_GetInt4d($this->_summaryInformation, ($secOffset+8) + (8 * $i));
  964. // Use value of property id as appropriate
  965. // offset: ($secOffset+12) + (8 * $i); size: 4; offset from beginning of section (48)
  966. $offset = self::_GetInt4d($this->_summaryInformation, ($secOffset+12) + (8 * $i));
  967. $type = self::_GetInt4d($this->_summaryInformation, $secOffset + $offset);
  968. // initialize property value
  969. $value = null;
  970. // extract property value based on property type
  971. switch ($type) {
  972. case 0x02: // 2 byte signed integer
  973. $value = self::_GetInt2d($this->_summaryInformation, $secOffset + 4 + $offset);
  974. break;
  975. case 0x03: // 4 byte signed integer
  976. $value = self::_GetInt4d($this->_summaryInformation, $secOffset + 4 + $offset);
  977. break;
  978. case 0x13: // 4 byte unsigned integer
  979. // not needed yet, fix later if necessary
  980. break;
  981. case 0x1E: // null-terminated string prepended by dword string length
  982. $byteLength = self::_GetInt4d($this->_summaryInformation, $secOffset + 4 + $offset);
  983. $value = substr($this->_summaryInformation, $secOffset + 8 + $offset, $byteLength);
  984. $value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage);
  985. $value = rtrim($value);
  986. break;
  987. case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
  988. // PHP-time
  989. $value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_summaryInformation, $secOffset + 4 + $offset, 8));
  990. break;
  991. case 0x47: // Clipboard format
  992. // not needed yet, fix later if necessary
  993. break;
  994. }
  995. switch ($id) {
  996. case 0x01: // Code Page
  997. $codePage = PHPExcel_Shared_CodePage::NumberToName($value);
  998. break;
  999. case 0x02: // Title
  1000. $this->_phpExcel->getProperties()->setTitle($value);
  1001. break;
  1002. case 0x03: // Subject
  1003. $this->_phpExcel->getProperties()->setSubject($value);
  1004. break;
  1005. case 0x04: // Author (Creator)
  1006. $this->_phpExcel->getProperties()->setCreator($value);
  1007. break;
  1008. case 0x05: // Keywords
  1009. $this->_phpExcel->getProperties()->setKeywords($value);
  1010. break;
  1011. case 0x06: // Comments (Description)
  1012. $this->_phpExcel->getProperties()->setDescription($value);
  1013. break;
  1014. case 0x07: // Template
  1015. // Not supported by PHPExcel
  1016. break;
  1017. case 0x08: // Last Saved By (LastModifiedBy)
  1018. $this->_phpExcel->getProperties()->setLastModifiedBy($value);
  1019. break;
  1020. case 0x09: // Revision
  1021. // Not supported by PHPExcel
  1022. break;
  1023. case 0x0A: // Total Editing Time
  1024. // Not supported by PHPExcel
  1025. break;
  1026. case 0x0B: // Last Printed
  1027. // Not supported by PHPExcel
  1028. break;
  1029. case 0x0C: // Created Date/Time
  1030. $this->_phpExcel->getProperties()->setCreated($value);
  1031. break;
  1032. case 0x0D: // Modified Date/Time
  1033. $this->_phpExcel->getProperties()->setModified($value);
  1034. break;
  1035. case 0x0E: // Number of Pages
  1036. // Not supported by PHPExcel
  1037. break;
  1038. case 0x0F: // Number of Words
  1039. // Not supported by PHPExcel
  1040. break;
  1041. case 0x10: // Number of Characters
  1042. // Not supported by PHPExcel
  1043. break;
  1044. case 0x11: // Thumbnail
  1045. // Not supported by PHPExcel
  1046. break;
  1047. case 0x12: // Name of creating application
  1048. // Not supported by PHPExcel
  1049. break;
  1050. case 0x13: // Security
  1051. // Not supported by PHPExcel
  1052. break;
  1053. }
  1054. }
  1055. }
  1056. /**
  1057. * Read additional document summary information
  1058. */
  1059. private function _readDocumentSummaryInformation()
  1060. {
  1061. if (!isset($this->_documentSummaryInformation)) {
  1062. return;
  1063. }
  1064. // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
  1065. // offset: 2; size: 2;
  1066. // offset: 4; size: 2; OS version
  1067. // offset: 6; size: 2; OS indicator
  1068. // offset: 8; size: 16
  1069. // offset: 24; size: 4; section count
  1070. $secCount = self::_GetInt4d($this->_documentSummaryInformation, 24);
  1071. // echo '$secCount = ',$secCount,'<br />';
  1072. // 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
  1073. // offset: 44; size: 4; first section offset
  1074. $secOffset = self::_GetInt4d($this->_documentSummaryInformation, 44);
  1075. // echo '$secOffset = ',$secOffset,'<br />';
  1076. // section header
  1077. // offset: $secOffset; size: 4; section length
  1078. $secLength = self::_GetInt4d($this->_documentSummaryInformation, $secOffset);
  1079. // echo '$secLength = ',$secLength,'<br />';
  1080. // offset: $secOffset+4; size: 4; property count
  1081. $countProperties = self::_GetInt4d($this->_documentSummaryInformation, $secOffset+4);
  1082. // echo '$countProperties = ',$countProperties,'<br />';
  1083. // initialize code page (used to resolve string values)
  1084. $codePage = 'CP1252';
  1085. // offset: ($secOffset+8); size: var
  1086. // loop through property decarations and properties
  1087. for ($i = 0; $i < $countProperties; ++$i) {
  1088. // echo 'Property ',$i,'<br />';
  1089. // offset: ($secOffset+8) + (8 * $i); size: 4; property ID
  1090. $id = self::_GetInt4d($this->_documentSummaryInformation, ($secOffset+8) + (8 * $i));
  1091. // echo 'ID is ',$id,'<br />';
  1092. // Use value of property id as appropriate
  1093. // offset: 60 + 8 * $i; size: 4; offset from beginning of section (48)
  1094. $offset = self::_GetInt4d($this->_documentSummaryInformation, ($secOffset+12) + (8 * $i));
  1095. $type = self::_GetInt4d($this->_documentSummaryInformation, $secOffset + $offset);
  1096. // echo 'Type is ',$type,', ';
  1097. // initialize property value
  1098. $value = null;
  1099. // extract property value based on property type
  1100. switch ($type) {
  1101. case 0x02: // 2 byte signed integer
  1102. $value = self::_GetInt2d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
  1103. break;
  1104. case 0x03: // 4 byte signed integer
  1105. $value = self::_GetInt4d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
  1106. break;
  1107. case 0x13: // 4 byte unsigned integer
  1108. // not needed yet, fix later if necessary
  1109. break;
  1110. case 0x1E: // null-terminated string prepended by dword string length
  1111. $byteLength = self::_GetInt4d($this->_documentSummaryInformation, $secOffset + 4 + $offset);
  1112. $value = substr($this->_documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
  1113. $value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage);
  1114. $value = rtrim($value);
  1115. break;
  1116. case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
  1117. // PHP-Time
  1118. $value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_documentSummaryInformation, $secOffset + 4 + $offset, 8));
  1119. break;
  1120. case 0x47: // Clipboard format
  1121. // not needed yet, fix later if necessary
  1122. break;
  1123. }
  1124. switch ($id) {
  1125. case 0x01: // Code Page
  1126. $codePage = PHPExcel_Shared_CodePage::NumberToName($value);
  1127. break;
  1128. case 0x02: // Category
  1129. $this->_phpExcel->getProperties()->setCategory($value);
  1130. break;
  1131. case 0x03: // Presentation Target
  1132. // Not supported by PHPExcel
  1133. break;
  1134. case 0x04: // Bytes
  1135. // Not supported by PHPExcel
  1136. break;
  1137. case 0x05: // Lines
  1138. // Not supported by PHPExcel
  1139. break;
  1140. case 0x06: // Paragraphs
  1141. // Not supported by PHPExcel
  1142. break;
  1143. case 0x07: // Slides
  1144. // Not supported by PHPExcel
  1145. break;
  1146. case 0x08: // Notes
  1147. // Not supported by PHPExcel
  1148. break;
  1149. case 0x09: // Hidden Slides
  1150. // Not supported by PHPExcel
  1151. break;
  1152. case 0x0A: // MM Clips
  1153. // Not supported by PHPExcel
  1154. break;
  1155. case 0x0B: // Scale Crop
  1156. // Not supported by PHPExcel
  1157. break;
  1158. case 0x0C: // Heading Pairs
  1159. // Not supported by PHPExcel
  1160. break;
  1161. case 0x0D: // Titles of Parts
  1162. // Not supported by PHPExcel
  1163. break;
  1164. case 0x0E: // Manager
  1165. $this->_phpExcel->getProperties()->setManager($value);
  1166. break;
  1167. case 0x0F: // Company
  1168. $this->_phpExcel->getProperties()->setCompany($value);
  1169. break;
  1170. case 0x10: // Links up-to-date
  1171. // Not supported by PHPExcel
  1172. break;
  1173. }
  1174. }
  1175. }
  1176. /**
  1177. * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
  1178. */
  1179. private function _readDefault()
  1180. {
  1181. $length = self::_GetInt2d($this->_data, $this->_pos + 2);
  1182. // $recordData = substr($this->_data, $this->_pos + 4, $length);
  1183. // move stream pointer to next record
  1184. $this->_pos += 4 + $length;
  1185. }
  1186. /**
  1187. * The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
  1188. * this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
  1189. */
  1190. private function _readNote()
  1191. {
  1192. // echo '<b>Read Cell Annotation</b><br />';
  1193. $length = self::_GetInt2d($this->_data, $this->_pos + 2);
  1194. $recordData = substr($this->_data, $this->_pos + 4, $length);
  1195. // move stream pointer to next record
  1196. $this->_pos += 4 + $length;
  1197. if ($this->_readDataOnly) {
  1198. return;
  1199. }
  1200. $cellAddress = $this->_readBIFF8CellAddress(substr($recordData, 0, 4));
  1201. if ($this->_version == self::XLS_BIFF8) {
  1202. $noteObjID = self::_GetInt2d($recordData, 6);
  1203. $noteAuthor = self::_readUnicodeStringLong(substr($recordData, 8));
  1204. $noteAuthor = $noteAuthor['value'];
  1205. // echo 'Note Address=',$cellAddress,'<br />';
  1206. // echo 'Note Object ID=',$noteObjID,'<br />';
  1207. // echo 'Note Author=',$noteAuthor,'<hr />';
  1208. //
  1209. $this->_cellNotes[$noteObjID] = array('cellRef' => $cellAddress,
  1210. 'objectID' => $noteObjID,
  1211. 'author' => $noteAuthor
  1212. );
  1213. } else {
  1214. $extension = false;
  1215. if ($cellAddress == '$B$65536') {
  1216. // If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
  1217. // note from the previous cell annotation. We're not yet handling this, so annotations longer than the
  1218. // max 2048 bytes will probably throw a wobbly.
  1219. $row = self::_GetInt2d($recordData, 0);
  1220. $extension = true;
  1221. $cellAddress = array_pop(array_keys($this->_phpSheet->getComments()));
  1222. }
  1223. // echo 'Note Address=',$cellAddress,'<br />';
  1224. $cellAddress = str_replace('$','',$cellAddress);
  1225. $noteLength = self::_GetInt2d($recordData, 4);
  1226. $noteText = trim(substr($recordData, 6));
  1227. // echo 'Note Length=',$noteLength,'<br />';
  1228. // echo 'Note Text=',$noteText,'<br />';
  1229. if ($extension) {
  1230. // Concatenate this extension with the currently set comment for the cell
  1231. $comment = $this->_phpSheet->getComment( $cellAddress );
  1232. $commentText = $comment->getText()->getPlainText();
  1233. $comment->setText($this->_parseRichText($commentText.$noteText) );
  1234. } else {
  1235. // Set comment for the cell
  1236. $this->_phpSheet->getComment( $cellAddress )
  1237. // ->setAuthor( $author )
  1238. ->setText($this->_parseRichText($noteText) );
  1239. }
  1240. }
  1241. }
  1242. /**
  1243. * The TEXT Object record contains the text associated with a cell annotation.
  1244. */
  1245. private function _readTextObject()
  1246. {
  1247. $length = self::_GetInt2d($this->_data, $this->_pos + 2);
  1248. $recordData = substr($this->_data, $this->_pos + 4, $length);
  1249. // move stream pointer to next record
  1250. $this->_pos += 4 + $length;
  1251. if ($this->_readDataOnly) {
  1252. return;
  1253. }
  1254. // recordData consists of an array of subrecords looking like this:
  1255. // grbit: 2 bytes; Option Flags
  1256. // rot: 2 bytes; rotation
  1257. // cchText: 2 bytes; length of the text (in the first continue record)
  1258. // cbRuns: 2 bytes; length of the formatting (in the second continue record)
  1259. // followed by the continuation records containing the actual text and formatting
  1260. $grbitOpts = self::_GetInt2d($recordData, 0);
  1261. $rot = self::_GetInt2d($recordData, 2);
  1262. $cchText = self::_GetInt2d($recordData, 10);
  1263. $cbRuns = self::_GetInt2d($recordData, 12);
  1264. $text = $this->_getSplicedRecordData();
  1265. $this->_textObjects[$this->textObjRef] = array(
  1266. 'text' => substr($text["recordData"],$text["spliceOffsets"][0]+1,$cchText),
  1267. 'format' => substr($text["recordData"],$text["spliceOffsets"][1],$cbRuns),
  1268. 'alignment' => $grbitOpts,
  1269. 'rotation' => $rot
  1270. );
  1271. // echo '<b>_readTextObject()</b><br />';
  1272. // var_dump($this->_textObjects[$this->textObjRef]);
  1273. // echo '<br />';
  1274. }
  1275. /**
  1276. * Read BOF
  1277. */
  1278. private function _readBof()
  1279. {
  1280. $length = self::_GetInt2d($this->_data, $this->_pos + 2);
  1281. $recordData = substr($this->_data, $this->_pos + 4, $length);
  1282. // move stream pointer to next record
  1283. $this->_pos += 4 + $length;
  1284. // offset: 2; size: 2; type of the following data
  1285. $substreamType = self::_GetInt2d($recordData, 2);
  1286. switch ($substreamType) {
  1287. case self::XLS_WorkbookGlobals:
  1288. $version = self::_GetInt2d($recordData, 0);
  1289. if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
  1290. throw new Exception('Cannot read this Excel file. Version is too old.');
  1291. }
  1292. $this->_version = $version;
  1293. break;
  1294. case self::XLS_Worksheet:
  1295. // do not use this version information for anything
  1296. // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
  1297. break;
  1298. default:
  1299. // substream, e.g. chart
  1300. // just skip the entire substream
  1301. do {
  1302. $code = self::_GetInt2d($this->_data, $this->_pos);
  1303. $this->_readDefault();
  1304. } while ($code != self::XLS_Type_EOF && $this->_pos < $this->_dataSize);
  1305. break;
  1306. }
  1307. }
  1308. /**
  1309. * FILEPASS
  1310. *
  1311. * This record is part of the File Protection Block. It
  1312. * contains information about the read/write password of the
  1313. * file. All record contents following this record will be
  1314. * encrypted.
  1315. *
  1316. * -- "OpenOffice.org's Documentation of the Microsoft
  1317. * Excel File Format"
  1318. */
  1319. private function _readFilepass()
  1320. {
  1321. $length = self::_GetInt2d($this->_data, $this->_pos + 2);
  1322. // $recordData = substr($this->_data, $this->_pos + 4, $length);
  1323. // move stream pointer to next record
  1324. $this->_pos += 4 + $length;
  1325. throw new Exception('Cannot read encrypted file');
  1326. }
  1327. /**
  1328. * CODEPAGE
  1329. *
  1330. * This record stores the text encoding used to write byte
  1331. * strings, stored as MS Windows code page identifier.
  1332. *
  1333. * -- "OpenOffice.org's Documentation of the Microsoft
  1334. * Excel File Format"
  1335. */
  1336. private function _readCodepage()
  1337. {
  1338. $length = self::_GetInt2d($this->_data, $this->_pos + 2);
  1339. $recordData = substr($this->_data, $this->_pos + 4, $length);
  1340. // move stream pointer to next record
  1341. $this->_pos += 4 + $length;
  1342. // offset: 0; size: 2; code page identifier
  1343. $codepage = self::_GetInt2d($recordData, 0);
  1344. $this->_codepage = PHPExcel_Shared_CodePage::NumberToName($codepage);
  1345. }
  1346. /**
  1347. * DATEMODE
  1348. *…

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