PageRenderTime 61ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 2ms

/pma/libraries/PHPExcel/PHPExcel/Reader/Excel5.php

https://bitbucket.org/kucing2k/ediassoc
PHP | 6184 lines | 3529 code | 936 blank | 1719 comment | 465 complexity | 769a5a06a0a09a193dbefafd481f405f MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, BSD-2-Clause, GPL-2.0

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

  1. <?php
  2. /**
  3. * PHPExcel
  4. *
  5. * Copyright (c) 2006 - 2010 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 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version 1.7.4, 2010-08-26
  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. PHPExcel_Autoloader::Register();
  64. PHPExcel_Shared_ZipStreamWrapper::register();
  65. // check mbstring.func_overload
  66. if (ini_get('mbstring.func_overload') & 2) {
  67. throw new Exception('Multibyte function overloading in PHP must be disabled for string functions (2).');
  68. }
  69. }
  70. /**
  71. * PHPExcel_Reader_Excel5
  72. *
  73. * This class uses {@link http://sourceforge.net/projects/phpexcelreader/parseXL}
  74. *
  75. * @category PHPExcel
  76. * @package PHPExcel_Reader_Excel5
  77. * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel)
  78. */
  79. class PHPExcel_Reader_Excel5 implements PHPExcel_Reader_IReader
  80. {
  81. // ParseXL definitions
  82. const XLS_BIFF8 = 0x0600;
  83. const XLS_BIFF7 = 0x0500;
  84. const XLS_WorkbookGlobals = 0x0005;
  85. const XLS_Worksheet = 0x0010;
  86. // record identifiers
  87. const XLS_Type_FORMULA = 0x0006;
  88. const XLS_Type_EOF = 0x000a;
  89. const XLS_Type_PROTECT = 0x0012;
  90. const XLS_Type_OBJECTPROTECT = 0x0063;
  91. const XLS_Type_SCENPROTECT = 0x00dd;
  92. const XLS_Type_PASSWORD = 0x0013;
  93. const XLS_Type_HEADER = 0x0014;
  94. const XLS_Type_FOOTER = 0x0015;
  95. const XLS_Type_EXTERNSHEET = 0x0017;
  96. const XLS_Type_DEFINEDNAME = 0x0018;
  97. const XLS_Type_VERTICALPAGEBREAKS = 0x001a;
  98. const XLS_Type_HORIZONTALPAGEBREAKS = 0x001b;
  99. const XLS_Type_NOTE = 0x001c;
  100. const XLS_Type_SELECTION = 0x001d;
  101. const XLS_Type_DATEMODE = 0x0022;
  102. const XLS_Type_EXTERNNAME = 0x0023;
  103. const XLS_Type_LEFTMARGIN = 0x0026;
  104. const XLS_Type_RIGHTMARGIN = 0x0027;
  105. const XLS_Type_TOPMARGIN = 0x0028;
  106. const XLS_Type_BOTTOMMARGIN = 0x0029;
  107. const XLS_Type_PRINTGRIDLINES = 0x002b;
  108. const XLS_Type_FILEPASS = 0x002f;
  109. const XLS_Type_FONT = 0x0031;
  110. const XLS_Type_CONTINUE = 0x003c;
  111. const XLS_Type_PANE = 0x0041;
  112. const XLS_Type_CODEPAGE = 0x0042;
  113. const XLS_Type_DEFCOLWIDTH = 0x0055;
  114. const XLS_Type_OBJ = 0x005d;
  115. const XLS_Type_COLINFO = 0x007d;
  116. const XLS_Type_IMDATA = 0x007f;
  117. const XLS_Type_SHEETPR = 0x0081;
  118. const XLS_Type_HCENTER = 0x0083;
  119. const XLS_Type_VCENTER = 0x0084;
  120. const XLS_Type_SHEET = 0x0085;
  121. const XLS_Type_PALETTE = 0x0092;
  122. const XLS_Type_SCL = 0x00a0;
  123. const XLS_Type_PAGESETUP = 0x00a1;
  124. const XLS_Type_MULRK = 0x00bd;
  125. const XLS_Type_MULBLANK = 0x00be;
  126. const XLS_Type_DBCELL = 0x00d7;
  127. const XLS_Type_XF = 0x00e0;
  128. const XLS_Type_MERGEDCELLS = 0x00e5;
  129. const XLS_Type_MSODRAWINGGROUP = 0x00eb;
  130. const XLS_Type_MSODRAWING = 0x00ec;
  131. const XLS_Type_SST = 0x00fc;
  132. const XLS_Type_LABELSST = 0x00fd;
  133. const XLS_Type_EXTSST = 0x00ff;
  134. const XLS_Type_EXTERNALBOOK = 0x01ae;
  135. const XLS_Type_DATAVALIDATIONS = 0x01b2;
  136. const XLS_Type_TXO = 0x01b6;
  137. const XLS_Type_HYPERLINK = 0x01b8;
  138. const XLS_Type_DATAVALIDATION = 0x01be;
  139. const XLS_Type_DIMENSION = 0x0200;
  140. const XLS_Type_BLANK = 0x0201;
  141. const XLS_Type_NUMBER = 0x0203;
  142. const XLS_Type_LABEL = 0x0204;
  143. const XLS_Type_BOOLERR = 0x0205;
  144. const XLS_Type_STRING = 0x0207;
  145. const XLS_Type_ROW = 0x0208;
  146. const XLS_Type_INDEX = 0x020b;
  147. const XLS_Type_ARRAY = 0x0221;
  148. const XLS_Type_DEFAULTROWHEIGHT = 0x0225;
  149. const XLS_Type_WINDOW2 = 0x023e;
  150. const XLS_Type_RK = 0x027e;
  151. const XLS_Type_STYLE = 0x0293;
  152. const XLS_Type_FORMAT = 0x041e;
  153. const XLS_Type_SHAREDFMLA = 0x04bc;
  154. const XLS_Type_BOF = 0x0809;
  155. const XLS_Type_SHEETPROTECTION = 0x0867;
  156. const XLS_Type_RANGEPROTECTION = 0x0868;
  157. const XLS_Type_SHEETLAYOUT = 0x0862;
  158. const XLS_Type_XFEXT = 0x087d;
  159. const XLS_Type_UNKNOWN = 0xffff;
  160. /**
  161. * Read data only?
  162. *
  163. * @var boolean
  164. */
  165. private $_readDataOnly = false;
  166. /**
  167. * Restict which sheets should be loaded?
  168. *
  169. * @var array
  170. */
  171. private $_loadSheetsOnly = null;
  172. /**
  173. * PHPExcel_Reader_IReadFilter instance
  174. *
  175. * @var PHPExcel_Reader_IReadFilter
  176. */
  177. private $_readFilter = null;
  178. /**
  179. * Summary Information stream data.
  180. *
  181. * @var string
  182. */
  183. private $_summaryInformation;
  184. /**
  185. * Workbook stream data. (Includes workbook globals substream as well as sheet substreams)
  186. *
  187. * @var string
  188. */
  189. private $_data;
  190. /**
  191. * Size in bytes of $this->_data
  192. *
  193. * @var int
  194. */
  195. private $_dataSize;
  196. /**
  197. * Current position in stream
  198. *
  199. * @var integer
  200. */
  201. private $_pos;
  202. /**
  203. * Workbook to be returned by the reader.
  204. *
  205. * @var PHPExcel
  206. */
  207. private $_phpExcel;
  208. /**
  209. * Worksheet that is currently being built by the reader.
  210. *
  211. * @var PHPExcel_Worksheet
  212. */
  213. private $_phpSheet;
  214. /**
  215. * BIFF version
  216. *
  217. * @var int
  218. */
  219. private $_version;
  220. /**
  221. * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
  222. * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'
  223. *
  224. * @var string
  225. */
  226. private $_codepage;
  227. /**
  228. * Shared formats
  229. *
  230. * @var array
  231. */
  232. private $_formats;
  233. /**
  234. * Shared fonts
  235. *
  236. * @var array
  237. */
  238. private $_objFonts;
  239. /**
  240. * Color palette
  241. *
  242. * @var array
  243. */
  244. private $_palette;
  245. /**
  246. * Worksheets
  247. *
  248. * @var array
  249. */
  250. private $_sheets;
  251. /**
  252. * External books
  253. *
  254. * @var array
  255. */
  256. private $_externalBooks;
  257. /**
  258. * REF structures. Only applies to BIFF8.
  259. *
  260. * @var array
  261. */
  262. private $_ref;
  263. /**
  264. * External names
  265. *
  266. * @var array
  267. */
  268. private $_externalNames;
  269. /**
  270. * Defined names
  271. *
  272. * @var array
  273. */
  274. private $_definedname;
  275. /**
  276. * Shared strings. Only applies to BIFF8.
  277. *
  278. * @var array
  279. */
  280. private $_sst;
  281. /**
  282. * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
  283. *
  284. * @var boolean
  285. */
  286. private $_frozen;
  287. /**
  288. * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
  289. *
  290. * @var boolean
  291. */
  292. private $_isFitToPages;
  293. /**
  294. * Objects. One OBJ record contributes with one entry.
  295. *
  296. * @var array
  297. */
  298. private $_objs;
  299. /**
  300. * The combined MSODRAWINGGROUP data
  301. *
  302. * @var string
  303. */
  304. private $_drawingGroupData;
  305. /**
  306. * The combined MSODRAWING data (per sheet)
  307. *
  308. * @var string
  309. */
  310. private $_drawingData;
  311. /**
  312. * Keep track of XF index
  313. *
  314. * @var int
  315. */
  316. private $_xfIndex;
  317. /**
  318. * Mapping of XF index (that is a cell XF) to final index in cellXf collection
  319. *
  320. * @var array
  321. */
  322. private $_mapCellXfIndex;
  323. /**
  324. * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection
  325. *
  326. * @var array
  327. */
  328. private $_mapCellStyleXfIndex;
  329. /**
  330. * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
  331. *
  332. * @var array
  333. */
  334. private $_sharedFormulas;
  335. /**
  336. * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
  337. * refers to a shared formula.
  338. *
  339. * @var array
  340. */
  341. private $_sharedFormulaParts;
  342. /**
  343. * Read data only?
  344. *
  345. * @return boolean
  346. */
  347. public function getReadDataOnly()
  348. {
  349. return $this->_readDataOnly;
  350. }
  351. /**
  352. * Set read data only
  353. *
  354. * @param boolean $pValue
  355. * @return PHPExcel_Reader_Excel5
  356. */
  357. public function setReadDataOnly($pValue = false)
  358. {
  359. $this->_readDataOnly = $pValue;
  360. return $this;
  361. }
  362. /**
  363. * Get which sheets to load
  364. *
  365. * @return mixed
  366. */
  367. public function getLoadSheetsOnly()
  368. {
  369. return $this->_loadSheetsOnly;
  370. }
  371. /**
  372. * Set which sheets to load
  373. *
  374. * @param mixed $value
  375. * @return PHPExcel_Reader_Excel5
  376. */
  377. public function setLoadSheetsOnly($value = null)
  378. {
  379. $this->_loadSheetsOnly = is_array($value) ?
  380. $value : array($value);
  381. return $this;
  382. }
  383. /**
  384. * Set all sheets to load
  385. *
  386. * @return PHPExcel_Reader_Excel5
  387. */
  388. public function setLoadAllSheets()
  389. {
  390. $this->_loadSheetsOnly = null;
  391. return $this;
  392. }
  393. /**
  394. * Read filter
  395. *
  396. * @return PHPExcel_Reader_IReadFilter
  397. */
  398. public function getReadFilter() {
  399. return $this->_readFilter;
  400. }
  401. /**
  402. * Set read filter
  403. *
  404. * @param PHPExcel_Reader_IReadFilter $pValue
  405. * @return PHPExcel_Reader_Excel5
  406. */
  407. public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
  408. $this->_readFilter = $pValue;
  409. return $this;
  410. }
  411. /**
  412. * Create a new PHPExcel_Reader_Excel5 instance
  413. */
  414. public function __construct() {
  415. $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
  416. }
  417. /**
  418. * Can the current PHPExcel_Reader_IReader read the file?
  419. *
  420. * @param string $pFileName
  421. * @return boolean
  422. */
  423. public function canRead($pFilename)
  424. {
  425. // Check if file exists
  426. if (!file_exists($pFilename)) {
  427. throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
  428. }
  429. try {
  430. // Use ParseXL for the hard work.
  431. $ole = new PHPExcel_Shared_OLERead();
  432. // get excel data
  433. $res = $ole->read($pFilename);
  434. return true;
  435. } catch (Exception $e) {
  436. return false;
  437. }
  438. }
  439. /**
  440. * Loads PHPExcel from file
  441. *
  442. * @param string $pFilename
  443. * @return PHPExcel
  444. * @throws Exception
  445. */
  446. public function load($pFilename)
  447. {
  448. // Read the OLE file
  449. $this->_loadOLE($pFilename);
  450. // Initialisations
  451. $this->_phpExcel = new PHPExcel;
  452. $this->_phpExcel->removeSheetByIndex(0); // remove 1st sheet
  453. if (!$this->_readDataOnly) {
  454. $this->_phpExcel->removeCellStyleXfByIndex(0); // remove the default style
  455. $this->_phpExcel->removeCellXfByIndex(0); // remove the default style
  456. }
  457. // Read the summary information stream (containing meta data)
  458. $this->_readSummaryInformation();
  459. // total byte size of Excel data (workbook global substream + sheet substreams)
  460. $this->_dataSize = strlen($this->_data);
  461. // initialize
  462. $this->_pos = 0;
  463. $this->_codepage = 'CP1252';
  464. $this->_formats = array();
  465. $this->_objFonts = array();
  466. $this->_palette = array();
  467. $this->_sheets = array();
  468. $this->_externalBooks = array();
  469. $this->_ref = array();
  470. $this->_definedname = array();
  471. $this->_sst = array();
  472. $this->_drawingGroupData = '';
  473. $this->_xfIndex = '';
  474. $this->_mapCellXfIndex = array();
  475. $this->_mapCellStyleXfIndex = array();
  476. // Parse Workbook Global Substream
  477. while ($this->_pos < $this->_dataSize) {
  478. $code = $this->_GetInt2d($this->_data, $this->_pos);
  479. switch ($code) {
  480. case self::XLS_Type_BOF: $this->_readBof(); break;
  481. case self::XLS_Type_FILEPASS: $this->_readFilepass(); break;
  482. case self::XLS_Type_CODEPAGE: $this->_readCodepage(); break;
  483. case self::XLS_Type_DATEMODE: $this->_readDateMode(); break;
  484. case self::XLS_Type_FONT: $this->_readFont(); break;
  485. case self::XLS_Type_FORMAT: $this->_readFormat(); break;
  486. case self::XLS_Type_XF: $this->_readXf(); break;
  487. case self::XLS_Type_XFEXT: $this->_readXfExt(); break;
  488. case self::XLS_Type_STYLE: $this->_readStyle(); break;
  489. case self::XLS_Type_PALETTE: $this->_readPalette(); break;
  490. case self::XLS_Type_SHEET: $this->_readSheet(); break;
  491. case self::XLS_Type_EXTERNALBOOK: $this->_readExternalBook(); break;
  492. case self::XLS_Type_EXTERNNAME: $this->_readExternName(); break;
  493. case self::XLS_Type_EXTERNSHEET: $this->_readExternSheet(); break;
  494. case self::XLS_Type_DEFINEDNAME: $this->_readDefinedName(); break;
  495. case self::XLS_Type_MSODRAWINGGROUP: $this->_readMsoDrawingGroup(); break;
  496. case self::XLS_Type_SST: $this->_readSst(); break;
  497. case self::XLS_Type_EOF: $this->_readDefault(); break 2;
  498. default: $this->_readDefault(); break;
  499. }
  500. }
  501. // Resolve indexed colors for font, fill, and border colors
  502. // Cannot be resolved already in XF record, because PALETTE record comes afterwards
  503. if (!$this->_readDataOnly) {
  504. foreach ($this->_objFonts as $objFont) {
  505. if (isset($objFont->colorIndex)) {
  506. $color = $this->_readColor($objFont->colorIndex);
  507. $objFont->getColor()->setRGB($color['rgb']);
  508. }
  509. }
  510. foreach ($this->_phpExcel->getCellXfCollection() as $objStyle) {
  511. // fill start and end color
  512. $fill = $objStyle->getFill();
  513. if (isset($fill->startcolorIndex)) {
  514. $startColor = $this->_readColor($fill->startcolorIndex);
  515. $fill->getStartColor()->setRGB($startColor['rgb']);
  516. }
  517. if (isset($fill->endcolorIndex)) {
  518. $endColor = $this->_readColor($fill->endcolorIndex);
  519. $fill->getEndColor()->setRGB($endColor['rgb']);
  520. }
  521. // border colors
  522. $top = $objStyle->getBorders()->getTop();
  523. $right = $objStyle->getBorders()->getRight();
  524. $bottom = $objStyle->getBorders()->getBottom();
  525. $left = $objStyle->getBorders()->getLeft();
  526. $diagonal = $objStyle->getBorders()->getDiagonal();
  527. if (isset($top->colorIndex)) {
  528. $borderTopColor = $this->_readColor($top->colorIndex);
  529. $top->getColor()->setRGB($borderTopColor['rgb']);
  530. }
  531. if (isset($right->colorIndex)) {
  532. $borderRightColor = $this->_readColor($right->colorIndex);
  533. $right->getColor()->setRGB($borderRightColor['rgb']);
  534. }
  535. if (isset($bottom->colorIndex)) {
  536. $borderBottomColor = $this->_readColor($bottom->colorIndex);
  537. $bottom->getColor()->setRGB($borderBottomColor['rgb']);
  538. }
  539. if (isset($left->colorIndex)) {
  540. $borderLeftColor = $this->_readColor($left->colorIndex);
  541. $left->getColor()->setRGB($borderLeftColor['rgb']);
  542. }
  543. if (isset($diagonal->colorIndex)) {
  544. $borderDiagonalColor = $this->_readColor($diagonal->colorIndex);
  545. $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
  546. }
  547. }
  548. }
  549. // treat MSODRAWINGGROUP records, workbook-level Escher
  550. if (!$this->_readDataOnly && $this->_drawingGroupData) {
  551. $escherWorkbook = new PHPExcel_Shared_Escher();
  552. $reader = new PHPExcel_Reader_Excel5_Escher($escherWorkbook);
  553. $escherWorkbook = $reader->load($this->_drawingGroupData);
  554. // debug Escher stream
  555. //$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
  556. //$debug->load($this->_drawingGroupData);
  557. }
  558. // Parse the individual sheets
  559. foreach ($this->_sheets as $sheet) {
  560. if ($sheet['sheetType'] != 0x00) {
  561. // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
  562. continue;
  563. }
  564. // check if sheet should be skipped
  565. if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) {
  566. continue;
  567. }
  568. // add sheet to PHPExcel object
  569. $this->_phpSheet = $this->_phpExcel->createSheet();
  570. $this->_phpSheet->setTitle($sheet['name']);
  571. $this->_phpSheet->setSheetState($sheet['sheetState']);
  572. $this->_pos = $sheet['offset'];
  573. // Initialize isFitToPages. May change after reading SHEETPR record.
  574. $this->_isFitToPages = false;
  575. // Initialize drawingData
  576. $this->_drawingData = '';
  577. // Initialize objs
  578. $this->_objs = array();
  579. // Initialize shared formula parts
  580. $this->_sharedFormulaParts = array();
  581. // Initialize shared formulas
  582. $this->_sharedFormulas = array();
  583. while ($this->_pos <= $this->_dataSize - 4) {
  584. $code = $this->_GetInt2d($this->_data, $this->_pos);
  585. switch ($code) {
  586. case self::XLS_Type_BOF: $this->_readBof(); break;
  587. case self::XLS_Type_PRINTGRIDLINES: $this->_readPrintGridlines(); break;
  588. case self::XLS_Type_DEFAULTROWHEIGHT: $this->_readDefaultRowHeight(); break;
  589. case self::XLS_Type_SHEETPR: $this->_readSheetPr(); break;
  590. case self::XLS_Type_HORIZONTALPAGEBREAKS: $this->_readHorizontalPageBreaks(); break;
  591. case self::XLS_Type_VERTICALPAGEBREAKS: $this->_readVerticalPageBreaks(); break;
  592. case self::XLS_Type_HEADER: $this->_readHeader(); break;
  593. case self::XLS_Type_FOOTER: $this->_readFooter(); break;
  594. case self::XLS_Type_HCENTER: $this->_readHcenter(); break;
  595. case self::XLS_Type_VCENTER: $this->_readVcenter(); break;
  596. case self::XLS_Type_LEFTMARGIN: $this->_readLeftMargin(); break;
  597. case self::XLS_Type_RIGHTMARGIN: $this->_readRightMargin(); break;
  598. case self::XLS_Type_TOPMARGIN: $this->_readTopMargin(); break;
  599. case self::XLS_Type_BOTTOMMARGIN: $this->_readBottomMargin(); break;
  600. case self::XLS_Type_PAGESETUP: $this->_readPageSetup(); break;
  601. case self::XLS_Type_PROTECT: $this->_readProtect(); break;
  602. case self::XLS_Type_SCENPROTECT: $this->_readScenProtect(); break;
  603. case self::XLS_Type_OBJECTPROTECT: $this->_readObjectProtect(); break;
  604. case self::XLS_Type_PASSWORD: $this->_readPassword(); break;
  605. case self::XLS_Type_DEFCOLWIDTH: $this->_readDefColWidth(); break;
  606. case self::XLS_Type_COLINFO: $this->_readColInfo(); break;
  607. case self::XLS_Type_DIMENSION: $this->_readDefault(); break;
  608. case self::XLS_Type_ROW: $this->_readRow(); break;
  609. case self::XLS_Type_DBCELL: $this->_readDefault(); break;
  610. case self::XLS_Type_RK: $this->_readRk(); break;
  611. case self::XLS_Type_LABELSST: $this->_readLabelSst(); break;
  612. case self::XLS_Type_MULRK: $this->_readMulRk(); break;
  613. case self::XLS_Type_NUMBER: $this->_readNumber(); break;
  614. case self::XLS_Type_FORMULA: $this->_readFormula(); break;
  615. case self::XLS_Type_SHAREDFMLA: $this->_readSharedFmla(); break;
  616. case self::XLS_Type_BOOLERR: $this->_readBoolErr(); break;
  617. case self::XLS_Type_MULBLANK: $this->_readMulBlank(); break;
  618. case self::XLS_Type_LABEL: $this->_readLabel(); break;
  619. case self::XLS_Type_BLANK: $this->_readBlank(); break;
  620. case self::XLS_Type_MSODRAWING: $this->_readMsoDrawing(); break;
  621. case self::XLS_Type_OBJ: $this->_readObj(); break;
  622. case self::XLS_Type_WINDOW2: $this->_readWindow2(); break;
  623. case self::XLS_Type_SCL: $this->_readScl(); break;
  624. case self::XLS_Type_PANE: $this->_readPane(); break;
  625. case self::XLS_Type_SELECTION: $this->_readSelection(); break;
  626. case self::XLS_Type_MERGEDCELLS: $this->_readMergedCells(); break;
  627. case self::XLS_Type_HYPERLINK: $this->_readHyperLink(); break;
  628. case self::XLS_Type_DATAVALIDATIONS: $this->_readDataValidations(); break;
  629. case self::XLS_Type_DATAVALIDATION: $this->_readDataValidation(); break;
  630. case self::XLS_Type_SHEETLAYOUT: $this->_readSheetLayout(); break;
  631. case self::XLS_Type_SHEETPROTECTION: $this->_readSheetProtection(); break;
  632. case self::XLS_Type_RANGEPROTECTION: $this->_readRangeProtection(); break;
  633. //case self::XLS_Type_IMDATA: $this->_readImData(); break;
  634. case self::XLS_Type_CONTINUE: $this->_readContinue(); break;
  635. case self::XLS_Type_EOF: $this->_readDefault(); break 2;
  636. default: $this->_readDefault(); break;
  637. }
  638. }
  639. // treat MSODRAWING records, sheet-level Escher
  640. if (!$this->_readDataOnly && $this->_drawingData) {
  641. $escherWorksheet = new PHPExcel_Shared_Escher();
  642. $reader = new PHPExcel_Reader_Excel5_Escher($escherWorksheet);
  643. $escherWorksheet = $reader->load($this->_drawingData);
  644. // debug Escher stream
  645. //$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
  646. //$debug->load($this->_drawingData);
  647. // get all spContainers in one long array, so they can be mapped to OBJ records
  648. $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
  649. }
  650. // treat OBJ records
  651. foreach ($this->_objs as $n => $obj) {
  652. // the first shape container never has a corresponding OBJ record, hence $n + 1
  653. $spContainer = $allSpContainers[$n + 1];
  654. // we skip all spContainers that are a part of a group shape since we cannot yet handle those
  655. if ($spContainer->getNestingLevel() > 1) {
  656. continue;
  657. }
  658. // calculate the width and height of the shape
  659. list($startColumn, $startRow) = PHPExcel_Cell::coordinateFromString($spContainer->getStartCoordinates());
  660. list($endColumn, $endRow) = PHPExcel_Cell::coordinateFromString($spContainer->getEndCoordinates());
  661. $startOffsetX = $spContainer->getStartOffsetX();
  662. $startOffsetY = $spContainer->getStartOffsetY();
  663. $endOffsetX = $spContainer->getEndOffsetX();
  664. $endOffsetY = $spContainer->getEndOffsetY();
  665. $width = PHPExcel_Shared_Excel5::getDistanceX($this->_phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
  666. $height = PHPExcel_Shared_Excel5::getDistanceY($this->_phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
  667. // calculate offsetX and offsetY of the shape
  668. $offsetX = $startOffsetX * PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, $startColumn) / 1024;
  669. $offsetY = $startOffsetY * PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $startRow) / 256;
  670. switch ($obj['type']) {
  671. case 0x08:
  672. // picture
  673. // get index to BSE entry (1-based)
  674. $BSEindex = $spContainer->getOPT(0x0104);
  675. $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
  676. $BSE = $BSECollection[$BSEindex - 1];
  677. $blipType = $BSE->getBlipType();
  678. // need check because some blip types are not supported by Escher reader such as EMF
  679. if ($blip = $BSE->getBlip()) {
  680. $ih = imagecreatefromstring($blip->getData());
  681. $drawing = new PHPExcel_Worksheet_MemoryDrawing();
  682. $drawing->setImageResource($ih);
  683. // width, height, offsetX, offsetY
  684. $drawing->setResizeProportional(false);
  685. $drawing->setWidth($width);
  686. $drawing->setHeight($height);
  687. $drawing->setOffsetX($offsetX);
  688. $drawing->setOffsetY($offsetY);
  689. switch ($blipType) {
  690. case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG:
  691. $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG);
  692. $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_JPEG);
  693. break;
  694. case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG:
  695. $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG);
  696. $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_PNG);
  697. break;
  698. }
  699. $drawing->setWorksheet($this->_phpSheet);
  700. $drawing->setCoordinates($spContainer->getStartCoordinates());
  701. }
  702. break;
  703. default:
  704. // other object type
  705. break;
  706. }
  707. }
  708. // treat SHAREDFMLA records
  709. if ($this->_version == self::XLS_BIFF8) {
  710. foreach ($this->_sharedFormulaParts as $cell => $baseCell) {
  711. $formula = $this->_getFormulaFromStructure($this->_sharedFormulas[$baseCell], $cell);
  712. $this->_phpSheet->getCell($cell)->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA);
  713. }
  714. }
  715. }
  716. // add the named ranges (defined names)
  717. foreach ($this->_definedname as $definedName) {
  718. if ($definedName['isBuiltInName']) {
  719. switch ($definedName['name']) {
  720. case pack('C', 0x06):
  721. // print area
  722. // in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
  723. $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
  724. $extractedRanges = array();
  725. foreach ($ranges as $range) {
  726. // $range should look like one of these
  727. // Foo!$C$7:$J$66
  728. // Bar!$A$1:$IV$2
  729. $explodes = explode('!', $range); // FIXME: what if sheetname contains exclamation mark?
  730. $sheetName = $explodes[0];
  731. if (count($explodes) == 2) {
  732. $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
  733. }
  734. }
  735. if ($docSheet = $this->_phpExcel->getSheetByName($sheetName)) {
  736. $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
  737. }
  738. break;
  739. case pack('C', 0x07):
  740. // print titles (repeating rows)
  741. // Assuming BIFF8, there are 3 cases
  742. // 1. repeating rows
  743. // formula looks like this: Sheet!$A$1:$IV$2
  744. // rows 1-2 repeat
  745. // 2. repeating columns
  746. // formula looks like this: Sheet!$A$1:$B$65536
  747. // columns A-B repeat
  748. // 3. both repeating rows and repeating columns
  749. // formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
  750. $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
  751. foreach ($ranges as $range) {
  752. // $range should look like this one of these
  753. // Sheet!$A$1:$B$65536
  754. // Sheet!$A$1:$IV$2
  755. $explodes = explode('!', $range);
  756. if (count($explodes) == 2) {
  757. if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) {
  758. $extractedRange = $explodes[1];
  759. $extractedRange = str_replace('$', '', $extractedRange);
  760. $coordinateStrings = explode(':', $extractedRange);
  761. if (count($coordinateStrings) == 2) {
  762. list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[0]);
  763. list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[1]);
  764. if ($firstColumn == 'A' and $lastColumn == 'IV') {
  765. // then we have repeating rows
  766. $docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow, $lastRow));
  767. } elseif ($firstRow == 1 and $lastRow == 65536) {
  768. // then we have repeating columns
  769. $docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn, $lastColumn));
  770. }
  771. }
  772. }
  773. }
  774. }
  775. break;
  776. }
  777. } else {
  778. // Extract range
  779. $explodes = explode('!', $definedName['formula']);
  780. if (count($explodes) == 2) {
  781. if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) {
  782. $extractedRange = $explodes[1];
  783. $extractedRange = str_replace('$', '', $extractedRange);
  784. $localOnly = ($definedName['scope'] == 0) ? false : true;
  785. $scope = ($definedName['scope'] == 0) ?
  786. null : $this->_phpExcel->getSheetByName($this->_sheets[$definedName['scope'] - 1]['name']);
  787. $this->_phpExcel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $docSheet, $extractedRange, $localOnly, $scope) );
  788. }
  789. }
  790. }
  791. }
  792. return $this->_phpExcel;
  793. }
  794. /**
  795. * Use OLE reader to extract the relevant data streams from the OLE file
  796. *
  797. * @param string $pFilename
  798. */
  799. private function _loadOLE($pFilename)
  800. {
  801. // OLE reader
  802. $ole = new PHPExcel_Shared_OLERead();
  803. // get excel data
  804. $res = $ole->read($pFilename);
  805. $this->_data = $ole->getWorkBook();
  806. // Get summary information data
  807. $this->_summaryInformation = $ole->getSummaryInformation();
  808. }
  809. /**
  810. * Read summary information
  811. */
  812. private function _readSummaryInformation()
  813. {
  814. if (!isset($this->_summaryInformation)) {
  815. return;
  816. }
  817. // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
  818. // offset: 2; size: 2;
  819. // offset: 4; size: 2; OS version
  820. // offset: 6; size: 2; OS indicator
  821. // offset: 8; size: 16
  822. // offset: 24; size: 4; section count
  823. // 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
  824. // offset: 44; size: 4
  825. // section header
  826. // offset: 48; size: 4; section length
  827. $secLength = $this->_GetInt4d($this->_summaryInformation, 48);
  828. // offset: 52; size: 4; property count
  829. $countProperties = $this->_GetInt4d($this->_summaryInformation, 52);
  830. // initialize code page (used to resolve string values)
  831. $codePage = 'CP1252';
  832. // offset: 56; size: var
  833. // loop through property decarations and properties
  834. for ($i = 0; $i < $countProperties; ++$i) {
  835. // offset: 56 + 8 * $i; size: 4; property ID
  836. $id = $this->_GetInt4d($this->_summaryInformation, 56 + 8 * $i);
  837. // offset: 60 + 8 * $i; size: 4; offset from beginning of section (48)
  838. $offset = $this->_GetInt4d($this->_summaryInformation, 60 + 8 * $i);
  839. $type = $this->_GetInt4d($this->_summaryInformation, 48 + $offset);
  840. // initialize property value
  841. $value = null;
  842. // extract property value based on property type
  843. switch ($type) {
  844. case 0x02: // 2 byte signed integer
  845. $value = $this->_GetInt2d($this->_summaryInformation, 52 + $offset);
  846. break;
  847. case 0x03: // 4 byte signed integer
  848. $value = $this->_GetInt4d($this->_summaryInformation, 52 + $offset);
  849. break;
  850. case 0x13: // 4 byte unsigned integer
  851. // not needed yet, fix later if necessary
  852. break;
  853. case 0x1E: // null-terminated string prepended by dword string length
  854. $byteLength = $this->_GetInt4d($this->_summaryInformation, 52 + $offset);
  855. $value = substr($this->_summaryInformation, 56 + $offset, $byteLength);
  856. $value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage);
  857. $value = rtrim($value);
  858. break;
  859. case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
  860. // PHP-time
  861. $value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_summaryInformation, 52 + $offset, 8));
  862. break;
  863. case 0x47: // Clipboard format
  864. // not needed yet, fix later if necessary
  865. break;
  866. }
  867. // Use value of property id as appropriate
  868. switch ($id) {
  869. case 0x01: // Code Page
  870. $codePage = PHPExcel_Shared_CodePage::NumberToName($value);
  871. break;
  872. case 0x02: // Title
  873. $this->_phpExcel->getProperties()->setTitle($value);
  874. break;
  875. case 0x03: // Subject
  876. $this->_phpExcel->getProperties()->setSubject($value);
  877. break;
  878. case 0x04: // Author (Creator)
  879. $this->_phpExcel->getProperties()->setCreator($value);
  880. break;
  881. case 0x05: // Keywords
  882. $this->_phpExcel->getProperties()->setKeywords($value);
  883. break;
  884. case 0x06: // Comments (Description)
  885. $this->_phpExcel->getProperties()->setDescription($value);
  886. break;
  887. case 0x08: // Last Saved By (LastModifiedBy)
  888. $this->_phpExcel->getProperties()->setLastModifiedBy($value);
  889. break;
  890. case 0x09: // Revision
  891. // not supported by PHPExcel
  892. break;
  893. case 0x0C: // Created
  894. $this->_phpExcel->getProperties()->setCreated($value);
  895. break;
  896. case 0x0D: // Modified
  897. $this->_phpExcel->getProperties()->setModified($value);
  898. break;
  899. case 0x12: // Name of creating application
  900. // not supported by PHPExcel
  901. break;
  902. }
  903. }
  904. }
  905. /**
  906. * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
  907. */
  908. private function _readDefault()
  909. {
  910. $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
  911. $recordData = substr($this->_data, $this->_pos + 4, $length);
  912. // move stream pointer to next record
  913. $this->_pos += 4 + $length;
  914. }
  915. /**
  916. * Read BOF
  917. */
  918. private function _readBof()
  919. {
  920. $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
  921. $recordData = substr($this->_data, $this->_pos + 4, $length);
  922. // move stream pointer to next record
  923. $this->_pos += 4 + $length;
  924. // offset: 2; size: 2; type of the following data
  925. $substreamType = $this->_GetInt2d($recordData, 2);
  926. switch ($substreamType) {
  927. case self::XLS_WorkbookGlobals:
  928. $version = $this->_GetInt2d($recordData, 0);
  929. if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
  930. throw new Exception('Cannot read this Excel file. Version is too old.');
  931. }
  932. $this->_version = $version;
  933. break;
  934. case self::XLS_Worksheet:
  935. // do not use this version information for anything
  936. // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
  937. break;
  938. default:
  939. // substream, e.g. chart
  940. // just skip the entire substream
  941. do {
  942. $code = $this->_GetInt2d($this->_data, $this->_pos);
  943. $this->_readDefault();
  944. } while ($code != self::XLS_Type_EOF && $this->_pos < $this->_dataSize);
  945. break;
  946. }
  947. }
  948. /**
  949. * FILEPASS
  950. *
  951. * This record is part of the File Protection Block. It
  952. * contains information about the read/write password of the
  953. * file. All record contents following this record will be
  954. * encrypted.
  955. *
  956. * -- "OpenOffice.org's Documentation of the Microsoft
  957. * Excel File Format"
  958. */
  959. private function _readFilepass()
  960. {
  961. $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
  962. $recordData = substr($this->_data, $this->_pos + 4, $length);
  963. // move stream pointer to next record
  964. $this->_pos += 4 + $length;
  965. throw new Exception('Cannot read encrypted file');
  966. }
  967. /**
  968. * CODEPAGE
  969. *
  970. * This record stores the text encoding used to write byte
  971. * strings, stored as MS Windows code page identifier.
  972. *
  973. * -- "OpenOffice.org's Documentation of the Microsoft
  974. * Excel File Format"
  975. */
  976. private function _readCodepage()
  977. {
  978. $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
  979. $recordData = substr($this->_data, $this->_pos + 4, $length);
  980. // move stream pointer to next record
  981. $this->_pos += 4 + $length;
  982. // offset: 0; size: 2; code page identifier
  983. $codepage = $this->_GetInt2d($recordData, 0);
  984. $this->_codepage = PHPExcel_Shared_CodePage::NumberToName($codepage);
  985. }
  986. /**
  987. * DATEMODE
  988. *
  989. * This record specifies the base date for displaying date
  990. * values. All dates are stored as count of days past this
  991. * base date. In BIFF2-BIFF4 this record is part of the
  992. * Calculation Settings Block. In BIFF5-BIFF8 it is
  993. * stored in the Workbook Globals Substream.
  994. *
  995. * -- "OpenOffice.org's Documentation of the Microsoft
  996. * Excel File Format"
  997. */
  998. private function _readDateMode()
  999. {
  1000. $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
  1001. $recordData = substr($this->_data, $this->_pos + 4, $length);
  1002. // move stream pointer to next record
  1003. $this->_pos += 4 + $length;
  1004. // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
  1005. PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900);
  1006. if (ord($recordData{0}) == 1) {
  1007. PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_MAC_1904);
  1008. }
  1009. }
  1010. /**
  1011. * Read a FONT record
  1012. */
  1013. private function _readFont()
  1014. {
  1015. $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
  1016. $recordData = substr($this->_data, $this->_pos + 4, $length);
  1017. // move stream pointer to next record
  1018. $this->_pos += 4 + $length;
  1019. if (!$this->_readDataOnly) {
  1020. $objFont = new PHPExcel_Style_Font();
  1021. // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
  1022. $size = $this->_GetInt2d($recordData, 0);
  1023. $objFont->setSize($size / 20);
  1024. // offset: 2; size: 2; option flags
  1025. // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
  1026. // bit: 1; mask 0x0002; italic
  1027. $isItalic = (0x0002 & $this->_GetInt2d($recordData, 2)) >> 1;
  1028. if ($isItalic) $objFont->setItalic(true);
  1029. // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
  1030. // bit: 3; mask 0x0008; strike
  1031. $isStrike = (0x0008 & $this->_GetInt2d($recordData, 2)) >> 3;
  1032. if ($isStrike) $objFont->setStrikethrough(true);
  1033. // offset: 4; size: 2; colour index
  1034. $colorIndex = $this->_GetInt2d($recordData, 4);
  1035. $objFont->colorIndex = $colorIndex;
  1036. // offset: 6; size: 2; font weight
  1037. $weight = $this->_GetInt2d($recordData, 6);
  1038. switch ($weight) {
  1039. case 0x02BC:
  1040. $objFont->setBold(true);
  1041. break;
  1042. }
  1043. // offset: 8; size: 2; escapement type
  1044. $escapement = $this->_GetInt2d($recordData, 8);
  1045. switch ($escapement) {
  1046. case 0x0001:
  1047. $objFont->setSuperScript(true);
  1048. break;
  1049. case 0x0002:
  1050. $objFont->setSubScript(true);
  1051. break;
  1052. }
  1053. // offset: 10; size: 1; underline type
  1054. $underlineType = ord($recordData{10});
  1055. switch ($underlineType) {
  1056. case 0x00:
  1057. break; // no underline
  1058. case 0x01:
  1059. $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE);
  1060. break;
  1061. case 0x02:
  1062. $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLE);
  1063. break;
  1064. case 0x21:
  1065. $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING);
  1066. break;
  1067. case 0x22:
  1068. $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING);
  1069. break;
  1070. }
  1071. // offset: 11; size: 1; font family
  1072. // offset: 12; size: 1; character set
  1073. // offset: 13; size: 1; not used
  1074. // offset: 14; size: var; font name
  1075. if ($this->_version == self::XLS_BIFF8) {
  1076. $string = $this->_readUnicodeStringShort(substr($recordData, 14));
  1077. } else {
  1078. $string = $this->_readByteStringShort(substr($recordData, 14));
  1079. }
  1080. $objFont->setName($string['value']);
  1081. $this->_objFonts[] = $objFont;
  1082. }
  1083. }
  1084. /**
  1085. * FORMAT
  1086. *
  1087. * This record contains information about a number format.
  1088. * All FORMAT records occur together in a sequential list.
  1089. *
  1090. * In BIFF2-BIFF4 other records referencing a FORMAT record
  1091. * contain a zero-based index into this list. From BIFF5 on
  1092. * the FORMAT record contains the index itself that will be
  1093. * used by other records.
  1094. *
  1095. * -- "OpenOffice.org's Documentation of the Microsoft
  1096. * Excel File Format"
  1097. */
  1098. private function _readFormat()
  1099. {
  1100. $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
  1101. $recordData = substr($this->_data, $this->_pos + 4, $length);
  1102. // move stream pointer to next record
  1103. $this->_pos += 4 + $length;
  1104. if (!$this->_readDataOnly) {
  1105. $indexCode = $this->_GetInt2d($recordData, 0);
  1106. if ($this->_version == self::XLS_BIFF8) {
  1107. $string = $this->_readUnicodeStringLong(substr($recordData, 2));
  1108. } else {
  1109. // BIFF7
  1110. $string = $this->_readByteStringShort(substr($recordData, 2));
  1111. }
  1112. $formatString = $string['value'];
  1113. $this->_formats[$indexCode] = $formatString;
  1114. }
  1115. }
  1116. /**
  1117. * XF - Extended Format
  1118. *
  1119. * This record contains formatting information for cells, rows, columns or styles.
  1120. * According to http://support.microsoft.com/kb/147732 there are always at least 15 cell style XF
  1121. * and 1 cell XF.
  1122. * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF
  1123. * and XF record 15 is a cell XF
  1124. * We only read the first cell style XF and skip the remaining cell style XF records
  1125. * We read all cell XF records.
  1126. *
  1127. * -- "OpenOffice.org's Documentation of the Microsoft
  1128. * Excel File Format"
  1129. */
  1130. private function _readXf()
  1131. {
  1132. $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
  1133. $recordData = substr($this->_data, $this->_pos + 4, $length);
  1134. // move stream pointer to next record
  1135. $this->_pos += 4 + $length;
  1136. $objStyle = new PHPExcel_Style();
  1137. if (!$this->_readDataOnly) {
  1138. // offset: 0; size: 2; Index to FONT record
  1139. if ($this->_GetInt2d($recordData, 0) < 4) {
  1140. $fontIndex = $this->_GetInt2d($recordData, 0);
  1141. } else {
  1142. // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
  1143. // check the OpenOffice documentation of the FONT record
  1144. $fontIndex = $this->_GetInt2d($recordData, 0) - 1;
  1145. }
  1146. $objStyle->setFont($this->_objFonts[$fontIndex]);
  1147. // offset: 2; size: 2; Index to FORMAT record
  1148. $numberFormatIndex = $this->_GetInt2d($recordData, 2);
  1149. if (isset($this->_formats[$numberFormatIndex])) {
  1150. // then we have user-defined format code
  1151. $numberformat = array('code' => $this->_formats[$numberFormatIndex]);
  1152. } elseif (($code = PHPExcel_Style_NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') {
  1153. // then we have built-in format code
  1154. $numberformat = array('code' => $code);
  1155. } else {
  1156. // we set the general format code
  1157. $numberformat = array('code' => 'General');
  1158. }
  1159. $objStyle->getNumberFormat()->setFormatCode($numberformat['code']);
  1160. // offset: 4; size: 2; XF type, cell protection, and parent style XF
  1161. // bit 2-0; mask 0x0007; XF_TYPE_PROT
  1162. $xfTypeProt = $this->_GetInt2d($recordData, 4);
  1163. // bit 0; mask 0x01; 1 = cell is locked
  1164. $isLocked = (0x01 & $xfTypeProt) >> 0;
  1165. $objStyle->getProtection()->setLocked($isLocked ?
  1166. PHPExcel_Style_Protection::PROTECTION_INHERIT : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED);
  1167. // bit 1; mask 0x02; 1 = Formula is hidden
  1168. $isHidden = (0x02 & $xfTypeProt) >> 1;
  1169. $objStyle->getProtection()->setHidden($isHidden ?
  1170. PHPExcel_Style_Protection::PROTECTION_PROTECTED : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED);
  1171. // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF
  1172. $isCellStyleXf = (0x04 & $xfTypeProt) >> 2;
  1173. // offset: 6; size: 1; Alignment and text break
  1174. // bit 2-0, mask 0x07; horizontal alignment
  1175. $horAlign = (0x07 & ord($recordData{6})) >> 0;
  1176. switch ($horAlign) {
  1177. case 0:
  1178. $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_GENERAL);
  1179. break;
  1180. case 1:
  1181. $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1182. break;
  1183. case 2:
  1184. $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1185. break;
  1186. case 3:
  1187. $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
  1188. break;
  1189. case 5:
  1190. $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY);
  1191. break;
  1192. case 6:
  1193. $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS);
  1194. break;
  1195. }
  1196. // bit 3, mask 0x08; wrap text
  1197. $wrapText = (0x08 & ord($recordData{6})) >> 3;
  1198. switch ($wrapText) {
  1199. case 0:
  1200. $objStyle->getAlignment()->setWrapText(false);
  1201. break;
  1202. case 1:
  1203. $objStyle->getAlignment()->setWrapText(true);
  1204. break;
  1205. }
  1206. // bit 6-4, mask 0x70; vertical alignment
  1207. $vertAlign = (0x70 & ord($recordData{6})) >> 4;
  1208. switch ($vertAlign) {
  1209. case 0:
  1210. $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_TOP);
  1211. break;
  1212. case 1:
  1213. $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);
  1214. break;
  1215. case 2:
  1216. $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_BOTTOM);
  1217. break;
  1218. case 3:
  1219. $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_JUSTIFY);
  1220. break;
  1221. }
  1222. if ($this->_version == self::XLS_BIFF8) {
  1223. // offset: 7; size: 1; XF_ROTATION: Text rotation angle
  1224. $angle = ord($recordData{7});
  1225. $rotation = 0;
  1226. if ($angle <= 90) {
  1227. $rotation = $angle;
  1228. } else if ($angle <= 180) {
  1229. $rotation = 90 - $angle;
  1230. } else if ($angle == 255) {
  1231. $rotation = -165;
  1232. }
  1233. $objStyle->getAlignment()->setTextRotation($rotation);
  1234. // offset: 8; size: 1; Indentation, shrink to cell size, and text direction
  1235. // bit: 3-0; mask: 0x0F; indent level
  1236. $indent = (0x0F & ord($recordData{8})) >> 0;
  1237. $objStyle->getAlignment()->setIndent($indent);
  1238. // bit: 4; mask: 0x10; 1 = shrink content to fit into cell
  1239. $shrinkToFit = (0x10 & ord($recordData{8})) >> 4;
  1240. switch ($shrinkToFit) {
  1241. case 0:
  1242. $objStyle->getAlignment()->setShrinkToFit(false);
  1243. break;
  1244. case 1:
  1245. $objStyle->getAlignment()->setShrinkToFit(true);
  1246. break;
  1247. }
  1248. // offset: 9; size: 1; Flags used for attribute groups
  1249. // offset: 10; size: 4; Cell border lines and background area
  1250. // bit: 3-0; mask: 0x0000000F; left style
  1251. if ($bordersLeftStyle = $this->_mapBorderStyle((0x0000000F & $this->_GetInt4d($recordData, 10)) >> 0)) {
  1252. $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
  1253. }
  1254. // bit: 7-4; mask: 0x000000F0; right style
  1255. if ($bordersRightStyle = $this->_mapBorderStyle((0x000000F0 & $this->_GetInt4d($recordData, 10)) >> 4)) {
  1256. $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
  1257. }
  1258. // bit: 11-8; mask: 0x00000F00; top style
  1259. if ($bordersTopStyle = $this->_mapBorderStyle((0x00000F00 & $this->_GetInt4d($recordData, 10)) >> 8)) {
  1260. $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
  1261. }
  1262. // bit: 15-12; mask: 0x0000F000; bottom style
  1263. if ($bordersBottomStyle = $this->_mapBorderStyle((0x0000F000 & $this->_GetInt4d($recordData, 10)) >> 12)) {
  1264. $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
  1265. }
  1266. // bit: 22-16; mask: 0x007F0000; left color
  1267. $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $this->_GetInt4d($recordData, 10)) >> 16;
  1268. // bit: 29-23; mask: 0x3F800000; right color
  1269. $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $this->_GetInt4d($recordData, 10)) >> 23;
  1270. // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
  1271. $diagonalDown = (0x40000000 & $this->_GetInt4d($recordData, 10)) >> 30 ?
  1272. true : false;
  1273. // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
  1274. $diagonalUp = (0x80000000 & $this->_GetInt4d($recordData, 10)) >> 31 ?
  1275. true : false;
  1276. if ($diagonalUp == false && $diagonalDown == false) {
  1277. $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_NONE);
  1278. } elseif ($diagonalUp == true && $diagonalDown == false) {
  1279. $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_UP);
  1280. } elseif ($diagonalUp == false && $diagonalDown == true) {
  1281. $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_DOWN);
  1282. } elseif ($diagonalUp == true && $diagonalDown == true) {
  1283. $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_BOTH);
  1284. }
  1285. // offset: 14; size: 4;
  1286. // bit: 6-0; mask: 0x0000007F; top color
  1287. $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & $this->_GetInt4d($recordData, 14)) >> 0;
  1288. // bit: 13-7; mask: 0x00003F80; bottom color
  1289. $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & $this->_GetInt4d($recordData, 14)) >> 7;
  1290. // bit: 20-14; mask: 0x001FC000; diagonal color
  1291. $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & $this->_GetInt4d($recordData, 14)) >> 14;
  1292. // bit: 24-21; mask: 0x01E00000; diagonal style
  1293. if ($bordersDiagonalStyle = $this->_mapBorderStyle((0x01E00000 & $this->_GetInt4d($recordData, 14))

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