PluginProbe
TablePress – Tables in WordPress made easy / 1.9.2
TablePress – Tables in WordPress made easy v1.9.2
3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 2.4.4 All 44 releases
tablepress / libraries / excel-reader.class.php

excel-reader.class.php in TablePress – Tables in WordPress made easy 1.9.2, at libraries/excel-reader.class.php

2,536 lines 73.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Excel 97/2003 Reader Class
4 *
5 * Based on PHP Excel Reader 2.21.
6 * @link https://code.google.com/archive/p/php-excel-reader/
7 *
8 * @package TablePress
9 * @subpackage Import
10 * @author Matt Kruse, Matt Roxburgh, Vadim Tkachenko, Tobias Bäthge
11 * @since 1.1.0
12 */
13
14 // Prohibit direct script loading.
15 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
16
17 /**
18 * A class for reading Microsoft Excel (97/2003) Spreadsheets.
19 *
20 * Version 2.21
21 *
22 * Enhanced and maintained by Matt Kruse <https://mattkruse.com/>
23 * Maintained at https://code.google.com/archive/p/php-excel-reader/
24 * Licensed under MIT license
25 *
26 * Format parsing and MUCH more contributed by Matt Roxburgh
27 *
28 * Cleanup and changes for TablePress by Tobias Bäthge
29 * --------------------------------------------------------------------------
30 */
31
32 define( 'NUM_BIG_BLOCK_DEPOT_BLOCKS_POS', 0x2c );
33 define( 'SMALL_BLOCK_DEPOT_BLOCK_POS', 0x3c );
34 define( 'ROOT_START_BLOCK_POS', 0x30 );
35 define( 'BIG_BLOCK_SIZE', 0x200 );
36 define( 'SMALL_BLOCK_SIZE', 0x40 );
37 define( 'EXTENSION_BLOCK_POS', 0x44 );
38 define( 'NUM_EXTENSION_BLOCK_POS', 0x48 );
39 define( 'PROPERTY_STORAGE_BLOCK_SIZE', 0x80 );
40 define( 'BIG_BLOCK_DEPOT_BLOCKS_POS', 0x4c );
41 define( 'SMALL_BLOCK_THRESHOLD', 0x1000 );
42
43 // property storage offsets
44 define( 'SIZE_OF_NAME_POS', 0x40 );
45 define( 'TYPE_POS', 0x42 );
46 define( 'START_BLOCK_POS', 0x74 );
47 define( 'SIZE_POS', 0x78 );
48
49 define( 'IDENTIFIER_OLE', pack( 'CCCCCCCC', 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 ) );
50
51 /**
52 * OLERead class
53 */
54 class OLERead {
55
56 /**
57 * [$data description]
58 *
59 * @since 1.0.0
60 * @var string
61 */
62 protected $data = '';
63
64 /**
65 * [$error description]
66 *
67 * @since 1.0.0
68 * @var int
69 */
70 public $error;
71
72 /**
73 * [$bigBlockChain description]
74 *
75 * @since 1.0.0
76 * @var array
77 */
78 protected $bigBlockChain = array();
79
80 /**
81 * [$smallBlockChain description]
82 *
83 * @since 1.0.0
84 * @var array
85 */
86 protected $smallBlockChain = array();
87
88 /**
89 * [$entry description]
90 *
91 * @since 1.0.0
92 * @var [type]
93 */
94 protected $entry;
95
96 /**
97 * [$props description]
98 *
99 * @since 1.0.0
100 * @var [type]
101 */
102 protected $props;
103
104 /**
105 * [$wrkbook description]
106 *
107 * @since 1.0.0
108 * @var [type]
109 */
110 protected $wrkbook;
111
112 /**
113 * [$rootentry description]
114 *
115 * @since 1.0.0
116 * @var [type]
117 */
118 protected $rootentry;
119
120 /**
121 * Class constructor.
122 *
123 * @since 1.0.0
124 */
125 public function __construct() {
126 // Unused.
127 }
128
129 /**
130 * [read description]
131 *
132 * @since 1.0.0
133 *
134 * @param [type] $data [description]
135 * @return [type] [description]
136 */
137 public function read( $data ) {
138 $this->data = $data;
139 if ( ! $this->data ) {
140 $this->error = 1;
141 return false;
142 }
143 if ( IDENTIFIER_OLE !== substr( $this->data, 0, 8 ) ) {
144 $this->error = 2;
145 return false;
146 }
147 $numBigBlockDepotBlocks = $this->_GetInt4d( $this->data, NUM_BIG_BLOCK_DEPOT_BLOCKS_POS );
148 $sbdStartBlock = $this->_GetInt4d( $this->data, SMALL_BLOCK_DEPOT_BLOCK_POS );
149 $rootStartBlock = $this->_GetInt4d( $this->data, ROOT_START_BLOCK_POS );
150 $extensionBlock = $this->_GetInt4d( $this->data, EXTENSION_BLOCK_POS );
151 $numExtensionBlocks = $this->_GetInt4d( $this->data, NUM_EXTENSION_BLOCK_POS );
152
153 $bigBlockDepotBlocks = array();
154 $pos = BIG_BLOCK_DEPOT_BLOCKS_POS;
155 $bbdBlocks = $numBigBlockDepotBlocks;
156 if ( 0 !== $numExtensionBlocks ) {
157 $bbdBlocks = ( BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS ) / 4;
158 }
159
160 for ( $i = 0; $i < $bbdBlocks; $i++ ) {
161 $bigBlockDepotBlocks[ $i ] = $this->_GetInt4d( $this->data, $pos );
162 $pos += 4;
163 }
164
165 for ( $j = 0; $j < $numExtensionBlocks; $j++ ) {
166 $pos = ( $extensionBlock + 1 ) * BIG_BLOCK_SIZE;
167 $blocksToRead = min( $numBigBlockDepotBlocks - $bbdBlocks, BIG_BLOCK_SIZE / 4 - 1 );
168
169 for ( $i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; $i++ ) {
170 $bigBlockDepotBlocks[ $i ] = $this->_GetInt4d( $this->data, $pos );
171 $pos += 4;
172 }
173
174 $bbdBlocks += $blocksToRead;
175 if ( $bbdBlocks < $numBigBlockDepotBlocks ) {
176 $extensionBlock = $this->_GetInt4d( $this->data, $pos );
177 }
178 }
179
180 // readBigBlockDepot()
181 $index = 0;
182 $this->bigBlockChain = array();
183
184 for ( $i = 0; $i < $numBigBlockDepotBlocks; $i++ ) {
185 $pos = ( $bigBlockDepotBlocks[ $i ] + 1 ) * BIG_BLOCK_SIZE;
186 for ( $j = 0; $j < BIG_BLOCK_SIZE / 4; $j++ ) {
187 $this->bigBlockChain[ $index ] = $this->_GetInt4d( $this->data, $pos );
188 $pos += 4;
189 $index++;
190 }
191 }
192
193 // readSmallBlockDepot();
194 $index = 0;
195 $sbdBlock = $sbdStartBlock;
196 $this->smallBlockChain = array();
197
198 while ( -2 !== $sbdBlock ) {
199 $pos = ( $sbdBlock + 1 ) * BIG_BLOCK_SIZE;
200 for ( $j = 0; $j < BIG_BLOCK_SIZE / 4; $j++ ) {
201 $this->smallBlockChain[ $index ] = $this->_GetInt4d( $this->data, $pos );
202 $pos += 4;
203 $index++;
204 }
205 $sbdBlock = $this->bigBlockChain[ $sbdBlock ];
206 }
207
208 // readData(rootStartBlock)
209 $block = $rootStartBlock;
210 $this->entry = $this->__readData( $block );
211 $this->__readPropertySets();
212 }
213
214 /**
215 * [__readData description]
216 *
217 * @since 1.0.0
218 *
219 * @param [type] $bl [description]
220 * @return [type] [description]
221 */
222 protected function __readData( $bl ) {
223 $block = $bl;
224 $data = '';
225 while ( -2 !== $block ) {
226 $pos = ( $block + 1 ) * BIG_BLOCK_SIZE;
227 $data = $data . substr( $this->data, $pos, BIG_BLOCK_SIZE );
228 $block = $this->bigBlockChain[ $block ];
229 }
230 return $data;
231 }
232
233 /**
234 * [__readPropertySets description]
235 *
236 * @since 1.0.0
237 *
238 * @return [type] [description]
239 */
240 protected function __readPropertySets() {
241 $offset = 0;
242 while ( $offset < strlen( $this->entry ) ) {
243 $d = substr( $this->entry, $offset, PROPERTY_STORAGE_BLOCK_SIZE );
244 $nameSize = ord( $d[ SIZE_OF_NAME_POS ] ) | ( ord( $d[ SIZE_OF_NAME_POS + 1 ] ) << 8 );
245 $type = ord( $d[ TYPE_POS ] );
246 $startBlock = $this->_GetInt4d( $d, START_BLOCK_POS );
247 $size = $this->_GetInt4d( $d, SIZE_POS );
248 $name = '';
249 for ( $i = 0; $i < $nameSize; $i++ ) {
250 $name .= $d[ $i ];
251 }
252 $name = str_replace( "\x00", '', $name );
253 $this->props[] = array(
254 'name' => $name,
255 'type' => $type,
256 'startBlock' => $startBlock,
257 'size' => $size,
258 );
259 if ( 'workbook' === strtolower( $name ) || 'book' === strtolower( $name ) ) {
260 $this->wrkbook = count( $this->props ) - 1;
261 }
262 if ( 'Root Entry' === $name ) {
263 $this->rootentry = count( $this->props ) - 1;
264 }
265 $offset += PROPERTY_STORAGE_BLOCK_SIZE;
266 }
267 }
268
269 /**
270 * [getWorkBook description]
271 *
272 * @since 1.0.0
273 *
274 * @return [type] [description]
275 */
276 public function getWorkBook() {
277 if ( $this->props[ $this->wrkbook ]['size'] < SMALL_BLOCK_THRESHOLD ) {
278 $rootdata = $this->__readData( $this->props[ $this->rootentry ]['startBlock'] );
279 $streamData = '';
280 $block = $this->props[ $this->wrkbook ]['startBlock'];
281 while ( -2 !== $block ) {
282 $pos = $block * SMALL_BLOCK_SIZE;
283 $streamData .= substr( $rootdata, $pos, SMALL_BLOCK_SIZE );
284 $block = $this->smallBlockChain[ $block ];
285 }
286 return $streamData;
287 } else {
288 $numBlocks = $this->props[ $this->wrkbook ]['size'] / BIG_BLOCK_SIZE;
289 if ( 0 !== $this->props[ $this->wrkbook ]['size'] % BIG_BLOCK_SIZE ) {
290 $numBlocks++;
291 }
292
293 if ( 0 === $numBlocks ) {
294 return '';
295 }
296 $streamData = '';
297 $block = $this->props[ $this->wrkbook ]['startBlock'];
298 while ( -2 !== $block ) {
299 $pos = ( $block + 1 ) * BIG_BLOCK_SIZE;
300 $streamData .= substr( $this->data, $pos, BIG_BLOCK_SIZE );
301 $block = $this->bigBlockChain[ $block ];
302 }
303 return $streamData;
304 }
305 }
306
307 /**
308 * [_GetInt4d description]
309 *
310 * @since 1.0.0
311 *
312 * @param [type] $data [description]
313 * @param [type] $pos [description]
314 * @return [type] [description]
315 */
316 protected function _GetInt4d( $data, $pos ) {
317 $value = ord( $data[ $pos ] ) | ( ord( $data[ $pos + 1 ] ) << 8 ) | ( ord( $data[ $pos + 2 ] ) << 16 ) | ( ord( $data[ $pos + 3 ] ) << 24 );
318 if ( $value >= 4294967294 ) {
319 $value = -2;
320 }
321 return $value;
322 }
323
324 } // class OLERead
325
326 define( 'SPREADSHEET_EXCEL_READER_BIFF8', 0x600 );
327 define( 'SPREADSHEET_EXCEL_READER_BIFF7', 0x500 );
328 define( 'SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS', 0x5 );
329 define( 'SPREADSHEET_EXCEL_READER_WORKSHEET', 0x10 );
330 define( 'SPREADSHEET_EXCEL_READER_TYPE_BOF', 0x809 );
331 define( 'SPREADSHEET_EXCEL_READER_TYPE_EOF', 0x0a );
332 define( 'SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET', 0x85 );
333 define( 'SPREADSHEET_EXCEL_READER_TYPE_DIMENSION', 0x200 );
334 define( 'SPREADSHEET_EXCEL_READER_TYPE_ROW', 0x208 );
335 define( 'SPREADSHEET_EXCEL_READER_TYPE_DBCELL', 0xd7 );
336 define( 'SPREADSHEET_EXCEL_READER_TYPE_FILEPASS', 0x2f );
337 define( 'SPREADSHEET_EXCEL_READER_TYPE_NOTE', 0x1c );
338 define( 'SPREADSHEET_EXCEL_READER_TYPE_TXO', 0x1b6 );
339 define( 'SPREADSHEET_EXCEL_READER_TYPE_RK', 0x7e );
340 define( 'SPREADSHEET_EXCEL_READER_TYPE_RK2', 0x27e );
341 define( 'SPREADSHEET_EXCEL_READER_TYPE_MULRK', 0xbd );
342 define( 'SPREADSHEET_EXCEL_READER_TYPE_MULBLANK', 0xbe );
343 define( 'SPREADSHEET_EXCEL_READER_TYPE_INDEX', 0x20b );
344 define( 'SPREADSHEET_EXCEL_READER_TYPE_SST', 0xfc );
345 define( 'SPREADSHEET_EXCEL_READER_TYPE_EXTSST', 0xff );
346 define( 'SPREADSHEET_EXCEL_READER_TYPE_CONTINUE', 0x3c );
347 define( 'SPREADSHEET_EXCEL_READER_TYPE_LABEL', 0x204 );
348 define( 'SPREADSHEET_EXCEL_READER_TYPE_LABELSST', 0xfd );
349 define( 'SPREADSHEET_EXCEL_READER_TYPE_NUMBER', 0x203 );
350 define( 'SPREADSHEET_EXCEL_READER_TYPE_NAME', 0x18 );
351 define( 'SPREADSHEET_EXCEL_READER_TYPE_ARRAY', 0x221 );
352 define( 'SPREADSHEET_EXCEL_READER_TYPE_STRING', 0x207 );
353 define( 'SPREADSHEET_EXCEL_READER_TYPE_FORMULA', 0x406 );
354 define( 'SPREADSHEET_EXCEL_READER_TYPE_FORMULA2', 0x6 );
355 define( 'SPREADSHEET_EXCEL_READER_TYPE_FORMAT', 0x41e );
356 define( 'SPREADSHEET_EXCEL_READER_TYPE_XF', 0xe0 );
357 define( 'SPREADSHEET_EXCEL_READER_TYPE_BOOLERR', 0x205 );
358 define( 'SPREADSHEET_EXCEL_READER_TYPE_FONT', 0x0031 );
359 define( 'SPREADSHEET_EXCEL_READER_TYPE_PALETTE', 0x0092 );
360 define( 'SPREADSHEET_EXCEL_READER_TYPE_UNKNOWN', 0xffff );
361 define( 'SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR', 0x22 );
362 define( 'SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS', 0xE5 );
363 define( 'SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS', 25569 );
364 define( 'SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904', 24107 );
365 define( 'SPREADSHEET_EXCEL_READER_MSINADAY', 86400 );
366 define( 'SPREADSHEET_EXCEL_READER_TYPE_HYPER', 0x01b8 );
367 define( 'SPREADSHEET_EXCEL_READER_TYPE_COLINFO', 0x7d );
368 define( 'SPREADSHEET_EXCEL_READER_TYPE_DEFCOLWIDTH', 0x55 );
369 define( 'SPREADSHEET_EXCEL_READER_TYPE_STANDARDWIDTH', 0x99 );
370 define( 'SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT', '%s' );
371
372 /*
373 * Main Class
374 */
375 class Spreadsheet_Excel_Reader {
376
377 /*
378 * The following four public constants were added to make data retrieval easier.
379 */
380
381 /**
382 * [$colnames description]
383 *
384 * @since 1.0.0
385 * @var array
386 */
387 public $colnames = array();
388
389 /**
390 * [$colindexes description]
391 *
392 * @since 1.0.0
393 * @var array
394 */
395 public $colindexes = array();
396
397 /**
398 * [$standardColWidth description]
399 *
400 * @since 1.0.0
401 * @var int
402 */
403 public $standardColWidth = 0;
404
405 /**
406 * [$defaultColWidth description]
407 *
408 * @since 1.0.0
409 * @var int
410 */
411 public $defaultColWidth = 0;
412
413 /**
414 * [$store_extended_info description]
415 *
416 * @since 1.0.0
417 * @var [type]
418 */
419 protected $store_extended_info;
420
421 /**
422 * [$_encoderFunction description]
423 *
424 * @since 1.0.0
425 * @var [type]
426 */
427 protected $_encoderFunction;
428
429 /**
430 * [$nineteenFour description]
431 *
432 * @since 1.0.0
433 * @var [type]
434 */
435 protected $nineteenFour;
436
437 /**
438 * [$sn description]
439 *
440 * @since 1.0.0
441 * @var [type]
442 */
443 protected $sn;
444
445 /**
446 * [myHex description]
447 *
448 * @since 1.0.0
449 *
450 * @param [type] $d [description]
451 * @return [type] [description]
452 */
453 protected function myHex( $d ) {
454 if ( $d < 16 ) {
455 return '0' . dechex( $d );
456 }
457 return dechex( $d );
458 }
459
460 /**
461 * [dumpHexData description]
462 *
463 * @since 1.0.0
464 *
465 * @param [type] $data [description]
466 * @param [type] $pos [description]
467 * @param [type] $length [description]
468 * @return [type] [description]
469 */
470 protected function dumpHexData( $data, $pos, $length ) {
471 $info = '';
472 for ( $i = 0; $i <= $length; $i++ ) {
473 if ( 0 !== $i ) {
474 $info .= ' ';
475 }
476 $info .= $this->myHex( ord( $data[ $pos + $i ] ) ) . ( ord( $data[ $pos + $i ] ) > 31 ? '[' . $data[ $pos + $i ] . ']' : '' );
477 }
478 return $info;
479 }
480
481 /**
482 * [getCol description]
483 *
484 * @since 1.0.0
485 *
486 * @param [type] $col [description]
487 * @return [type] [description]
488 */
489 protected function getCol( $col ) {
490 if ( is_string( $col ) ) {
491 $col = strtolower( $col );
492 if ( array_key_exists( $col, $this->colnames ) ) {
493 $col = $this->colnames[ $col ];
494 }
495 }
496 return $col;
497 }
498
499 // PUBLIC API FUNCTIONS
500 // --------------------
501
502 /**
503 * [val description]
504 *
505 * @since 1.0.0
506 *
507 * @param [type] $row [description]
508 * @param [type] $col [description]
509 * @param int $sheet Optional. [description]
510 * @return [type] [description]
511 */
512 public function val( $row, $col, $sheet = 0 ) {
513 $col = $this->getCol( $col );
514 if ( array_key_exists( $row, $this->sheets[ $sheet ]['cells'] ) && array_key_exists( $col, $this->sheets[ $sheet ]['cells'][ $row ] ) ) {
515 return $this->sheets[ $sheet ]['cells'][ $row ][ $col ];
516 }
517 return '';
518 }
519
520 /**
521 * [value description]
522 *
523 * @since 1.0.0
524 *
525 * @param [type] $row [description]
526 * @param [type] $col [description]
527 * @param int $sheet Optional. [description]
528 * @return [type] [description]
529 */
530 public function value( $row, $col, $sheet = 0 ) {
531 return $this->val( $row, $col, $sheet );
532 }
533
534 /**
535 * [info description]
536 *
537 * @since 1.0.0
538 *
539 * @param [type] $row [description]
540 * @param [type] $col [description]
541 * @param string $type Optional. [description]
542 * @param int $sheet Optional. [description]
543 * @return [type] [description]
544 */
545 public function info( $row, $col, $type = '', $sheet = 0 ) {
546 $col = $this->getCol( $col );
547 if ( array_key_exists( 'cellsInfo', $this->sheets[ $sheet ] )
548 && array_key_exists( $row, $this->sheets[ $sheet ]['cellsInfo'] )
549 && array_key_exists( $col, $this->sheets[ $sheet ]['cellsInfo'][ $row ] )
550 && array_key_exists( $type, $this->sheets[ $sheet ]['cellsInfo'][ $row ][ $col ] ) ) {
551 return $this->sheets[ $sheet ]['cellsInfo'][ $row ][ $col ][ $type ];
552 }
553 return '';
554 }
555
556 /**
557 * [type description]
558 *
559 * @since 1.0.0
560 *
561 * @param [type] $row [description]
562 * @param [type] $col [description]
563 * @param int $sheet Optional. [description]
564 * @return [type] [description]
565 */
566 public function type( $row, $col, $sheet = 0 ) {
567 return $this->info( $row, $col, 'type', $sheet );
568 }
569
570 /**
571 * [raw description]
572 *
573 * @since 1.0.0
574 *
575 * @param [type] $row [description]
576 * @param [type] $col [description]
577 * @param int $sheet Optional. [description]
578 * @return [type] [description]
579 */
580 public function raw( $row, $col, $sheet = 0 ) {
581 return $this->info( $row, $col, 'raw', $sheet );
582 }
583
584 /**
585 * [rowspan description]
586 *
587 * @since 1.0.0
588 *
589 * @param [type] $row [description]
590 * @param [type] $col [description]
591 * @param int $sheet Optional. [description]
592 * @return [type] [description]
593 */
594 public function rowspan( $row, $col, $sheet = 0 ) {
595 $value = $this->info( $row, $col, 'rowspan', $sheet );
596 if ( '' === $value ) {
597 return 1;
598 } else {
599 $value = (int) $value;
600 }
601 return $value;
602 }
603
604 /**
605 * [colspan description]
606 *
607 * @since 1.0.0
608 *
609 * @param [type] $row [description]
610 * @param [type] $col [description]
611 * @param int $sheet Optional. [description]
612 * @return [type] [description]
613 */
614 public function colspan( $row, $col, $sheet = 0 ) {
615 $value = $this->info( $row, $col, 'colspan', $sheet );
616 if ( '' === $value ) {
617 return 1;
618 } else {
619 $value = (int) $value;
620 }
621 return $value;
622 }
623
624 /**
625 * [hyperlink description]
626 *
627 * @since 1.0.0
628 *
629 * @param [type] $row [description]
630 * @param [type] $col [description]
631 * @param int $sheet Optional. [description]
632 * @return [type] [description]
633 */
634 public function hyperlink( $row, $col, $sheet = 0 ) {
635 $link = $this->sheets[ $sheet ]['cellsInfo'][ $row ][ $col ]['hyperlink'];
636 if ( $link ) {
637 return $link['link'];
638 }
639 return '';
640 }
641
642 /**
643 * [rowcount description]
644 *
645 * @since 1.0.0
646 *
647 * @param int $sheet Optional. [description]
648 * @return [type] [description]
649 */
650 public function rowcount( $sheet = 0 ) {
651 return $this->sheets[ $sheet ]['numRows'];
652 }
653
654 /**
655 * [colcount description]
656 *
657 * @since 1.0.0
658 *
659 * @param int $sheet Optional. [description]
660 * @return [type] [description]
661 */
662 public function colcount( $sheet = 0 ) {
663 return $this->sheets[ $sheet ]['numCols'];
664 }
665
666 /**
667 * [colwidth description]
668 *
669 * @since 1.0.0
670 *
671 * @param [type] $col [description]
672 * @param int $sheet Optional. [description]
673 * @return [type] [description]
674 */
675 public function colwidth( $col, $sheet = 0 ) {
676 // Col width is actually the width of the number 0. So we have to estimate and come close
677 return $this->colInfo[ $sheet ][ $col ]['width'] / 9142 * 200;
678 }
679
680 /**
681 * [colhidden description]
682 *
683 * @since 1.0.0
684 *
685 * @param [type] $col [description]
686 * @param int $sheet Optional. [description]
687 * @return [type] [description]
688 */
689 public function colhidden( $col, $sheet = 0 ) {
690 return (bool) $this->colInfo[ $sheet ][ $col ]['hidden'];
691 }
692
693 /**
694 * [rowheight description]
695 *
696 * @since 1.0.0
697 *
698 * @param [type] $row [description]
699 * @param int $sheet Optional. [description]
700 * @return [type] [description]
701 */
702 public function rowheight( $row, $sheet = 0 ) {
703 return $this->rowInfo[ $sheet ][ $row ]['height'];
704 }
705
706 /**
707 * [rowhidden description]
708 *
709 * @since 1.0.0
710 *
711 * @param [type] $row [description]
712 * @param int $sheet Optional. [description]
713 * @return [type] [description]
714 */
715 public function rowhidden( $row, $sheet = 0 ) {
716 return (bool) $this->rowInfo[ $sheet ][ $row ]['hidden'];
717 }
718
719 // GET THE CSS FOR FORMATTING
720 // ==========================
721
722 /**
723 * [style description]
724 *
725 * @since 1.0.0
726 *
727 * @param [type] $row [description]
728 * @param [type] $col [description]
729 * @param int $sheet Optional. [description]
730 * @return [type] [description]
731 */
732 public function style( $row, $col, $sheet = 0 ) {
733 $css = '';
734 $font = $this->font( $row, $col, $sheet );
735 if ( '' !== $font ) {
736 $css .= "font-family:{$font};";
737 }
738 $align = $this->align( $row, $col, $sheet );
739 if ( '' !== $align ) {
740 $css .= "text-align:{$align};";
741 }
742 $height = $this->height( $row, $col, $sheet );
743 if ( '' !== $height ) {
744 $css .= "font-size:{$height}px;";
745 }
746 $bgcolor = $this->bgColor( $row, $col, $sheet );
747 if ( '' !== $bgcolor ) {
748 $bgcolor = $this->colors[ $bgcolor ];
749 $css .= "background-color:{$bgcolor};";
750 }
751 $color = $this->color( $row, $col, $sheet );
752 if ( '' !== $color ) {
753 $css .= "color:{$color};";
754 }
755 $bold = $this->bold( $row, $col, $sheet );
756 if ( $bold ) {
757 $css .= 'font-weight:bold;';
758 }
759 $italic = $this->italic( $row, $col, $sheet );
760 if ( $italic ) {
761 $css .= 'font-style:italic;';
762 }
763 $underline = $this->underline( $row, $col, $sheet );
764 if ( $underline ) {
765 $css .= 'text-decoration:underline;';
766 }
767 // Borders
768 $bLeft = $this->borderLeft( $row, $col, $sheet );
769 $bRight = $this->borderRight( $row, $col, $sheet );
770 $bTop = $this->borderTop( $row, $col, $sheet );
771 $bBottom = $this->borderBottom( $row, $col, $sheet );
772 $bLeftCol = $this->borderLeftColor( $row, $col, $sheet );
773 $bRightCol = $this->borderRightColor( $row, $col, $sheet );
774 $bTopCol = $this->borderTopColor( $row, $col, $sheet );
775 $bBottomCol = $this->borderBottomColor( $row, $col, $sheet );
776 // Try to output the minimal required style.
777 if ( '' !== $bLeft && $bLeft === $bRight && $bRight === $bTop && $bTop === $bBottom ) {
778 $css .= 'border:' . $this->lineStylesCss[ $bLeft ] . ';';
779 } else {
780 if ( '' !== $bLeft ) {
781 $css .= 'border-left:' . $this->lineStylesCss[ $bLeft ] . ';';
782 }
783 if ( '' !== $bRight ) {
784 $css .= 'border-right:' . $this->lineStylesCss[ $bRight ] . ';';
785 }
786 if ( '' !== $bTop ) {
787 $css .= 'border-top:' . $this->lineStylesCss[ $bTop ] . ';';
788 }
789 if ( '' !== $bBottom ) {
790 $css .= 'border-bottom:' . $this->lineStylesCss[ $bBottom ] . ';';
791 }
792 }
793 // Only output border colors if there is an actual border specified.
794 if ( '' !== $bLeft && '' !== $bLeftCol ) {
795 $css .= "border-left-color:{$bLeftCol};";
796 }
797 if ( '' !== $bRight && '' !== $bRightCol ) {
798 $css .= "border-right-color:{$bRightCol};";
799 }
800 if ( '' !== $bTop && '' !== $bTopCol ) {
801 $css .= "border-top-color:{$bTopCol};";
802 }
803 if ( '' !== $bBottom && '' !== $bBottomCol ) {
804 $css .= "border-bottom-color:{$bBottomCol};";
805 }
806
807 return $css;
808 }
809
810 // FORMAT PROPERTIES
811 // =================
812
813 /**
814 * [format description]
815 *
816 * @since 1.0.0
817 *
818 * @param [type] $row [description]
819 * @param [type] $col [description]
820 * @param int $sheet Optional. [description]
821 * @return [type] [description]
822 */
823 public function format( $row, $col, $sheet = 0 ) {
824 return $this->info( $row, $col, 'format', $sheet );
825 }
826
827 /**
828 * [formatIndex description]
829 *
830 * @since 1.0.0
831 *
832 * @param [type] $row [description]
833 * @param [type] $col [description]
834 * @param int $sheet Optional. [description]
835 * @return [type] [description]
836 */
837 public function formatIndex( $row, $col, $sheet = 0 ) {
838 return $this->info( $row, $col, 'formatIndex', $sheet );
839 }
840
841 /**
842 * [formatColor description]
843 *
844 * @since 1.0.0
845 *
846 * @param [type] $row [description]
847 * @param [type] $col [description]
848 * @param int $sheet Optional. [description]
849 * @return [type] [description]
850 */
851 public function formatColor( $row, $col, $sheet = 0 ) {
852 return $this->info( $row, $col, 'formatColor', $sheet );
853 }
854
855 // CELL (XF) PROPERTIES
856 // ====================
857
858 /**
859 * [xfRecord description]
860 *
861 * @since 1.0.0
862 *
863 * @param [type] $row [description]
864 * @param [type] $col [description]
865 * @param int $sheet Optional. [description]
866 * @return [type] [description]
867 */
868 public function xfRecord( $row, $col, $sheet = 0 ) {
869 $xfIndex = $this->info( $row, $col, 'xfIndex', $sheet );
870 if ( '' !== $xfIndex ) {
871 return $this->xfRecords[ $xfIndex ];
872 }
873 return null;
874 }
875
876 /**
877 * [xfProperty description]
878 *
879 * @since 1.0.0
880 *
881 * @param [type] $row [description]
882 * @param [type] $col [description]
883 * @param [type] $sheet [description]
884 * @param [type] $prop [description]
885 * @return [type] [description]
886 */
887 public function xfProperty( $row, $col, $sheet, $prop ) {
888 $xfRecord = $this->xfRecord( $row, $col, $sheet );
889 if ( null !== $xfRecord ) {
890 return $xfRecord[ $prop ];
891 }
892 return '';
893 }
894
895 /**
896 * [align description]
897 *
898 * @since 1.0.0
899 *
900 * @param [type] $row [description]
901 * @param [type] $col [description]
902 * @param int $sheet Optional. [description]
903 * @return [type] [description]
904 */
905 public function align( $row, $col, $sheet = 0 ) {
906 return $this->xfProperty( $row, $col, $sheet, 'align' );
907 }
908
909 /**
910 * [bgColor description]
911 *
912 * @since 1.0.0
913 *
914 * @param [type] $row [description]
915 * @param [type] $col [description]
916 * @param int $sheet Optional. [description]
917 * @return [type] [description]
918 */
919 public function bgColor( $row, $col, $sheet = 0 ) {
920 return $this->xfProperty( $row, $col, $sheet, 'bgColor' );
921 }
922
923 /**
924 * [borderLeft description]
925 *
926 * @since 1.0.0
927 *
928 * @param [type] $row [description]
929 * @param [type] $col [description]
930 * @param int $sheet Optional. [description]
931 * @return [type] [description]
932 */
933 public function borderLeft( $row, $col, $sheet = 0 ) {
934 return $this->xfProperty( $row, $col, $sheet, 'borderLeft' );
935 }
936
937 /**
938 * [borderRight description]
939 *
940 * @since 1.0.0
941 *
942 * @param [type] $row [description]
943 * @param [type] $col [description]
944 * @param int $sheet Optional. [description]
945 * @return [type] [description]
946 */
947 public function borderRight( $row, $col, $sheet = 0 ) {
948 return $this->xfProperty( $row, $col, $sheet, 'borderRight' );
949 }
950
951 /**
952 * [borderTop description]
953 *
954 * @since 1.0.0
955 *
956 * @param [type] $row [description]
957 * @param [type] $col [description]
958 * @param int $sheet Optional. [description]
959 * @return [type] [description]
960 */
961 public function borderTop( $row, $col, $sheet = 0 ) {
962 return $this->xfProperty( $row, $col, $sheet, 'borderTop' );
963 }
964
965 /**
966 * [borderBottom description]
967 *
968 * @since 1.0.0
969 *
970 * @param [type] $row [description]
971 * @param [type] $col [description]
972 * @param int $sheet Optional. [description]
973 * @return [type] [description]
974 */
975 public function borderBottom( $row, $col, $sheet = 0 ) {
976 return $this->xfProperty( $row, $col, $sheet, 'borderBottom' );
977 }
978
979 /**
980 * [borderLeftColor description]
981 *
982 * @since 1.0.0
983 *
984 * @param [type] $row [description]
985 * @param [type] $col [description]
986 * @param int $sheet Optional. [description]
987 * @return [type] [description]
988 */
989 public function borderLeftColor( $row, $col, $sheet = 0 ) {
990 return $this->colors[ $this->xfProperty( $row, $col, $sheet, 'borderLeftColor' ) ];
991 }
992
993 /**
994 * [borderRightColor description]
995 *
996 * @since 1.0.0
997 *
998 * @param [type] $row [description]
999 * @param [type] $col [description]
1000 * @param int $sheet Optional. [description]
1001 * @return [type] [description]
1002 */
1003 public function borderRightColor( $row, $col, $sheet = 0 ) {
1004 return $this->colors[ $this->xfProperty( $row, $col, $sheet, 'borderRightColor' ) ];
1005 }
1006
1007 /**
1008 * [borderTopColor description]
1009 *
1010 * @since 1.0.0
1011 *
1012 * @param [type] $row [description]
1013 * @param [type] $col [description]
1014 * @param int $sheet Optional. [description]
1015 * @return [type] [description]
1016 */
1017 public function borderTopColor( $row, $col, $sheet = 0 ) {
1018 return $this->colors[ $this->xfProperty( $row, $col, $sheet, 'borderTopColor' ) ];
1019 }
1020
1021 /**
1022 * [borderBottomColor description]
1023 *
1024 * @since 1.0.0
1025 *
1026 * @param [type] $row [description]
1027 * @param [type] $col [description]
1028 * @param int $sheet Optional. [description]
1029 * @return [type] [description]
1030 */
1031 public function borderBottomColor( $row, $col, $sheet = 0 ) {
1032 return $this->colors[ $this->xfProperty( $row, $col, $sheet, 'borderBottomColor' ) ];
1033 }
1034
1035 // FONT PROPERTIES
1036 // ===============
1037
1038 /**
1039 * [fontRecord description]
1040 *
1041 * @since 1.0.0
1042 *
1043 * @param [type] $row [description]
1044 * @param [type] $col [description]
1045 * @param int $sheet Optional. [description]
1046 * @return [type] [description]
1047 */
1048 public function fontRecord( $row, $col, $sheet = 0 ) {
1049 $xfRecord = $this->xfRecord( $row, $col, $sheet );
1050 if ( null !== $xfRecord ) {
1051 $font = $xfRecord['fontIndex'];
1052 if ( null !== $font ) {
1053 return $this->fontRecords[ $font ];
1054 }
1055 }
1056 return null;
1057 }
1058
1059 /**
1060 * [fontProperty description]
1061 *
1062 * @since 1.0.0
1063 *
1064 * @param [type] $row [description]
1065 * @param [type] $col [description]
1066 * @param int $sheet Optional. [description]
1067 * @param [type] $prop [description]
1068 * @return [type] [description]
1069 */
1070 public function fontProperty( $row, $col, $sheet = 0, $prop ) {
1071 $font = $this->fontRecord( $row, $col, $sheet );
1072 if ( null !== $font ) {
1073 return $font[ $prop ];
1074 }
1075 return false;
1076 }
1077
1078 /**
1079 * [fontIndex description]
1080 *
1081 * @since 1.0.0
1082 *
1083 * @param [type] $row [description]
1084 * @param [type] $col [description]
1085 * @param int $sheet Optional. [description]
1086 * @return [type] [description]
1087 */
1088 public function fontIndex( $row, $col, $sheet = 0 ) {
1089 return $this->xfProperty( $row, $col, $sheet, 'fontIndex' );
1090 }
1091
1092 /**
1093 * [color description]
1094 *
1095 * @since 1.0.0
1096 *
1097 * @param [type] $row [description]
1098 * @param [type] $col [description]
1099 * @param int $sheet Optional. [description]
1100 * @return [type] [description]
1101 */
1102 public function color( $row, $col, $sheet = 0 ) {
1103 $formatColor = $this->formatColor( $row, $col, $sheet );
1104 if ( '' !== $formatColor ) {
1105 return $formatColor;
1106 }
1107 $ci = $this->fontProperty( $row, $col, $sheet, 'color' );
1108 return $this->rawColor( $ci );
1109 }
1110
1111 /**
1112 * [rawColor description]
1113 *
1114 * @since 1.0.0
1115 *
1116 * @param [type] $ci [description]
1117 * @return [type] [description]
1118 */
1119 public function rawColor( $ci ) {
1120 if ( 0x7FFF !== $ci && '' !== $ci ) {
1121 return $this->colors[ $ci ];
1122 }
1123 return '';
1124 }
1125
1126 /**
1127 * [bold description]
1128 *
1129 * @since 1.0.0
1130 *
1131 * @param [type] $row [description]
1132 * @param [type] $col [description]
1133 * @param int $sheet Optional. [description]
1134 * @return [type] [description]
1135 */
1136 public function bold( $row, $col, $sheet = 0 ) {
1137 return $this->fontProperty( $row, $col, $sheet, 'bold' );
1138 }
1139
1140 /**
1141 * [italic description]
1142 *
1143 * @since 1.0.0
1144 *
1145 * @param [type] $row [description]
1146 * @param [type] $col [description]
1147 * @param int $sheet Optional. [description]
1148 * @return [type] [description]
1149 */
1150 public function italic( $row, $col, $sheet = 0 ) {
1151 return $this->fontProperty( $row, $col, $sheet, 'italic' );
1152 }
1153
1154 /**
1155 * [underline description]
1156 *
1157 * @since 1.0.0
1158 *
1159 * @param [type] $row [description]
1160 * @param [type] $col [description]
1161 * @param int $sheet Optional. [description]
1162 * @return [type] [description]
1163 */
1164 public function underline( $row, $col, $sheet = 0 ) {
1165 return $this->fontProperty( $row, $col, $sheet, 'under' );
1166 }
1167
1168 /**
1169 * [height description]
1170 *
1171 * @since 1.0.0
1172 *
1173 * @param [type] $row [description]
1174 * @param [type] $col [description]
1175 * @param int $sheet Optional. [description]
1176 * @return [type] [description]
1177 */
1178 public function height( $row, $col, $sheet = 0 ) {
1179 return $this->fontProperty( $row, $col, $sheet, 'height' );
1180 }
1181
1182 /**
1183 * [font description]
1184 *
1185 * @since 1.0.0
1186 *
1187 * @param [type] $row [description]
1188 * @param [type] $col [description]
1189 * @param int $sheet Optional. [description]
1190 * @return [type] [description]
1191 */
1192 public function font( $row, $col, $sheet = 0 ) {
1193 return $this->fontProperty( $row, $col, $sheet, 'font' );
1194 }
1195
1196 // DUMP AN HTML TABLE OF THE ENTIRE XLS DATA
1197 // =========================================
1198
1199 /**
1200 * [dump description]
1201 *
1202 * @since 1.0.0
1203 *
1204 * @param bool $row_numbers Optional. [description]
1205 * @param bool $col_letters Optional. [description]
1206 * @param int $sheet Optional. [description]
1207 * @param string $table_class Optional. [description]
1208 * @return [type] [description]
1209 */
1210 public function dump( $row_numbers = false, $col_letters = false, $sheet = 0, $table_class = 'excel' ) {
1211 $out = "<table class=\"$table_class\" cellspacing=0>";
1212 if ( $col_letters ) {
1213 $out .= "<thead>\n\t<tr>";
1214 if ( $row_numbers ) {
1215 $out .= "\n\t\t<th>&nbsp</th>";
1216 }
1217 for ( $i = 1; $i <= $this->colcount( $sheet ); $i++ ) {
1218 $style = 'width:' . ( $this->colwidth( $i, $sheet ) ) . 'px;';
1219 if ( $this->colhidden( $i, $sheet ) ) {
1220 $style .= 'display:none;';
1221 }
1222 $out .= "\n\t\t<th style=\"$style\">" . strtoupper( $this->colindexes[ $i ] ) . '</th>';
1223 }
1224 $out .= "</tr></thead>\n";
1225 }
1226
1227 $out .= "<tbody>\n";
1228 for ( $row = 1; $row <= $this->rowcount( $sheet ); $row++ ) {
1229 $rowheight = $this->rowheight( $row, $sheet );
1230 $style = 'height:' . ( $rowheight * ( 4 / 3 ) ) . 'px;';
1231 if ( $this->rowhidden( $row, $sheet ) ) {
1232 $style .= 'display:none;';
1233 }
1234 $out .= "\n\t<tr style=\"$style\">";
1235 if ( $row_numbers ) {
1236 $out .= "\n\t\t<th>{$row}</th>";
1237 }
1238 for ( $col = 1; $col <= $this->colcount( $sheet ); $col++ ) {
1239 // Account for Rowspans/Colspans
1240 $rowspan = $this->rowspan( $row, $col, $sheet );
1241 $colspan = $this->colspan( $row, $col, $sheet );
1242 for ( $i = 0; $i < $rowspan; $i++ ) {
1243 for ( $j = 0; $j < $colspan; $j++ ) {
1244 if ( $i > 0 || $j > 0 ) {
1245 $this->sheets[ $sheet ]['cellsInfo'][ $row + $i ][ $col + $j ]['dontprint'] = 1;
1246 }
1247 }
1248 }
1249 if ( ! $this->sheets[ $sheet ]['cellsInfo'][ $row ][ $col ]['dontprint'] ) {
1250 $style = $this->style( $row, $col, $sheet );
1251 if ( $this->colhidden( $col, $sheet ) ) {
1252 $style .= 'display:none;';
1253 }
1254 $out .= "\n\t\t<td style=\"$style\"" . ( $colspan > 1 ? " colspan={$colspan}" : '' ) . ( $rowspan > 1 ? " rowspan={$rowspan}" : '' ) . '>';
1255 $val = $this->val( $row, $col, $sheet );
1256 if ( '' === $val ) {
1257 $val = '&nbsp;';
1258 } else {
1259 $val = htmlentities( $val );
1260 $link = $this->hyperlink( $row, $col, $sheet );
1261 if ( '' !== $link ) {
1262 $val = "<a href=\"$link\">{$val}</a>";
1263 }
1264 }
1265 $out .= '<nobr>' . nl2br( $val ) . '</nobr>';
1266 $out .= '</td>';
1267 }
1268 }
1269 $out .= "</tr>\n";
1270 }
1271 $out .= '</tbody></table>';
1272 return $out;
1273 }
1274
1275 // --------------
1276 // END PUBLIC API
1277
1278 protected $boundsheets = array();
1279 protected $formatRecords = array();
1280 protected $fontRecords = array();
1281 protected $xfRecords = array();
1282 protected $colInfo = array();
1283 protected $rowInfo = array();
1284
1285 protected $sst = array();
1286 protected $sheets = array();
1287
1288 protected $data;
1289 protected $_ole;
1290 protected $_defaultEncoding = 'UTF-8';
1291 protected $_defaultFormat = SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT;
1292 protected $_columnsFormat = array();
1293 protected $_rowoffset = 1;
1294 protected $_coloffset = 1;
1295
1296 /**
1297 * List of default date formats used by Excel
1298 *
1299 * @since 1.0.0
1300 * @var array
1301 */
1302 protected $dateFormats = array(
1303 0xe => 'm/d/Y',
1304 0xf => 'M-d-Y',
1305 0x10 => 'd-M',
1306 0x11 => 'M-Y',
1307 0x12 => 'h:i a',
1308 0x13 => 'h:i:s a',
1309 0x14 => 'H:i',
1310 0x15 => 'H:i:s',
1311 0x16 => 'd/m/Y H:i',
1312 0x2d => 'i:s',
1313 0x2e => 'H:i:s',
1314 0x2f => 'i:s.S',
1315 );
1316
1317 /**
1318 * Default number formats used by Excel
1319 *
1320 * @since 1.0.0
1321 * @var array
1322 */
1323 protected $numberFormats = array(
1324 0x1 => '0',
1325 0x2 => '0.00',
1326 0x3 => '#,##0',
1327 0x4 => '#,##0.00',
1328 0x5 => '\$#,##0;(\$#,##0)',
1329 0x6 => '\$#,##0;[Red](\$#,##0)',
1330 0x7 => '\$#,##0.00;(\$#,##0.00)',
1331 0x8 => '\$#,##0.00;[Red](\$#,##0.00)',
1332 0x9 => '0%',
1333 0xa => '0.00%',
1334 0xb => '0.00E+00',
1335 0x25 => '#,##0;(#,##0)',
1336 0x26 => '#,##0;[Red](#,##0)',
1337 0x27 => '#,##0.00;(#,##0.00)',
1338 0x28 => '#,##0.00;[Red](#,##0.00)',
1339 0x29 => '#,##0;(#,##0)', // Not exact
1340 0x2a => '\$#,##0;(\$#,##0)', // Not exact
1341 0x2b => '#,##0.00;(#,##0.00)', // Not exact
1342 0x2c => '\$#,##0.00;(\$#,##0.00)', // Not exact
1343 0x30 => '##0.0E+0',
1344 );
1345
1346 /**
1347 * [$colors description]
1348 *
1349 * @since 1.0.0
1350 * @var array
1351 */
1352 protected $colors = array(
1353 0x00 => '#000000',
1354 0x01 => '#FFFFFF',
1355 0x02 => '#FF0000',
1356 0x03 => '#00FF00',
1357 0x04 => '#0000FF',
1358 0x05 => '#FFFF00',
1359 0x06 => '#FF00FF',
1360 0x07 => '#00FFFF',
1361 0x08 => '#000000',
1362 0x09 => '#FFFFFF',
1363 0x0A => '#FF0000',
1364 0x0B => '#00FF00',
1365 0x0C => '#0000FF',
1366 0x0D => '#FFFF00',
1367 0x0E => '#FF00FF',
1368 0x0F => '#00FFFF',
1369 0x10 => '#800000',
1370 0x11 => '#008000',
1371 0x12 => '#000080',
1372 0x13 => '#808000',
1373 0x14 => '#800080',
1374 0x15 => '#008080',
1375 0x16 => '#C0C0C0',
1376 0x17 => '#808080',
1377 0x18 => '#9999FF',
1378 0x19 => '#993366',
1379 0x1A => '#FFFFCC',
1380 0x1B => '#CCFFFF',
1381 0x1C => '#660066',
1382 0x1D => '#FF8080',
1383 0x1E => '#0066CC',
1384 0x1F => '#CCCCFF',
1385 0x20 => '#000080',
1386 0x21 => '#FF00FF',
1387 0x22 => '#FFFF00',
1388 0x23 => '#00FFFF',
1389 0x24 => '#800080',
1390 0x25 => '#800000',
1391 0x26 => '#008080',
1392 0x27 => '#0000FF',
1393 0x28 => '#00CCFF',
1394 0x29 => '#CCFFFF',
1395 0x2A => '#CCFFCC',
1396 0x2B => '#FFFF99',
1397 0x2C => '#99CCFF',
1398 0x2D => '#FF99CC',
1399 0x2E => '#CC99FF',
1400 0x2F => '#FFCC99',
1401 0x30 => '#3366FF',
1402 0x31 => '#33CCCC',
1403 0x32 => '#99CC00',
1404 0x33 => '#FFCC00',
1405 0x34 => '#FF9900',
1406 0x35 => '#FF6600',
1407 0x36 => '#666699',
1408 0x37 => '#969696',
1409 0x38 => '#003366',
1410 0x39 => '#339966',
1411 0x3A => '#003300',
1412 0x3B => '#333300',
1413 0x3C => '#993300',
1414 0x3D => '#993366',
1415 0x3E => '#333399',
1416 0x3F => '#333333',
1417 0x40 => '#000000',
1418 0x41 => '#FFFFFF',
1419 0x43 => '#000000',
1420 0x4D => '#000000',
1421 0x4E => '#FFFFFF',
1422 0x4F => '#000000',
1423 0x50 => '#FFFFFF',
1424 0x51 => '#000000',
1425 0x7FFF => '#000000',
1426 );
1427
1428 /**
1429 * [$lineStyles description]
1430 *
1431 * @since 1.0.0
1432 * @var array
1433 */
1434 protected $lineStyles = array(
1435 0x00 => '',
1436 0x01 => 'Thin',
1437 0x02 => 'Medium',
1438 0x03 => 'Dashed',
1439 0x04 => 'Dotted',
1440 0x05 => 'Thick',
1441 0x06 => 'Double',
1442 0x07 => 'Hair',
1443 0x08 => 'Medium dashed',
1444 0x09 => 'Thin dash-dotted',
1445 0x0A => 'Medium dash-dotted',
1446 0x0B => 'Thin dash-dot-dotted',
1447 0x0C => 'Medium dash-dot-dotted',
1448 0x0D => 'Slanted medium dash-dotted',
1449 );
1450
1451 /**
1452 * [$lineStylesCss description]
1453 *
1454 * @since 1.0.0
1455 * @var array
1456 */
1457 protected $lineStylesCss = array(
1458 'Thin' => '1px solid',
1459 'Medium' => '2px solid',
1460 'Dashed' => '1px dashed',
1461 'Dotted' => '1px dotted',
1462 'Thick' => '3px solid',
1463 'Double' => 'double',
1464 'Hair' => '1px solid',
1465 'Medium dashed' => '2px dashed',
1466 'Thin dash-dotted' => '1px dashed',
1467 'Medium dash-dotted' => '2px dashed',
1468 'Thin dash-dot-dotted' => '1px dashed',
1469 'Medium dash-dot-dotted' => '2px dashed',
1470 'Slanted medium dash-dotted' => '2px dashed',
1471 );
1472
1473 /**
1474 * [read16bitstring description]
1475 *
1476 * @since 1.0.0
1477 *
1478 * @param [type] $data [description]
1479 * @param [type] $start [description]
1480 * @return [type] [description]
1481 */
1482 protected function read16bitstring( $data, $start ) {
1483 $len = 0;
1484 while ( ord( $data[ $start + $len ] ) + ord( $data[ $start + $len + 1 ] ) > 0 ) {
1485 $len++;
1486 }
1487 return substr( $data, $start, $len );
1488 }
1489
1490 /**
1491 * [_format_value description]
1492 * ADDED by Matt Kruse for better formatting
1493 *
1494 * @since 1.0.0
1495 *
1496 * @param [type] $format [description]
1497 * @param [type] $num [description]
1498 * @param [type] $f [description]
1499 * @return [type] [description]
1500 */
1501 protected function _format_value( $format, $num, $f ) {
1502 // 49 = TEXT format
1503 // https://code.google.com/archive/p/php-excel-reader/issues/7
1504 if ( ( ! $f && '%s' === $format ) || ( 49 === (int) $f ) || ( 'GENERAL' === $format ) ) {
1505 return array(
1506 'string' => $num,
1507 'formatColor' => null,
1508 );
1509 }
1510
1511 // Custom pattern can be POSITIVE;NEGATIVE;ZERO
1512 // The "text" option as 4th parameter is not handled
1513 $parts = explode( ';', $format );
1514 $pattern = $parts[0];
1515 // Negative pattern
1516 if ( count( $parts ) > 2 && 0 === (int) $num ) {
1517 $pattern = $parts[2];
1518 }
1519 // Zero pattern
1520 if ( count( $parts ) > 1 && $num < 0 ) {
1521 $pattern = $parts[1];
1522 $num = abs( $num );
1523 }
1524
1525 $color = '';
1526 $matches = array();
1527 $color_regex = '/^\[(BLACK|BLUE|CYAN|GREEN|MAGENTA|RED|WHITE|YELLOW)\]/i';
1528 if ( preg_match( $color_regex, $pattern, $matches ) ) {
1529 $color = strtolower( $matches[1] );
1530 $pattern = preg_replace( $color_regex, '', $pattern );
1531 }
1532
1533 // In Excel formats, "_" is used to add spacing, which we can't do in HTML.
1534 $pattern = preg_replace( '/_./', '', $pattern );
1535
1536 // Some non-number characters are escaped with \, which we don't need.
1537 $pattern = preg_replace( '/\\\/', '', $pattern );
1538
1539 // Some non-number strings are quoted, so we'll get rid of the quotes.
1540 $pattern = preg_replace( '/"/', '', $pattern );
1541
1542 // TEMPORARY - Convert # to 0.
1543 $pattern = preg_replace( '/\#/', '0', $pattern );
1544
1545 // Find out if we need comma formatting.
1546 $has_commas = preg_match( '/,/', $pattern );
1547 if ( $has_commas ) {
1548 $pattern = preg_replace( '/,/', '', $pattern );
1549 }
1550
1551 // Handle Percentages.
1552 if ( preg_match( '/\d(\%)([^\%]|$)/', $pattern, $matches ) ) {
1553 $num *= 100;
1554 $pattern = preg_replace( '/(\d)(\%)([^\%]|$)/', '$1%$3', $pattern );
1555 }
1556
1557 // Handle the number itself.
1558 $number_regex = '/(\d+)(\.?)(\d*)/';
1559 if ( preg_match( $number_regex, $pattern, $matches ) ) {
1560 //$left = $matches[1];
1561 //$dec = $matches[2];
1562 $right = $matches[3];
1563 if ( $has_commas ) {
1564 $formatted = number_format( $num, strlen( $right ) );
1565 } else {
1566 $sprintf_pattern = '%1.' . strlen( $right ) . 'f';
1567 $formatted = sprintf( $sprintf_pattern, $num );
1568 }
1569 $pattern = preg_replace( $number_regex, $formatted, $pattern );
1570 }
1571
1572 return array(
1573 'string' => $pattern,
1574 'formatColor' => $color,
1575 );
1576 }
1577
1578 /**
1579 * [__construct description]
1580 *
1581 * @since 1.0.0
1582 *
1583 * @param string $data Optional. [description]
1584 * @param bool $store_extended_info Optional. [description]
1585 * @param string $outputEncoding Optional. [description]
1586 */
1587 public function __construct( $data = '', $store_extended_info = false, $outputEncoding = '' ) {
1588 $this->_ole = new OLERead();
1589 $this->setUTFEncoder( 'iconv' );
1590 if ( '' !== $outputEncoding ) {
1591 $this->setOutputEncoding( $outputEncoding );
1592 }
1593 for ( $i = 1; $i < 245; $i++ ) {
1594 $name = strtolower( ( ( ( $i - 1 ) / 26 >= 1 ) ? chr( ( $i - 1 ) / 26 + 64 ) : '' ) . chr( ( $i - 1 ) % 26 + 65 ) );
1595 $this->colnames[ $name ] = $i;
1596 $this->colindexes[ $i ] = $name;
1597 }
1598 $this->store_extended_info = $store_extended_info;
1599 if ( '' !== $data ) {
1600 $this->read( $data );
1601 }
1602 }
1603
1604 /**
1605 * Set the encoding method.
1606 *
1607 * @since 1.0.0
1608 *
1609 * @param [type] $encoding [description]
1610 */
1611 public function setOutputEncoding( $encoding ) {
1612 $this->_defaultEncoding = $encoding;
1613 }
1614
1615 /**
1616 * [setUTFEncoder description]
1617 * $encoder = 'iconv' or 'mb'
1618 * set iconv if you would like use 'iconv' for encode UTF-16LE to your encoding
1619 * set mb if you would like use 'mb_convert_encoding' for encode UTF-16LE to your encoding
1620 *
1621 * @since 1.0.0
1622 *
1623 * @param string $encoder Optional. [description]
1624 */
1625 public function setUTFEncoder( $encoder = 'iconv' ) {
1626 $this->_encoderFunction = '';
1627 if ( 'iconv' === $encoder ) {
1628 $this->_encoderFunction = function_exists( 'iconv' ) ? 'iconv' : '';
1629 } elseif ( 'mb' === $encoder ) {
1630 $this->_encoderFunction = function_exists( 'mb_convert_encoding' ) ? 'mb_convert_encoding' : '';
1631 }
1632 }
1633
1634 /**
1635 * [setRowColOffset description]
1636 *
1637 * @since 1.0.0
1638 *
1639 * @param [type] $iOffset [description]
1640 */
1641 public function setRowColOffset( $iOffset ) {
1642 $this->_rowoffset = $iOffset;
1643 $this->_coloffset = $iOffset;
1644 }
1645
1646 /**
1647 * Set the default number format.
1648 *
1649 * @since 1.0.0
1650 *
1651 * @param [type] $sFormat [description]
1652 */
1653 public function setDefaultFormat( $sFormat ) {
1654 $this->_defaultFormat = $sFormat;
1655 }
1656
1657 /**
1658 * Force a column to use a certain format.
1659 *
1660 * @since 1.0.0
1661 *
1662 * @param [type] $column [description]
1663 * @param [type] $sFormat [description]
1664 */
1665 public function setColumnFormat( $column, $sFormat ) {
1666 $this->_columnsFormat[ $column ] = $sFormat;
1667 }
1668
1669 /**
1670 * Read the spreadsheet file using OLE, then parse.
1671 *
1672 * @since 1.0.0
1673 *
1674 * @param [type] $data [description]
1675 * @return [type] [description]
1676 */
1677 public function read( $data ) {
1678 $res = $this->_ole->read( $data );
1679
1680 // oops, something goes wrong (Darko Miljanovic)
1681 if ( false === $res ) {
1682 // check error code
1683 if ( 1 === $this->_ole->error ) {
1684 die( 'Data is not readable' );
1685 } elseif ( 2 === $this->_ole->error ) {
1686 die( 'OLE error' );
1687 }
1688 // check other error codes here (e.g. bad fileformat, etc...)
1689 }
1690 $this->data = $this->_ole->getWorkBook();
1691 $this->_parse();
1692 }
1693
1694 /**
1695 * Parse a workbook.
1696 *
1697 * @since 1.0.0
1698 *
1699 * @return [type] [description]
1700 */
1701 protected function _parse() {
1702 $pos = 0;
1703 $data = $this->data;
1704
1705 //$code = $this->v( $data, $pos );
1706 $length = $this->v( $data, $pos + 2 );
1707 $version = $this->v( $data, $pos + 4 );
1708 $substreamType = $this->v( $data, $pos + 6 );
1709 if ( SPREADSHEET_EXCEL_READER_BIFF8 !== $version && SPREADSHEET_EXCEL_READER_BIFF7 !== $version ) {
1710 return false;
1711 }
1712
1713 if ( SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS !== $substreamType ) {
1714 return false;
1715 }
1716
1717 $pos += $length + 4;
1718
1719 $code = $this->v( $data, $pos );
1720 $length = $this->v( $data, $pos + 2 );
1721
1722 while ( SPREADSHEET_EXCEL_READER_TYPE_EOF !== $code ) {
1723 switch ( $code ) {
1724 case SPREADSHEET_EXCEL_READER_TYPE_SST:
1725 $spos = $pos + 4;
1726 $limitpos = $spos + $length;
1727 $uniqueStrings = $this->_GetInt4d( $data, $spos + 4 );
1728 $spos += 8;
1729 for ( $i = 0; $i < $uniqueStrings; $i++ ) {
1730 // Read in the number of characters
1731 if ( $spos === $limitpos ) {
1732 $opcode = $this->v( $data, $spos );
1733 $conlength = $this->v( $data, $spos + 2 );
1734 if ( 0x3c !== $opcode ) {
1735 return -1;
1736 }
1737 $spos += 4;
1738 $limitpos = $spos + $conlength;
1739 }
1740 $numChars = ord( $data[ $spos ] ) | ( ord( $data[ $spos + 1 ] ) << 8 );
1741 $spos += 2;
1742 $optionFlags = ord( $data[ $spos ] );
1743 $spos++;
1744 $asciiEncoding = ( 0 === ( $optionFlags & 0x01 ) );
1745 $extendedString = ( 0 !== ( $optionFlags & 0x04 ) );
1746
1747 // See if string contains formatting information.
1748 $richString = ( 0 !== ( $optionFlags & 0x08 ) );
1749
1750 if ( $richString ) {
1751 // Read in the crun
1752 $formattingRuns = $this->v( $data, $spos );
1753 $spos += 2;
1754 }
1755
1756 if ( $extendedString ) {
1757 // Read in cchExtRst
1758 $extendedRunLength = $this->_GetInt4d( $data, $spos );
1759 $spos += 4;
1760 }
1761
1762 $len = ( $asciiEncoding ) ? $numChars : $numChars * 2;
1763 if ( $spos + $len < $limitpos ) {
1764 $retstr = substr( $data, $spos, $len );
1765 $spos += $len;
1766 } else {
1767 // found continue
1768 $retstr = substr( $data, $spos, $limitpos - $spos );
1769 $bytesRead = $limitpos - $spos;
1770 $charsLeft = $numChars - ( ( $asciiEncoding ) ? $bytesRead : ( $bytesRead / 2 ) );
1771 $spos = $limitpos;
1772
1773 while ( $charsLeft > 0 ) {
1774 $opcode = $this->v( $data, $spos );
1775 $conlength = $this->v( $data, $spos + 2 );
1776 if ( 0x3c !== $opcode ) {
1777 return -1;
1778 }
1779 $spos += 4;
1780 $limitpos = $spos + $conlength;
1781 $option = ord( $data[ $spos ] );
1782 $spos += 1;
1783 if ( $asciiEncoding && 0 === $option ) {
1784 $len = min( $charsLeft, $limitpos - $spos ); // min( $charsLeft, $conlength );
1785 $retstr .= substr( $data, $spos, $len );
1786 $charsLeft -= $len;
1787 $asciiEncoding = true;
1788 } elseif ( ! $asciiEncoding && 0 !== $option ) {
1789 $len = min( $charsLeft * 2, $limitpos - $spos ); // min( $charsLeft, $conlength );
1790 $retstr .= substr( $data, $spos, $len );
1791 $charsLeft -= $len / 2;
1792 $asciiEncoding = false;
1793 } elseif ( ! $asciiEncoding && 0 === $option ) {
1794 // Bummer - the string starts off as Unicode, but after the
1795 // continuation it is in straightforward ASCII encoding
1796 $len = min( $charsLeft, $limitpos - $spos ); // min( $charsLeft, $conlength );
1797 for ( $j = 0; $j < $len; $j++ ) {
1798 $retstr .= $data[ $spos + $j ] . chr( 0 );
1799 }
1800 $charsLeft -= $len;
1801 $asciiEncoding = false;
1802 } else {
1803 $newstr = '';
1804 for ( $j = 0; $j < strlen( $retstr ); $j++ ) {
1805 $newstr = $retstr[ $j ] . chr( 0 );
1806 }
1807 $retstr = $newstr;
1808 $len = min( $charsLeft * 2, $limitpos - $spos ); // min( $charsLeft, $conlength );
1809 $retstr .= substr( $data, $spos, $len );
1810 $charsLeft -= $len / 2;
1811 $asciiEncoding = false;
1812 }
1813 $spos += $len;
1814 }
1815 }
1816 $retstr = ( $asciiEncoding ) ? $retstr : $this->_encodeUTF16( $retstr );
1817
1818 if ( $richString ) {
1819 $spos += 4 * $formattingRuns;
1820 }
1821
1822 // For extended strings, skip over the extended string data
1823 if ( $extendedString ) {
1824 $spos += $extendedRunLength;
1825 }
1826 $this->sst[] = $retstr;
1827 }
1828 break;
1829 case SPREADSHEET_EXCEL_READER_TYPE_FILEPASS:
1830 return false;
1831 // break; // unreachable
1832 case SPREADSHEET_EXCEL_READER_TYPE_NAME:
1833 break;
1834 case SPREADSHEET_EXCEL_READER_TYPE_FORMAT:
1835 $indexCode = $this->v( $data, $pos + 4 );
1836 if ( SPREADSHEET_EXCEL_READER_BIFF8 === $version ) {
1837 $numchars = $this->v( $data, $pos + 6 );
1838 if ( 0 === ord( $data[ $pos + 8 ] ) ) {
1839 $formatString = substr( $data, $pos + 9, $numchars );
1840 } else {
1841 $formatString = substr( $data, $pos + 9, $numchars * 2 );
1842 }
1843 } else {
1844 $numchars = ord( $data[ $pos + 6 ] );
1845 $formatString = substr( $data, $pos + 7, $numchars * 2 );
1846 }
1847 $this->formatRecords[ $indexCode ] = $formatString;
1848 break;
1849 case SPREADSHEET_EXCEL_READER_TYPE_FONT:
1850 $height = $this->v( $data, $pos + 4 );
1851 $option = $this->v( $data, $pos + 6 );
1852 $color = $this->v( $data, $pos + 8 );
1853 $weight = $this->v( $data, $pos + 10 );
1854 $under = ord( $data[ $pos + 14 ] );
1855 // Font name
1856 $numchars = ord( $data[ $pos + 18 ] );
1857 if ( 0 === ( ord( $data[ $pos + 19 ] ) & 1 ) ) {
1858 $font = substr( $data, $pos + 20, $numchars );
1859 } else {
1860 $font = substr( $data, $pos + 20, $numchars * 2 );
1861 $font = $this->_encodeUTF16( $font );
1862 }
1863 $this->fontRecords[] = array(
1864 'height' => $height / 20,
1865 'italic' => (bool) ( $option & 2 ),
1866 'color' => $color,
1867 'under' => ( 0 !== $under ),
1868 'bold' => ( 700 === $weight ),
1869 'font' => $font,
1870 'raw' => $this->dumpHexData( $data, $pos + 3, $length ),
1871 );
1872 break;
1873 case SPREADSHEET_EXCEL_READER_TYPE_PALETTE:
1874 $colors = ord( $data[ $pos + 4 ] ) | ord( $data[ $pos + 5 ] ) << 8;
1875 for ( $coli = 0; $coli < $colors; $coli++ ) {
1876 $colOff = $pos + 2 + ( $coli * 4 );
1877 $colr = ord( $data[ $colOff ] );
1878 $colg = ord( $data[ $colOff + 1 ] );
1879 $colb = ord( $data[ $colOff + 2 ] );
1880 $this->colors[ 0x07 + $coli ] = '#' . $this->myhex( $colr ) . $this->myhex( $colg ) . $this->myhex( $colb );
1881 }
1882 break;
1883 case SPREADSHEET_EXCEL_READER_TYPE_XF:
1884 $fontIndexCode = ( ord( $data[ $pos + 4 ] ) | ord( $data[ $pos + 5 ] ) << 8 ) - 1;
1885 $fontIndexCode = max( 0, $fontIndexCode );
1886 $indexCode = ord( $data[ $pos + 6 ] ) | ord( $data[ $pos + 7 ] ) << 8;
1887 $alignbit = ord( $data[ $pos + 10 ] ) & 3;
1888 $bgi = ( ord( $data[ $pos + 22 ] ) | ord( $data[ $pos + 23 ] ) << 8 ) & 0x3FFF;
1889 $bgcolor = ( $bgi & 0x7F );
1890 // $bgcolor = ( $bgi & 0x3f80 ) >> 7;
1891 $align = '';
1892 if ( 3 === $alignbit ) {
1893 $align = 'right';
1894 } elseif ( 2 === $alignbit ) {
1895 $align = 'center';
1896 }
1897 $fillPattern = ( ord( $data[ $pos + 21 ] ) & 0xFC ) >> 2;
1898 if ( 0 === $fillPattern ) {
1899 $bgcolor = '';
1900 }
1901
1902 $xf = array();
1903 $xf['formatIndex'] = $indexCode;
1904 $xf['align'] = $align;
1905 $xf['fontIndex'] = $fontIndexCode;
1906 $xf['bgColor'] = $bgcolor;
1907 $xf['fillPattern'] = $fillPattern;
1908
1909 $border = ord( $data[ $pos + 14 ] ) | ( ord( $data[ $pos + 15 ] ) << 8 ) | ( ord( $data[ $pos + 16 ] ) << 16 ) | ( ord( $data[ $pos + 17 ] ) << 24 );
1910 $xf['borderLeft'] = $this->lineStyles[ ( $border & 0xF ) ];
1911 $xf['borderRight'] = $this->lineStyles[ ( $border & 0xF0 ) >> 4 ];
1912 $xf['borderTop'] = $this->lineStyles[ ( $border & 0xF00 ) >> 8 ];
1913 $xf['borderBottom'] = $this->lineStyles[ ( $border & 0xF000 ) >> 12 ];
1914
1915 $xf['borderLeftColor'] = ( $border & 0x7F0000 ) >> 16;
1916 $xf['borderRightColor'] = ( $border & 0x3F800000 ) >> 23;
1917 $border = ( ord( $data[ $pos + 18 ] ) | ord( $data[ $pos + 19 ] ) << 8 );
1918 $xf['borderTopColor'] = ( $border & 0x7F );
1919 $xf['borderBottomColor'] = ( $border & 0x3F80 ) >> 7;
1920 if ( array_key_exists( $indexCode, $this->dateFormats ) ) {
1921 $xf['type'] = 'date';
1922 $xf['format'] = $this->dateFormats[ $indexCode ];
1923 if ( '' === $align ) {
1924 $xf['align'] = 'right';
1925 }
1926 } elseif ( array_key_exists( $indexCode, $this->numberFormats ) ) {
1927 $xf['type'] = 'number';
1928 $xf['format'] = $this->numberFormats[ $indexCode ];
1929 if ( '' === $align ) {
1930 $xf['align'] = 'right';
1931 }
1932 } else {
1933 $isdate = false;
1934 $formatstr = '';
1935 if ( $indexCode > 0 ) {
1936 if ( isset( $this->formatRecords[ $indexCode ] ) ) {
1937 $formatstr = $this->formatRecords[ $indexCode ];
1938 }
1939 if ( '' !== $formatstr ) {
1940 $tmp = preg_replace( '/\;.*/', '', $formatstr );
1941 $tmp = preg_replace( '/^\[[^\]]*\]/', '', $tmp );
1942 if ( 0 === preg_match( '/[^hmsday\/\-:\s\\\,AMP]/i', $tmp ) ) { // found day and time format
1943 $isdate = true;
1944 $formatstr = $tmp;
1945 $formatstr = str_replace( array( 'AM/PM', 'mmmm', 'mmm' ), array( 'a', 'F', 'M' ), $formatstr );
1946 // m/mm are used for both minutes and months - oh SNAP!
1947 // This mess tries to fix for that.
1948 // 'm' = minutes only if following h/hh or preceding s/ss
1949 $formatstr = preg_replace( '/(h:?)mm?/', '$1i', $formatstr );
1950 $formatstr = preg_replace( '/mm?(:?s)/', '1$1', $formatstr );
1951 // A single 'm' = n in PHP
1952 $formatstr = preg_replace( '/(^|[^m])m([^m]|$)/', '$1n$2', $formatstr );
1953 $formatstr = preg_replace( '/(^|[^m])m([^m]|$)/', '$1n$2', $formatstr );
1954 // else it's months
1955 $formatstr = str_replace( 'mm', 'm', $formatstr );
1956 // Convert single 'd' to 'j'
1957 $formatstr = preg_replace( '/(^|[^d])d([^d]|$)/', '$1j$2', $formatstr );
1958 $formatstr = str_replace( array( 'dddd', 'ddd', 'dd', 'yyyy', 'yy', 'hh', 'h' ), array( 'l', 'D', 'd', 'Y', 'y', 'H', 'g' ), $formatstr );
1959 $formatstr = preg_replace( '/ss?/', 's', $formatstr );
1960 }
1961 }
1962 }
1963 if ( $isdate ) {
1964 $xf['type'] = 'date';
1965 $xf['format'] = $formatstr;
1966 if ( '' === $align ) {
1967 $xf['align'] = 'right';
1968 }
1969 } else {
1970 // If the format string has a 0 or # in it, we'll assume it's a number.
1971 if ( preg_match( '/[0#]/', $formatstr ) ) {
1972 $xf['type'] = 'number';
1973 if ( '' === $align ) {
1974 $xf['align'] = 'right';
1975 }
1976 } else {
1977 $xf['type'] = 'other';
1978 }
1979 $xf['format'] = $formatstr;
1980 $xf['code'] = $indexCode;
1981 }
1982 }
1983 $this->xfRecords[] = $xf;
1984 break;
1985 case SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR:
1986 $this->nineteenFour = ( 1 === ord( $data[ $pos + 4 ] ) );
1987 break;
1988 case SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET:
1989 $rec_offset = $this->_GetInt4d( $data, $pos + 4 );
1990 //$rec_typeFlag = ord( $data[ $pos + 8 ] );
1991 //$rec_visibilityFlag = ord( $data[ $pos + 9 ] );
1992 $rec_length = ord( $data[ $pos + 10 ] );
1993
1994 if ( SPREADSHEET_EXCEL_READER_BIFF8 === $version ) {
1995 $chartype = ord( $data[ $pos + 11 ] );
1996 if ( 0 === $chartype ) {
1997 $rec_name = substr( $data, $pos + 12, $rec_length );
1998 } else {
1999 $rec_name = $this->_encodeUTF16( substr( $data, $pos + 12, 2 * $rec_length ) );
2000 }
2001 } elseif ( SPREADSHEET_EXCEL_READER_BIFF7 === $version ) {
2002 $rec_name = substr( $data, $pos + 11, $rec_length );
2003 }
2004 $this->boundsheets[] = array(
2005 'name' => $rec_name,
2006 'offset' => $rec_offset,
2007 );
2008 break;
2009 } // switch
2010
2011 $pos += $length + 4;
2012 $code = ord( $data[ $pos ] ) | ord( $data[ $pos + 1 ] ) << 8;
2013 $length = ord( $data[ $pos + 2 ] ) | ord( $data[ $pos + 3 ] ) << 8;
2014 } // while
2015
2016 foreach ( $this->boundsheets as $key => $val ) {
2017 $this->sn = $key;
2018 $this->_parsesheet( $val['offset'] );
2019 }
2020 return true;
2021 }
2022
2023 /**
2024 * Parse a worksheet.
2025 *
2026 * @since 1.0.0
2027 *
2028 * @param [type] $spos [description]
2029 * @return [type] [description]
2030 */
2031 protected function _parsesheet( $spos ) {
2032 $cont = true;
2033 $data = $this->data;
2034 // read BOF
2035 // $code = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2036 $length = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2037
2038 $version = ord( $data[ $spos + 4 ] ) | ord( $data[ $spos + 5 ] ) << 8;
2039 $substreamType = ord( $data[ $spos + 6 ] ) | ord( $data[ $spos + 7 ] ) << 8;
2040
2041 if ( SPREADSHEET_EXCEL_READER_BIFF8 !== $version && SPREADSHEET_EXCEL_READER_BIFF7 !== $version ) {
2042 return -1;
2043 }
2044
2045 if ( SPREADSHEET_EXCEL_READER_WORKSHEET !== $substreamType ) {
2046 return -2;
2047 }
2048
2049 $spos += $length + 4;
2050 while ( $cont ) {
2051 $lowcode = ord( $data[ $spos ] );
2052 if ( SPREADSHEET_EXCEL_READER_TYPE_EOF === $lowcode ) {
2053 break;
2054 }
2055 $code = $lowcode | ord( $data[ $spos + 1 ] ) << 8;
2056 $length = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2057 $spos += 4;
2058 $this->sheets[ $this->sn ]['maxrow'] = $this->_rowoffset - 1;
2059 $this->sheets[ $this->sn ]['maxcol'] = $this->_coloffset - 1;
2060 unset( $this->rectype );
2061 switch ( $code ) {
2062 case SPREADSHEET_EXCEL_READER_TYPE_DIMENSION:
2063 if ( ! isset( $this->numRows ) ) {
2064 if ( 10 === $length || SPREADSHEET_EXCEL_READER_BIFF7 === $version ) {
2065 $this->sheets[ $this->sn ]['numRows'] = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2066 $this->sheets[ $this->sn ]['numCols'] = ord( $data[ $spos + 6 ] ) | ord( $data[ $spos + 7 ] ) << 8;
2067 } else {
2068 $this->sheets[ $this->sn ]['numRows'] = ord( $data[ $spos + 4 ] ) | ord( $data[ $spos + 5 ] ) << 8;
2069 $this->sheets[ $this->sn ]['numCols'] = ord( $data[ $spos + 10 ] ) | ord( $data[ $spos + 11 ] ) << 8;
2070 }
2071 }
2072 break;
2073 case SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS:
2074 $cellRanges = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2075 for ( $i = 0; $i < $cellRanges; $i++ ) {
2076 $fr = ord( $data[ $spos + 8 * $i + 2 ] ) | ord( $data[ $spos + 8 * $i + 3 ] ) << 8;
2077 $lr = ord( $data[ $spos + 8 * $i + 4 ] ) | ord( $data[ $spos + 8 * $i + 5 ] ) << 8;
2078 $fc = ord( $data[ $spos + 8 * $i + 6 ] ) | ord( $data[ $spos + 8 * $i + 7 ] ) << 8;
2079 $lc = ord( $data[ $spos + 8 * $i + 8 ] ) | ord( $data[ $spos + 8 * $i + 9 ] ) << 8;
2080 if ( $lr - $fr > 0 ) {
2081 $this->sheets[ $this->sn ]['cellsInfo'][ $fr + 1 ][ $fc + 1 ]['rowspan'] = $lr - $fr + 1;
2082 }
2083 if ( $lc - $fc > 0 ) {
2084 $this->sheets[ $this->sn ]['cellsInfo'][ $fr + 1 ][ $fc + 1 ]['colspan'] = $lc - $fc + 1;
2085 }
2086 }
2087 break;
2088 case SPREADSHEET_EXCEL_READER_TYPE_RK:
2089 case SPREADSHEET_EXCEL_READER_TYPE_RK2:
2090 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2091 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2092 $rknum = $this->_GetInt4d( $data, $spos + 6 );
2093 $numValue = $this->_GetIEEE754( $rknum );
2094 $info = $this->_getCellDetails( $spos, $numValue, $column );
2095 $this->addcell( $row, $column, $info['string'], $info );
2096 break;
2097 case SPREADSHEET_EXCEL_READER_TYPE_LABELSST:
2098 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2099 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2100 $xfindex = ord( $data[ $spos + 4 ] ) | ord( $data[ $spos + 5 ] ) << 8;
2101 $index = $this->_GetInt4d( $data, $spos + 6 );
2102 $this->addcell( $row, $column, $this->sst[ $index ], array( 'xfIndex' => $xfindex ) );
2103 break;
2104 case SPREADSHEET_EXCEL_READER_TYPE_MULRK:
2105 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2106 $colFirst = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2107 $colLast = ord( $data[ $spos + $length - 2 ] ) | ord( $data[ $spos + $length - 1 ] ) << 8;
2108 $columns = $colLast - $colFirst + 1;
2109 $tmppos = $spos + 4;
2110 for ( $i = 0; $i < $columns; $i++ ) {
2111 $numValue = $this->_GetIEEE754( $this->_GetInt4d( $data, $tmppos + 2 ) );
2112 $info = $this->_getCellDetails( $tmppos - 4, $numValue, $colFirst + $i + 1 );
2113 $tmppos += 6;
2114 $this->addcell( $row, $colFirst + $i, $info['string'], $info );
2115 }
2116 break;
2117 case SPREADSHEET_EXCEL_READER_TYPE_NUMBER:
2118 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2119 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2120 $tmp = unpack( 'ddouble', substr( $data, $spos + 6, 8 ) ); // It machine machine dependent
2121 if ( $this->isDate( $spos ) ) {
2122 $numValue = $tmp['double'];
2123 } else {
2124 $numValue = $this->createNumber( $spos );
2125 }
2126 $info = $this->_getCellDetails( $spos, $numValue, $column );
2127 $this->addcell( $row, $column, $info['string'], $info );
2128 break;
2129
2130 case SPREADSHEET_EXCEL_READER_TYPE_FORMULA:
2131 case SPREADSHEET_EXCEL_READER_TYPE_FORMULA2:
2132 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2133 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2134 if ( 0 === ord( $data[ $spos + 6 ] ) && 255 === ord( $data[ $spos + 12 ] ) && 255 === ord( $data[ $spos + 13 ] ) ) {
2135 // String formula. Result follows in a STRING record
2136 // This row/col are stored to be referenced in that record
2137 // https://code.google.com/archive/p/php-excel-reader/issues/4
2138 $previousRow = $row;
2139 $previousCol = $column;
2140 } elseif ( 1 === ord( $data[ $spos + 6 ] ) && 255 === ord( $data[ $spos + 12 ] ) && 255 === ord( $data[ $spos + 13 ] ) ) {
2141 // Boolean formula. Result is in +2; 0 = false,1 = true
2142 // https://code.google.com/archive/p/php-excel-reader/issues/4
2143 if ( 1 === ord( $this->data[ $spos + 8 ] ) ) {
2144 $this->addcell( $row, $column, 'TRUE' );
2145 } else {
2146 $this->addcell( $row, $column, 'FALSE' );
2147 }
2148 } elseif ( 2 === ord( $data[ $spos + 6 ] ) && 255 === ord( $data[ $spos + 12 ] ) && 255 === ord( $data[ $spos + 13 ] ) ) {
2149 // Error formula. Error code is in +2;
2150 } elseif ( 3 === ord( $data[ $spos + 6 ] ) && 255 === ord( $data[ $spos + 12 ] ) && 255 === ord( $data[ $spos + 13 ] ) ) {
2151 // Formula result is a null string.
2152 $this->addcell( $row, $column, '' );
2153 } else {
2154 // Result is a number, so first 14 bytes are just like a _NUMBER record
2155 $tmp = unpack( 'ddouble', substr( $data, $spos + 6, 8 ) ); // machine dependent
2156 if ( $this->isDate( $spos ) ) {
2157 $numValue = $tmp['double'];
2158 } else {
2159 $numValue = $this->createNumber( $spos );
2160 }
2161 $info = $this->_getCellDetails( $spos, $numValue, $column );
2162 $this->addcell( $row, $column, $info['string'], $info );
2163 }
2164 break;
2165 case SPREADSHEET_EXCEL_READER_TYPE_BOOLERR:
2166 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2167 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2168 $string = ord( $data[ $spos + 6 ] );
2169 $this->addcell( $row, $column, $string );
2170 break;
2171 case SPREADSHEET_EXCEL_READER_TYPE_STRING:
2172 // https://code.google.com/archive/p/php-excel-reader/issues/4
2173 if ( SPREADSHEET_EXCEL_READER_BIFF8 === $version ) {
2174 // Unicode 16 string, like an SST record
2175 $xpos = $spos;
2176 $numChars = ord( $data[ $xpos ] ) | ( ord( $data[ $xpos + 1 ] ) << 8 );
2177 $xpos += 2;
2178 $optionFlags = ord( $data[ $xpos ] );
2179 $xpos++;
2180 $asciiEncoding = ( 0 === ( $optionFlags & 0x01 ) );
2181 $extendedString = ( 0 !== ( $optionFlags & 0x04 ) );
2182 // See if string contains formatting information
2183 $richString = ( 0 !== ( $optionFlags & 0x08 ) );
2184 if ( $richString ) {
2185 // Read in the crun
2186 // $formattingRuns = ord( $data[ $xpos ] ) | ( ord( $data[ $xpos + 1 ] ) << 8 );
2187 $xpos += 2;
2188 }
2189 if ( $extendedString ) {
2190 // Read in cchExtRst
2191 // $extendedRunLength =$this->_GetInt4d( $this->data, $xpos );
2192 $xpos += 4;
2193 }
2194 $len = ( $asciiEncoding ) ? $numChars : $numChars * 2;
2195 $retstr = substr( $data, $xpos, $len );
2196 $xpos += $len;
2197 $retstr = ( $asciiEncoding ) ? $retstr : $this->_encodeUTF16( $retstr );
2198 } elseif ( SPREADSHEET_EXCEL_READER_BIFF7 === $version ) {
2199 // Simple byte string
2200 $xpos = $spos;
2201 $numChars = ord( $data[ $xpos ] ) | ( ord( $data[ $xpos + 1 ] ) << 8 );
2202 $xpos += 2;
2203 $retstr = substr( $data, $xpos, $numChars );
2204 }
2205 $this->addcell( $previousRow, $previousCol, $retstr );
2206 break;
2207 case SPREADSHEET_EXCEL_READER_TYPE_ROW:
2208 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2209 $rowInfo = ord( $data[ $spos + 6 ] ) | ( ( ord( $data[ $spos + 7 ] ) << 8 ) & 0x7FFF );
2210 if ( ( $rowInfo & 0x8000 ) > 0 ) {
2211 $rowHeight = -1;
2212 } else {
2213 $rowHeight = $rowInfo & 0x7FFF;
2214 }
2215 $rowHidden = ( ord( $data[ $spos + 12 ] ) & 0x20 ) >> 5;
2216 $this->rowInfo[ $this->sn ][ $row + 1 ] = array(
2217 'height' => $rowHeight / 20,
2218 'hidden' => $rowHidden,
2219 );
2220 break;
2221 case SPREADSHEET_EXCEL_READER_TYPE_DBCELL:
2222 break;
2223 case SPREADSHEET_EXCEL_READER_TYPE_MULBLANK:
2224 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2225 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2226 $cols = ( $length / 2 ) - 3;
2227 for ( $c = 0; $c < $cols; $c++ ) {
2228 $xfindex = ord( $data[ $spos + 4 + ( $c * 2 ) ] ) | ord( $data[ $spos + 5 + ( $c * 2 ) ] ) << 8;
2229 $this->addcell( $row, $column + $c, '', array( 'xfIndex' => $xfindex ) );
2230 }
2231 break;
2232 case SPREADSHEET_EXCEL_READER_TYPE_LABEL:
2233 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2234 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2235 $this->addcell( $row, $column, substr( $data, $spos + 8, ord( $data[ $spos + 6 ] ) | ord( $data[ $spos + 7 ] ) << 8 ) );
2236 break;
2237 case SPREADSHEET_EXCEL_READER_TYPE_EOF:
2238 $cont = false;
2239 break;
2240 case SPREADSHEET_EXCEL_READER_TYPE_HYPER:
2241 // Only handle hyperlinks to a URL
2242 $row = ord( $this->data[ $spos ] ) | ord( $this->data[ $spos + 1 ] ) << 8;
2243 $row2 = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
2244 $column = ord( $this->data[ $spos + 4 ] ) | ord( $this->data[ $spos + 5 ] ) << 8;
2245 $column2 = ord( $this->data[ $spos + 6 ] ) | ord( $this->data[ $spos + 7 ] ) << 8;
2246 $linkdata = array();
2247 $flags = ord( $this->data[ $spos + 28 ] );
2248 $udesc = '';
2249 $ulink = '';
2250 $uloc = 32;
2251 $linkdata['flags'] = $flags;
2252 if ( ( $flags & 1 ) > 0 ) { // is a type we understand
2253 // is there a description ?
2254 if ( 0x14 === ( $flags & 0x14 ) ) { // has a description
2255 $uloc += 4;
2256 $descLen = ord( $this->data[ $spos + 32 ] ) | ord( $this->data[ $spos + 33 ] ) << 8;
2257 $udesc = substr( $this->data, $spos + $uloc, $descLen * 2 );
2258 $uloc += 2 * $descLen;
2259 }
2260 $ulink = $this->read16bitstring( $this->data, $spos + $uloc + 20 );
2261 if ( '' === $udesc ) {
2262 $udesc = $ulink;
2263 }
2264 }
2265 $linkdata['desc'] = $udesc;
2266 $linkdata['link'] = $this->_encodeUTF16( $ulink );
2267 for ( $r = $row; $r <= $row2; $r++ ) {
2268 for ( $c = $column; $c <= $column2; $c++ ) {
2269 $this->sheets[ $this->sn ]['cellsInfo'][ $r + 1 ][ $c + 1 ]['hyperlink'] = $linkdata;
2270 }
2271 }
2272 break;
2273 case SPREADSHEET_EXCEL_READER_TYPE_DEFCOLWIDTH:
2274 $this->defaultColWidth = ord( $data[ $spos + 4 ] ) | ord( $data[ $spos + 5 ] ) << 8;
2275 break;
2276 case SPREADSHEET_EXCEL_READER_TYPE_STANDARDWIDTH:
2277 $this->standardColWidth = ord( $data[ $spos + 4 ] ) | ord( $data[ $spos + 5 ] ) << 8;
2278 break;
2279 case SPREADSHEET_EXCEL_READER_TYPE_COLINFO:
2280 $colfrom = ord( $data[ $spos + 0 ] ) | ord( $data[ $spos + 1 ] ) << 8;
2281 $colto = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2282 $cw = ord( $data[ $spos + 4 ] ) | ord( $data[ $spos + 5 ] ) << 8;
2283 $cxf = ord( $data[ $spos + 6 ] ) | ord( $data[ $spos + 7 ] ) << 8;
2284 $co = ord( $data[ $spos + 8 ] );
2285 for ( $coli = $colfrom; $coli <= $colto; $coli++ ) {
2286 $this->colInfo[ $this->sn ][ $coli + 1 ] = array(
2287 'width' => $cw,
2288 'xf' => $cxf,
2289 'hidden' => ( $co & 0x01 ),
2290 'collapsed' => ( $co & 0x1000 ) >> 12,
2291 );
2292 }
2293 break;
2294 default:
2295 break;
2296 } // switch
2297 $spos += $length;
2298 } // while
2299
2300 if ( ! isset( $this->sheets[ $this->sn ]['numRows'] ) ) {
2301 $this->sheets[ $this->sn ]['numRows'] = $this->sheets[ $this->sn ]['maxrow'];
2302 }
2303 if ( ! isset( $this->sheets[ $this->sn ]['numCols'] ) ) {
2304 $this->sheets[ $this->sn ]['numCols'] = $this->sheets[ $this->sn ]['maxcol'];
2305 }
2306 }
2307
2308 /**
2309 * [isDate description]
2310 *
2311 * @since 1.0.0
2312 *
2313 * @param [type] $spos [description]
2314 * @return bool [description]
2315 */
2316 protected function isDate( $spos ) {
2317 $xfindex = ord( $this->data[ $spos + 4 ] ) | ord( $this->data[ $spos + 5 ] ) << 8;
2318 return ( 'date' === $this->xfRecords[ $xfindex ]['type'] );
2319 }
2320 /**
2321 * [gmgetdate description]
2322 *
2323 * @since 1.0.0
2324 *
2325 * @param [type] $ts Optional. [description]
2326 * @return [type] [description]
2327 */
2328 protected function gmgetdate( $ts = null ) {
2329 $k = array( 'seconds', 'minutes', 'hours', 'mday', 'wday', 'mon', 'year', 'yday', 'weekday', 'month', 0 );
2330 return array_combine( $k, explode( ':', gmdate( 's:i:G:j:w:n:Y:z:l:F:U', is_null( $ts ) ? time() : $ts ) ) );
2331 }
2332
2333 /**
2334 * Get the details for a particular cell
2335 *
2336 * @since 1.0.0
2337 *
2338 * @param [type] $spos [description]
2339 * @param [type] $numValue [description]
2340 * @param [type] $column [description]
2341 * @return [type] [description]
2342 */
2343 protected function _getCellDetails( $spos, $numValue, $column ) {
2344 $xfindex = ord( $this->data[ $spos + 4 ] ) | ord( $this->data[ $spos + 5 ] ) << 8;
2345 $xfrecord = $this->xfRecords[ $xfindex ];
2346 $type = $xfrecord['type'];
2347
2348 $format = $xfrecord['format'];
2349 $formatIndex = $xfrecord['formatIndex'];
2350 $fontIndex = $xfrecord['fontIndex'];
2351 $formatColor = '';
2352
2353 if ( isset( $this->_columnsFormat[ $column + 1 ] ) ) {
2354 $format = $this->_columnsFormat[ $column + 1 ];
2355 }
2356
2357 if ( 'date' === $type ) {
2358 // See https://groups.google.com/forum/#!topic/php-excel-reader-discuss/nD-XkNEtjhA
2359 $rectype = 'date';
2360 // Convert numeric value into a date
2361 $utcDays = floor( $numValue - ( $this->nineteenFour ? SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904 : SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS ) );
2362 $utcValue = ( $utcDays ) * SPREADSHEET_EXCEL_READER_MSINADAY;
2363 $dateinfo = $this->gmgetdate( $utcValue );
2364
2365 $raw = $numValue;
2366 $fractionalDay = $numValue - floor( $numValue ) + 0.0000001; // The 0.0000001 is to fix for php/excel fractional diffs
2367
2368 $totalseconds = floor( SPREADSHEET_EXCEL_READER_MSINADAY * $fractionalDay );
2369 $secs = $totalseconds % 60;
2370 $totalseconds -= $secs;
2371 $hours = floor( $totalseconds / ( 60 * 60 ) );
2372 $mins = floor( $totalseconds / 60 ) % 60;
2373 $string = date( $format, mktime( $hours, $mins, $secs, $dateinfo['mon'], $dateinfo['mday'], $dateinfo['year'] ) );
2374 } elseif ( 'number' === $type ) {
2375 $rectype = 'number';
2376 $formatted = $this->_format_value( $format, $numValue, $formatIndex );
2377 $string = $formatted['string'];
2378 $formatColor = $formatted['formatColor'];
2379 $raw = $numValue;
2380 } else {
2381 if ( '' === $format ) {
2382 $format = $this->_defaultFormat;
2383 }
2384 $rectype = 'unknown';
2385 $formatted = $this->_format_value( $format, $numValue, $formatIndex );
2386 $string = $formatted['string'];
2387 $formatColor = $formatted['formatColor'];
2388 $raw = $numValue;
2389 }
2390
2391 return array(
2392 'string' => $string,
2393 'raw' => $raw,
2394 'rectype' => $rectype,
2395 'format' => $format,
2396 'formatIndex' => $formatIndex,
2397 'fontIndex' => $fontIndex,
2398 'formatColor' => $formatColor,
2399 'xfIndex' => $xfindex,
2400 );
2401 }
2402
2403 /**
2404 * [createNumber description]
2405 *
2406 * @since 1.0.0
2407 *
2408 * @param [type] $spos [description]
2409 * @return [type] [description]
2410 */
2411 protected function createNumber( $spos ) {
2412 $rknumhigh = $this->_GetInt4d( $this->data, $spos + 10 );
2413 $rknumlow = $this->_GetInt4d( $this->data, $spos + 6 );
2414 $sign = ( $rknumhigh & 0x80000000 ) >> 31;
2415 $exp = ( $rknumhigh & 0x7ff00000 ) >> 20;
2416 $mantissa = ( 0x100000 | ( $rknumhigh & 0x000fffff ) );
2417 $mantissalow1 = ( $rknumlow & 0x80000000 ) >> 31;
2418 $mantissalow2 = ( $rknumlow & 0x7fffffff );
2419 $value = $mantissa / pow( 2, ( 20 - ( $exp - 1023 ) ) );
2420 if ( 0 !== $mantissalow1 ) {
2421 $value += 1 / pow( 2, ( 21 - ( $exp - 1023 ) ) );
2422 }
2423 $value += $mantissalow2 / pow( 2, ( 52 - ( $exp - 1023 ) ) );
2424 if ( $sign ) {
2425 $value *= -1;
2426 }
2427 return $value;
2428 }
2429
2430 /**
2431 * [addcell description]
2432 *
2433 * @since 1.0.0
2434 *
2435 * @param [type] $row [description]
2436 * @param [type] $col [description]
2437 * @param [type] $string [description]
2438 * @param [type] $info Optional. [description]
2439 * @return [type] [description]
2440 */
2441 protected function addcell( $row, $col, $string, $info = null ) {
2442 $this->sheets[ $this->sn ]['maxrow'] = max( $this->sheets[ $this->sn ]['maxrow'], $row + $this->_rowoffset );
2443 $this->sheets[ $this->sn ]['maxcol'] = max( $this->sheets[ $this->sn ]['maxcol'], $col + $this->_coloffset );
2444 $this->sheets[ $this->sn ]['cells'][ $row + $this->_rowoffset ][ $col + $this->_coloffset ] = $string;
2445 if ( $this->store_extended_info && $info ) {
2446 foreach ( $info as $key => $val ) {
2447 $this->sheets[ $this->sn ]['cellsInfo'][ $row + $this->_rowoffset ][ $col + $this->_coloffset ][ $key ] = $val;
2448 }
2449 }
2450 }
2451
2452 /**
2453 * [_GetIEEE754 description]
2454 *
2455 * @since 1.0.0
2456 *
2457 * @param [type] $rknum [description]
2458 * @return [type] [description]
2459 */
2460 protected function _GetIEEE754( $rknum ) {
2461 if ( 0 !== ( $rknum & 0x02 ) ) {
2462 $value = $rknum >> 2;
2463 } else {
2464 // info on IEEE754 encoding from
2465 // http://research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
2466 // The RK format calls for using only the most significant 30 bits of the
2467 // 64 bit floating point value. The other 34 bits are assumed to be 0
2468 // So, we use the upper 30 bits of $rknum as follows...
2469 $sign = ( $rknum & 0x80000000 ) >> 31;
2470 $exp = ( $rknum & 0x7ff00000 ) >> 20;
2471 $mantissa = ( 0x100000 | ( $rknum & 0x000ffffc ) );
2472 $value = $mantissa / pow( 2, ( 20 - ( $exp - 1023 ) ) );
2473 if ( $sign ) {
2474 $value *= -1;
2475 }
2476 }
2477 if ( 0 !== ( $rknum & 0x01 ) ) {
2478 $value /= 100;
2479 }
2480 return $value;
2481 }
2482
2483 /**
2484 * [_encodeUTF16 description]
2485 *
2486 * @since 1.0.0
2487 *
2488 * @param [type] $string [description]
2489 * @return [type] [description]
2490 */
2491 protected function _encodeUTF16( $string ) {
2492 if ( $this->_defaultEncoding ) {
2493 switch ( $this->_encoderFunction ) {
2494 case 'iconv':
2495 $string = iconv( 'UTF-16LE', $this->_defaultEncoding, $string );
2496 break;
2497 case 'mb_convert_encoding':
2498 $string = mb_convert_encoding( $string, $this->_defaultEncoding, 'UTF-16LE' );
2499 break;
2500 }
2501 }
2502 return $string;
2503 }
2504
2505 /**
2506 * [_GetInt4d description]
2507 *
2508 * @since 1.0.0
2509 *
2510 * @param [type] $data [description]
2511 * @param [type] $pos [description]
2512 * @return [type] [description]
2513 */
2514 protected function _GetInt4d( $data, $pos ) {
2515 $value = ord( $data[ $pos ] ) | ( ord( $data[ $pos + 1 ] ) << 8 ) | ( ord( $data[ $pos + 2 ] ) << 16 ) | ( ord( $data[ $pos + 3 ] ) << 24 );
2516 if ( $value >= 4294967294 ) {
2517 $value = -2;
2518 }
2519 return $value;
2520 }
2521
2522 /**
2523 * [v description]
2524 *
2525 * @since 1.0.0
2526 *
2527 * @param [type] $data [description]
2528 * @param [type] $pos [description]
2529 * @return [type] [description]
2530 */
2531 protected function v( $data, $pos ) {
2532 return ord( $data[ $pos ] ) | ord( $data[ $pos + 1 ] ) << 8;
2533 }
2534
2535 } // class Spreadsheet_Excel_Reader
2536