PluginProbe
TablePress – Tables in WordPress made easy / 2.0.4
TablePress – Tables in WordPress made easy v2.0.4
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 2.0.4, at libraries/excel-reader.class.php

2,535 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 string $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 [description]
1067 * @param string $prop [description]
1068 * @return [type] [description]
1069 */
1070 public function fontProperty( $row, $col, $sheet, $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 string $data [description]
1675 */
1676 public function read( $data ) {
1677 $res = $this->_ole->read( $data );
1678
1679 // oops, something goes wrong (Darko Miljanovic)
1680 if ( false === $res ) {
1681 // check error code
1682 if ( 1 === $this->_ole->error ) {
1683 die( 'Data is not readable' );
1684 } elseif ( 2 === $this->_ole->error ) {
1685 die( 'OLE error' );
1686 }
1687 // check other error codes here (e.g. bad fileformat, etc...)
1688 }
1689 $this->data = $this->_ole->getWorkBook();
1690 $this->_parse();
1691 }
1692
1693 /**
1694 * Parse a workbook.
1695 *
1696 * @since 1.0.0
1697 *
1698 * @return [type] [description]
1699 */
1700 protected function _parse() {
1701 $pos = 0;
1702 $data = $this->data;
1703
1704 //$code = $this->v( $data, $pos );
1705 $length = $this->v( $data, $pos + 2 );
1706 $version = $this->v( $data, $pos + 4 );
1707 $substreamType = $this->v( $data, $pos + 6 );
1708 if ( SPREADSHEET_EXCEL_READER_BIFF8 !== $version && SPREADSHEET_EXCEL_READER_BIFF7 !== $version ) {
1709 return false;
1710 }
1711
1712 if ( SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS !== $substreamType ) {
1713 return false;
1714 }
1715
1716 $pos += $length + 4;
1717
1718 $code = $this->v( $data, $pos );
1719 $length = $this->v( $data, $pos + 2 );
1720
1721 while ( SPREADSHEET_EXCEL_READER_TYPE_EOF !== $code ) {
1722 switch ( $code ) {
1723 case SPREADSHEET_EXCEL_READER_TYPE_SST:
1724 $spos = $pos + 4;
1725 $limitpos = $spos + $length;
1726 $uniqueStrings = $this->_GetInt4d( $data, $spos + 4 );
1727 $spos += 8;
1728 for ( $i = 0; $i < $uniqueStrings; $i++ ) {
1729 // Read in the number of characters
1730 if ( $spos === $limitpos ) {
1731 $opcode = $this->v( $data, $spos );
1732 $conlength = $this->v( $data, $spos + 2 );
1733 if ( 0x3c !== $opcode ) {
1734 return -1;
1735 }
1736 $spos += 4;
1737 $limitpos = $spos + $conlength;
1738 }
1739 $numChars = ord( $data[ $spos ] ) | ( ord( $data[ $spos + 1 ] ) << 8 );
1740 $spos += 2;
1741 $optionFlags = ord( $data[ $spos ] );
1742 $spos++;
1743 $asciiEncoding = ( 0 === ( $optionFlags & 0x01 ) );
1744 $extendedString = ( 0 !== ( $optionFlags & 0x04 ) );
1745
1746 // See if string contains formatting information.
1747 $richString = ( 0 !== ( $optionFlags & 0x08 ) );
1748
1749 if ( $richString ) {
1750 // Read in the crun
1751 $formattingRuns = $this->v( $data, $spos );
1752 $spos += 2;
1753 }
1754
1755 if ( $extendedString ) {
1756 // Read in cchExtRst
1757 $extendedRunLength = $this->_GetInt4d( $data, $spos );
1758 $spos += 4;
1759 }
1760
1761 $len = ( $asciiEncoding ) ? $numChars : $numChars * 2;
1762 if ( $spos + $len < $limitpos ) {
1763 $retstr = substr( $data, $spos, $len );
1764 $spos += $len;
1765 } else {
1766 // found continue
1767 $retstr = substr( $data, $spos, $limitpos - $spos );
1768 $bytesRead = $limitpos - $spos;
1769 $charsLeft = $numChars - ( ( $asciiEncoding ) ? $bytesRead : ( $bytesRead / 2 ) );
1770 $spos = $limitpos;
1771
1772 while ( $charsLeft > 0 ) {
1773 $opcode = $this->v( $data, $spos );
1774 $conlength = $this->v( $data, $spos + 2 );
1775 if ( 0x3c !== $opcode ) {
1776 return -1;
1777 }
1778 $spos += 4;
1779 $limitpos = $spos + $conlength;
1780 $option = ord( $data[ $spos ] );
1781 $spos += 1;
1782 if ( $asciiEncoding && 0 === $option ) {
1783 $len = min( $charsLeft, $limitpos - $spos ); // min( $charsLeft, $conlength );
1784 $retstr .= substr( $data, $spos, $len );
1785 $charsLeft -= $len;
1786 $asciiEncoding = true;
1787 } elseif ( ! $asciiEncoding && 0 !== $option ) {
1788 $len = min( $charsLeft * 2, $limitpos - $spos ); // min( $charsLeft, $conlength );
1789 $retstr .= substr( $data, $spos, $len );
1790 $charsLeft -= $len / 2;
1791 $asciiEncoding = false;
1792 } elseif ( ! $asciiEncoding && 0 === $option ) {
1793 // Bummer - the string starts off as Unicode, but after the
1794 // continuation it is in straightforward ASCII encoding
1795 $len = min( $charsLeft, $limitpos - $spos ); // min( $charsLeft, $conlength );
1796 for ( $j = 0; $j < $len; $j++ ) {
1797 $retstr .= $data[ $spos + $j ] . chr( 0 );
1798 }
1799 $charsLeft -= $len;
1800 $asciiEncoding = false;
1801 } else {
1802 $newstr = '';
1803 for ( $j = 0; $j < strlen( $retstr ); $j++ ) {
1804 $newstr = $retstr[ $j ] . chr( 0 );
1805 }
1806 $retstr = $newstr;
1807 $len = min( $charsLeft * 2, $limitpos - $spos ); // min( $charsLeft, $conlength );
1808 $retstr .= substr( $data, $spos, $len );
1809 $charsLeft -= $len / 2;
1810 $asciiEncoding = false;
1811 }
1812 $spos += $len;
1813 }
1814 }
1815 $retstr = ( $asciiEncoding ) ? $retstr : $this->_encodeUTF16( $retstr );
1816
1817 if ( $richString ) {
1818 $spos += 4 * $formattingRuns;
1819 }
1820
1821 // For extended strings, skip over the extended string data
1822 if ( $extendedString ) {
1823 $spos += $extendedRunLength;
1824 }
1825 $this->sst[] = $retstr;
1826 }
1827 break;
1828 case SPREADSHEET_EXCEL_READER_TYPE_FILEPASS:
1829 return false;
1830 // break; // unreachable
1831 case SPREADSHEET_EXCEL_READER_TYPE_NAME:
1832 break;
1833 case SPREADSHEET_EXCEL_READER_TYPE_FORMAT:
1834 $indexCode = $this->v( $data, $pos + 4 );
1835 if ( SPREADSHEET_EXCEL_READER_BIFF8 === $version ) {
1836 $numchars = $this->v( $data, $pos + 6 );
1837 if ( 0 === ord( $data[ $pos + 8 ] ) ) {
1838 $formatString = substr( $data, $pos + 9, $numchars );
1839 } else {
1840 $formatString = substr( $data, $pos + 9, $numchars * 2 );
1841 }
1842 } else {
1843 $numchars = ord( $data[ $pos + 6 ] );
1844 $formatString = substr( $data, $pos + 7, $numchars * 2 );
1845 }
1846 $this->formatRecords[ $indexCode ] = $formatString;
1847 break;
1848 case SPREADSHEET_EXCEL_READER_TYPE_FONT:
1849 $height = $this->v( $data, $pos + 4 );
1850 $option = $this->v( $data, $pos + 6 );
1851 $color = $this->v( $data, $pos + 8 );
1852 $weight = $this->v( $data, $pos + 10 );
1853 $under = ord( $data[ $pos + 14 ] );
1854 // Font name
1855 $numchars = ord( $data[ $pos + 18 ] );
1856 if ( 0 === ( ord( $data[ $pos + 19 ] ) & 1 ) ) {
1857 $font = substr( $data, $pos + 20, $numchars );
1858 } else {
1859 $font = substr( $data, $pos + 20, $numchars * 2 );
1860 $font = $this->_encodeUTF16( $font );
1861 }
1862 $this->fontRecords[] = array(
1863 'height' => $height / 20,
1864 'italic' => (bool) ( $option & 2 ),
1865 'color' => $color,
1866 'under' => ( 0 !== $under ),
1867 'bold' => ( 700 === $weight ),
1868 'font' => $font,
1869 'raw' => $this->dumpHexData( $data, $pos + 3, $length ),
1870 );
1871 break;
1872 case SPREADSHEET_EXCEL_READER_TYPE_PALETTE:
1873 $colors = ord( $data[ $pos + 4 ] ) | ord( $data[ $pos + 5 ] ) << 8;
1874 for ( $coli = 0; $coli < $colors; $coli++ ) {
1875 $colOff = $pos + 2 + ( $coli * 4 );
1876 $colr = ord( $data[ $colOff ] );
1877 $colg = ord( $data[ $colOff + 1 ] );
1878 $colb = ord( $data[ $colOff + 2 ] );
1879 $this->colors[ 0x07 + $coli ] = '#' . $this->myhex( $colr ) . $this->myhex( $colg ) . $this->myhex( $colb );
1880 }
1881 break;
1882 case SPREADSHEET_EXCEL_READER_TYPE_XF:
1883 $fontIndexCode = ( ord( $data[ $pos + 4 ] ) | ord( $data[ $pos + 5 ] ) << 8 ) - 1;
1884 $fontIndexCode = max( 0, $fontIndexCode );
1885 $indexCode = ord( $data[ $pos + 6 ] ) | ord( $data[ $pos + 7 ] ) << 8;
1886 $alignbit = ord( $data[ $pos + 10 ] ) & 3;
1887 $bgi = ( ord( $data[ $pos + 22 ] ) | ord( $data[ $pos + 23 ] ) << 8 ) & 0x3FFF;
1888 $bgcolor = ( $bgi & 0x7F );
1889 // $bgcolor = ( $bgi & 0x3f80 ) >> 7;
1890 $align = '';
1891 if ( 3 === $alignbit ) {
1892 $align = 'right';
1893 } elseif ( 2 === $alignbit ) {
1894 $align = 'center';
1895 }
1896 $fillPattern = ( ord( $data[ $pos + 21 ] ) & 0xFC ) >> 2;
1897 if ( 0 === $fillPattern ) {
1898 $bgcolor = '';
1899 }
1900
1901 $xf = array();
1902 $xf['formatIndex'] = $indexCode;
1903 $xf['align'] = $align;
1904 $xf['fontIndex'] = $fontIndexCode;
1905 $xf['bgColor'] = $bgcolor;
1906 $xf['fillPattern'] = $fillPattern;
1907
1908 $border = ord( $data[ $pos + 14 ] ) | ( ord( $data[ $pos + 15 ] ) << 8 ) | ( ord( $data[ $pos + 16 ] ) << 16 ) | ( ord( $data[ $pos + 17 ] ) << 24 );
1909 $xf['borderLeft'] = $this->lineStyles[ ( $border & 0xF ) ];
1910 $xf['borderRight'] = $this->lineStyles[ ( $border & 0xF0 ) >> 4 ];
1911 $xf['borderTop'] = $this->lineStyles[ ( $border & 0xF00 ) >> 8 ];
1912 $xf['borderBottom'] = $this->lineStyles[ ( $border & 0xF000 ) >> 12 ];
1913
1914 $xf['borderLeftColor'] = ( $border & 0x7F0000 ) >> 16;
1915 $xf['borderRightColor'] = ( $border & 0x3F800000 ) >> 23;
1916 $border = ( ord( $data[ $pos + 18 ] ) | ord( $data[ $pos + 19 ] ) << 8 );
1917 $xf['borderTopColor'] = ( $border & 0x7F );
1918 $xf['borderBottomColor'] = ( $border & 0x3F80 ) >> 7;
1919 if ( array_key_exists( $indexCode, $this->dateFormats ) ) {
1920 $xf['type'] = 'date';
1921 $xf['format'] = $this->dateFormats[ $indexCode ];
1922 if ( '' === $align ) {
1923 $xf['align'] = 'right';
1924 }
1925 } elseif ( array_key_exists( $indexCode, $this->numberFormats ) ) {
1926 $xf['type'] = 'number';
1927 $xf['format'] = $this->numberFormats[ $indexCode ];
1928 if ( '' === $align ) {
1929 $xf['align'] = 'right';
1930 }
1931 } else {
1932 $isdate = false;
1933 $formatstr = '';
1934 if ( $indexCode > 0 ) {
1935 if ( isset( $this->formatRecords[ $indexCode ] ) ) {
1936 $formatstr = $this->formatRecords[ $indexCode ];
1937 }
1938 if ( '' !== $formatstr ) {
1939 $tmp = preg_replace( '/\;.*/', '', $formatstr );
1940 $tmp = preg_replace( '/^\[[^\]]*\]/', '', $tmp );
1941 if ( 0 === preg_match( '/[^hmsday\/\-:\s\\\,AMP]/i', $tmp ) ) { // found day and time format
1942 $isdate = true;
1943 $formatstr = $tmp;
1944 $formatstr = str_replace( array( 'AM/PM', 'mmmm', 'mmm' ), array( 'a', 'F', 'M' ), $formatstr );
1945 // m/mm are used for both minutes and months - oh SNAP!
1946 // This mess tries to fix for that.
1947 // 'm' = minutes only if following h/hh or preceding s/ss
1948 $formatstr = preg_replace( '/(h:?)mm?/', '$1i', $formatstr );
1949 $formatstr = preg_replace( '/mm?(:?s)/', '1$1', $formatstr );
1950 // A single 'm' = n in PHP
1951 $formatstr = preg_replace( '/(^|[^m])m([^m]|$)/', '$1n$2', $formatstr );
1952 $formatstr = preg_replace( '/(^|[^m])m([^m]|$)/', '$1n$2', $formatstr );
1953 // else it's months
1954 $formatstr = str_replace( 'mm', 'm', $formatstr );
1955 // Convert single 'd' to 'j'
1956 $formatstr = preg_replace( '/(^|[^d])d([^d]|$)/', '$1j$2', $formatstr );
1957 $formatstr = str_replace( array( 'dddd', 'ddd', 'dd', 'yyyy', 'yy', 'hh', 'h' ), array( 'l', 'D', 'd', 'Y', 'y', 'H', 'g' ), $formatstr );
1958 $formatstr = preg_replace( '/ss?/', 's', $formatstr );
1959 }
1960 }
1961 }
1962 if ( $isdate ) {
1963 $xf['type'] = 'date';
1964 $xf['format'] = $formatstr;
1965 if ( '' === $align ) {
1966 $xf['align'] = 'right';
1967 }
1968 } else {
1969 // If the format string has a 0 or # in it, we'll assume it's a number.
1970 if ( preg_match( '/[0#]/', $formatstr ) ) {
1971 $xf['type'] = 'number';
1972 if ( '' === $align ) {
1973 $xf['align'] = 'right';
1974 }
1975 } else {
1976 $xf['type'] = 'other';
1977 }
1978 $xf['format'] = $formatstr;
1979 $xf['code'] = $indexCode;
1980 }
1981 }
1982 $this->xfRecords[] = $xf;
1983 break;
1984 case SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR:
1985 $this->nineteenFour = ( 1 === ord( $data[ $pos + 4 ] ) );
1986 break;
1987 case SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET:
1988 $rec_offset = $this->_GetInt4d( $data, $pos + 4 );
1989 //$rec_typeFlag = ord( $data[ $pos + 8 ] );
1990 //$rec_visibilityFlag = ord( $data[ $pos + 9 ] );
1991 $rec_length = ord( $data[ $pos + 10 ] );
1992
1993 if ( SPREADSHEET_EXCEL_READER_BIFF8 === $version ) {
1994 $chartype = ord( $data[ $pos + 11 ] );
1995 if ( 0 === $chartype ) {
1996 $rec_name = substr( $data, $pos + 12, $rec_length );
1997 } else {
1998 $rec_name = $this->_encodeUTF16( substr( $data, $pos + 12, 2 * $rec_length ) );
1999 }
2000 } elseif ( SPREADSHEET_EXCEL_READER_BIFF7 === $version ) {
2001 $rec_name = substr( $data, $pos + 11, $rec_length );
2002 }
2003 $this->boundsheets[] = array(
2004 'name' => $rec_name,
2005 'offset' => $rec_offset,
2006 );
2007 break;
2008 } // switch
2009
2010 $pos += $length + 4;
2011 $code = ord( $data[ $pos ] ) | ord( $data[ $pos + 1 ] ) << 8;
2012 $length = ord( $data[ $pos + 2 ] ) | ord( $data[ $pos + 3 ] ) << 8;
2013 } // while
2014
2015 foreach ( $this->boundsheets as $key => $val ) {
2016 $this->sn = $key;
2017 $this->_parsesheet( $val['offset'] );
2018 }
2019 return true;
2020 }
2021
2022 /**
2023 * Parse a worksheet.
2024 *
2025 * @since 1.0.0
2026 *
2027 * @param [type] $spos [description]
2028 * @return [type] [description]
2029 */
2030 protected function _parsesheet( $spos ) {
2031 $cont = true;
2032 $data = $this->data;
2033 // read BOF
2034 // $code = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2035 $length = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2036
2037 $version = ord( $data[ $spos + 4 ] ) | ord( $data[ $spos + 5 ] ) << 8;
2038 $substreamType = ord( $data[ $spos + 6 ] ) | ord( $data[ $spos + 7 ] ) << 8;
2039
2040 if ( SPREADSHEET_EXCEL_READER_BIFF8 !== $version && SPREADSHEET_EXCEL_READER_BIFF7 !== $version ) {
2041 return -1;
2042 }
2043
2044 if ( SPREADSHEET_EXCEL_READER_WORKSHEET !== $substreamType ) {
2045 return -2;
2046 }
2047
2048 $spos += $length + 4;
2049 while ( $cont ) {
2050 $lowcode = ord( $data[ $spos ] );
2051 if ( SPREADSHEET_EXCEL_READER_TYPE_EOF === $lowcode ) {
2052 break;
2053 }
2054 $code = $lowcode | ord( $data[ $spos + 1 ] ) << 8;
2055 $length = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2056 $spos += 4;
2057 $this->sheets[ $this->sn ]['maxrow'] = $this->_rowoffset - 1;
2058 $this->sheets[ $this->sn ]['maxcol'] = $this->_coloffset - 1;
2059 unset( $this->rectype );
2060 switch ( $code ) {
2061 case SPREADSHEET_EXCEL_READER_TYPE_DIMENSION:
2062 if ( ! isset( $this->numRows ) ) {
2063 if ( 10 === $length || SPREADSHEET_EXCEL_READER_BIFF7 === $version ) {
2064 $this->sheets[ $this->sn ]['numRows'] = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2065 $this->sheets[ $this->sn ]['numCols'] = ord( $data[ $spos + 6 ] ) | ord( $data[ $spos + 7 ] ) << 8;
2066 } else {
2067 $this->sheets[ $this->sn ]['numRows'] = ord( $data[ $spos + 4 ] ) | ord( $data[ $spos + 5 ] ) << 8;
2068 $this->sheets[ $this->sn ]['numCols'] = ord( $data[ $spos + 10 ] ) | ord( $data[ $spos + 11 ] ) << 8;
2069 }
2070 }
2071 break;
2072 case SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS:
2073 $cellRanges = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2074 for ( $i = 0; $i < $cellRanges; $i++ ) {
2075 $fr = ord( $data[ $spos + 8 * $i + 2 ] ) | ord( $data[ $spos + 8 * $i + 3 ] ) << 8;
2076 $lr = ord( $data[ $spos + 8 * $i + 4 ] ) | ord( $data[ $spos + 8 * $i + 5 ] ) << 8;
2077 $fc = ord( $data[ $spos + 8 * $i + 6 ] ) | ord( $data[ $spos + 8 * $i + 7 ] ) << 8;
2078 $lc = ord( $data[ $spos + 8 * $i + 8 ] ) | ord( $data[ $spos + 8 * $i + 9 ] ) << 8;
2079 if ( $lr - $fr > 0 ) {
2080 $this->sheets[ $this->sn ]['cellsInfo'][ $fr + 1 ][ $fc + 1 ]['rowspan'] = $lr - $fr + 1;
2081 }
2082 if ( $lc - $fc > 0 ) {
2083 $this->sheets[ $this->sn ]['cellsInfo'][ $fr + 1 ][ $fc + 1 ]['colspan'] = $lc - $fc + 1;
2084 }
2085 }
2086 break;
2087 case SPREADSHEET_EXCEL_READER_TYPE_RK:
2088 case SPREADSHEET_EXCEL_READER_TYPE_RK2:
2089 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2090 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2091 $rknum = $this->_GetInt4d( $data, $spos + 6 );
2092 $numValue = $this->_GetIEEE754( $rknum );
2093 $info = $this->_getCellDetails( $spos, $numValue, $column );
2094 $this->addcell( $row, $column, $info['string'], $info );
2095 break;
2096 case SPREADSHEET_EXCEL_READER_TYPE_LABELSST:
2097 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2098 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2099 $xfindex = ord( $data[ $spos + 4 ] ) | ord( $data[ $spos + 5 ] ) << 8;
2100 $index = $this->_GetInt4d( $data, $spos + 6 );
2101 $this->addcell( $row, $column, $this->sst[ $index ], array( 'xfIndex' => $xfindex ) );
2102 break;
2103 case SPREADSHEET_EXCEL_READER_TYPE_MULRK:
2104 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2105 $colFirst = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2106 $colLast = ord( $data[ $spos + $length - 2 ] ) | ord( $data[ $spos + $length - 1 ] ) << 8;
2107 $columns = $colLast - $colFirst + 1;
2108 $tmppos = $spos + 4;
2109 for ( $i = 0; $i < $columns; $i++ ) {
2110 $numValue = $this->_GetIEEE754( $this->_GetInt4d( $data, $tmppos + 2 ) );
2111 $info = $this->_getCellDetails( $tmppos - 4, $numValue, $colFirst + $i + 1 );
2112 $tmppos += 6;
2113 $this->addcell( $row, $colFirst + $i, $info['string'], $info );
2114 }
2115 break;
2116 case SPREADSHEET_EXCEL_READER_TYPE_NUMBER:
2117 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2118 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2119 $tmp = unpack( 'ddouble', substr( $data, $spos + 6, 8 ) ); // It machine machine dependent
2120 if ( $this->isDate( $spos ) ) {
2121 $numValue = $tmp['double'];
2122 } else {
2123 $numValue = $this->createNumber( $spos );
2124 }
2125 $info = $this->_getCellDetails( $spos, $numValue, $column );
2126 $this->addcell( $row, $column, $info['string'], $info );
2127 break;
2128
2129 case SPREADSHEET_EXCEL_READER_TYPE_FORMULA:
2130 case SPREADSHEET_EXCEL_READER_TYPE_FORMULA2:
2131 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2132 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2133 if ( 0 === ord( $data[ $spos + 6 ] ) && 255 === ord( $data[ $spos + 12 ] ) && 255 === ord( $data[ $spos + 13 ] ) ) {
2134 // String formula. Result follows in a STRING record
2135 // This row/col are stored to be referenced in that record
2136 // https://code.google.com/archive/p/php-excel-reader/issues/4
2137 $previousRow = $row;
2138 $previousCol = $column;
2139 } elseif ( 1 === ord( $data[ $spos + 6 ] ) && 255 === ord( $data[ $spos + 12 ] ) && 255 === ord( $data[ $spos + 13 ] ) ) {
2140 // Boolean formula. Result is in +2; 0 = false,1 = true
2141 // https://code.google.com/archive/p/php-excel-reader/issues/4
2142 if ( 1 === ord( $this->data[ $spos + 8 ] ) ) {
2143 $this->addcell( $row, $column, 'TRUE' );
2144 } else {
2145 $this->addcell( $row, $column, 'FALSE' );
2146 }
2147 } elseif ( 2 === ord( $data[ $spos + 6 ] ) && 255 === ord( $data[ $spos + 12 ] ) && 255 === ord( $data[ $spos + 13 ] ) ) {
2148 // Error formula. Error code is in +2;
2149 } elseif ( 3 === ord( $data[ $spos + 6 ] ) && 255 === ord( $data[ $spos + 12 ] ) && 255 === ord( $data[ $spos + 13 ] ) ) {
2150 // Formula result is a null string.
2151 $this->addcell( $row, $column, '' );
2152 } else {
2153 // Result is a number, so first 14 bytes are just like a _NUMBER record
2154 $tmp = unpack( 'ddouble', substr( $data, $spos + 6, 8 ) ); // machine dependent
2155 if ( $this->isDate( $spos ) ) {
2156 $numValue = $tmp['double'];
2157 } else {
2158 $numValue = $this->createNumber( $spos );
2159 }
2160 $info = $this->_getCellDetails( $spos, $numValue, $column );
2161 $this->addcell( $row, $column, $info['string'], $info );
2162 }
2163 break;
2164 case SPREADSHEET_EXCEL_READER_TYPE_BOOLERR:
2165 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2166 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2167 $string = ord( $data[ $spos + 6 ] );
2168 $this->addcell( $row, $column, $string );
2169 break;
2170 case SPREADSHEET_EXCEL_READER_TYPE_STRING:
2171 // https://code.google.com/archive/p/php-excel-reader/issues/4
2172 if ( SPREADSHEET_EXCEL_READER_BIFF8 === $version ) {
2173 // Unicode 16 string, like an SST record
2174 $xpos = $spos;
2175 $numChars = ord( $data[ $xpos ] ) | ( ord( $data[ $xpos + 1 ] ) << 8 );
2176 $xpos += 2;
2177 $optionFlags = ord( $data[ $xpos ] );
2178 $xpos++;
2179 $asciiEncoding = ( 0 === ( $optionFlags & 0x01 ) );
2180 $extendedString = ( 0 !== ( $optionFlags & 0x04 ) );
2181 // See if string contains formatting information
2182 $richString = ( 0 !== ( $optionFlags & 0x08 ) );
2183 if ( $richString ) {
2184 // Read in the crun
2185 // $formattingRuns = ord( $data[ $xpos ] ) | ( ord( $data[ $xpos + 1 ] ) << 8 );
2186 $xpos += 2;
2187 }
2188 if ( $extendedString ) {
2189 // Read in cchExtRst
2190 // $extendedRunLength =$this->_GetInt4d( $this->data, $xpos );
2191 $xpos += 4;
2192 }
2193 $len = ( $asciiEncoding ) ? $numChars : $numChars * 2;
2194 $retstr = substr( $data, $xpos, $len );
2195 $xpos += $len;
2196 $retstr = ( $asciiEncoding ) ? $retstr : $this->_encodeUTF16( $retstr );
2197 } elseif ( SPREADSHEET_EXCEL_READER_BIFF7 === $version ) {
2198 // Simple byte string
2199 $xpos = $spos;
2200 $numChars = ord( $data[ $xpos ] ) | ( ord( $data[ $xpos + 1 ] ) << 8 );
2201 $xpos += 2;
2202 $retstr = substr( $data, $xpos, $numChars );
2203 }
2204 $this->addcell( $previousRow, $previousCol, $retstr );
2205 break;
2206 case SPREADSHEET_EXCEL_READER_TYPE_ROW:
2207 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2208 $rowInfo = ord( $data[ $spos + 6 ] ) | ( ( ord( $data[ $spos + 7 ] ) << 8 ) & 0x7FFF );
2209 if ( ( $rowInfo & 0x8000 ) > 0 ) {
2210 $rowHeight = -1;
2211 } else {
2212 $rowHeight = $rowInfo & 0x7FFF;
2213 }
2214 $rowHidden = ( ord( $data[ $spos + 12 ] ) & 0x20 ) >> 5;
2215 $this->rowInfo[ $this->sn ][ $row + 1 ] = array(
2216 'height' => $rowHeight / 20,
2217 'hidden' => $rowHidden,
2218 );
2219 break;
2220 case SPREADSHEET_EXCEL_READER_TYPE_DBCELL:
2221 break;
2222 case SPREADSHEET_EXCEL_READER_TYPE_MULBLANK:
2223 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2224 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2225 $cols = ( $length / 2 ) - 3;
2226 for ( $c = 0; $c < $cols; $c++ ) {
2227 $xfindex = ord( $data[ $spos + 4 + ( $c * 2 ) ] ) | ord( $data[ $spos + 5 + ( $c * 2 ) ] ) << 8;
2228 $this->addcell( $row, $column + $c, '', array( 'xfIndex' => $xfindex ) );
2229 }
2230 break;
2231 case SPREADSHEET_EXCEL_READER_TYPE_LABEL:
2232 $row = ord( $data[ $spos ] ) | ord( $data[ $spos + 1 ] ) << 8;
2233 $column = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2234 $this->addcell( $row, $column, substr( $data, $spos + 8, ord( $data[ $spos + 6 ] ) | ord( $data[ $spos + 7 ] ) << 8 ) );
2235 break;
2236 case SPREADSHEET_EXCEL_READER_TYPE_EOF:
2237 $cont = false;
2238 break;
2239 case SPREADSHEET_EXCEL_READER_TYPE_HYPER:
2240 // Only handle hyperlinks to a URL
2241 $row = ord( $this->data[ $spos ] ) | ord( $this->data[ $spos + 1 ] ) << 8;
2242 $row2 = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
2243 $column = ord( $this->data[ $spos + 4 ] ) | ord( $this->data[ $spos + 5 ] ) << 8;
2244 $column2 = ord( $this->data[ $spos + 6 ] ) | ord( $this->data[ $spos + 7 ] ) << 8;
2245 $linkdata = array();
2246 $flags = ord( $this->data[ $spos + 28 ] );
2247 $udesc = '';
2248 $ulink = '';
2249 $uloc = 32;
2250 $linkdata['flags'] = $flags;
2251 if ( ( $flags & 1 ) > 0 ) { // is a type we understand
2252 // is there a description ?
2253 if ( 0x14 === ( $flags & 0x14 ) ) { // has a description
2254 $uloc += 4;
2255 $descLen = ord( $this->data[ $spos + 32 ] ) | ord( $this->data[ $spos + 33 ] ) << 8;
2256 $udesc = substr( $this->data, $spos + $uloc, $descLen * 2 );
2257 $uloc += 2 * $descLen;
2258 }
2259 $ulink = $this->read16bitstring( $this->data, $spos + $uloc + 20 );
2260 if ( '' === $udesc ) {
2261 $udesc = $ulink;
2262 }
2263 }
2264 $linkdata['desc'] = $udesc;
2265 $linkdata['link'] = $this->_encodeUTF16( $ulink );
2266 for ( $r = $row; $r <= $row2; $r++ ) {
2267 for ( $c = $column; $c <= $column2; $c++ ) {
2268 $this->sheets[ $this->sn ]['cellsInfo'][ $r + 1 ][ $c + 1 ]['hyperlink'] = $linkdata;
2269 }
2270 }
2271 break;
2272 case SPREADSHEET_EXCEL_READER_TYPE_DEFCOLWIDTH:
2273 $this->defaultColWidth = ord( $data[ $spos + 4 ] ) | ord( $data[ $spos + 5 ] ) << 8;
2274 break;
2275 case SPREADSHEET_EXCEL_READER_TYPE_STANDARDWIDTH:
2276 $this->standardColWidth = ord( $data[ $spos + 4 ] ) | ord( $data[ $spos + 5 ] ) << 8;
2277 break;
2278 case SPREADSHEET_EXCEL_READER_TYPE_COLINFO:
2279 $colfrom = ord( $data[ $spos + 0 ] ) | ord( $data[ $spos + 1 ] ) << 8;
2280 $colto = ord( $data[ $spos + 2 ] ) | ord( $data[ $spos + 3 ] ) << 8;
2281 $cw = ord( $data[ $spos + 4 ] ) | ord( $data[ $spos + 5 ] ) << 8;
2282 $cxf = ord( $data[ $spos + 6 ] ) | ord( $data[ $spos + 7 ] ) << 8;
2283 $co = ord( $data[ $spos + 8 ] );
2284 for ( $coli = $colfrom; $coli <= $colto; $coli++ ) {
2285 $this->colInfo[ $this->sn ][ $coli + 1 ] = array(
2286 'width' => $cw,
2287 'xf' => $cxf,
2288 'hidden' => ( $co & 0x01 ),
2289 'collapsed' => ( $co & 0x1000 ) >> 12,
2290 );
2291 }
2292 break;
2293 default:
2294 break;
2295 } // switch
2296 $spos += $length;
2297 } // while
2298
2299 if ( ! isset( $this->sheets[ $this->sn ]['numRows'] ) ) {
2300 $this->sheets[ $this->sn ]['numRows'] = $this->sheets[ $this->sn ]['maxrow'];
2301 }
2302 if ( ! isset( $this->sheets[ $this->sn ]['numCols'] ) ) {
2303 $this->sheets[ $this->sn ]['numCols'] = $this->sheets[ $this->sn ]['maxcol'];
2304 }
2305 }
2306
2307 /**
2308 * [isDate description]
2309 *
2310 * @since 1.0.0
2311 *
2312 * @param [type] $spos [description]
2313 * @return bool [description]
2314 */
2315 protected function isDate( $spos ) {
2316 $xfindex = ord( $this->data[ $spos + 4 ] ) | ord( $this->data[ $spos + 5 ] ) << 8;
2317 return ( 'date' === $this->xfRecords[ $xfindex ]['type'] );
2318 }
2319 /**
2320 * [gmgetdate description]
2321 *
2322 * @since 1.0.0
2323 *
2324 * @param int $ts Optional. Timestamp. Defaults to current Unix timestamp.
2325 * @return array [description]
2326 */
2327 protected function gmgetdate( $ts = null ) {
2328 $k = array( 'seconds', 'minutes', 'hours', 'mday', 'wday', 'mon', 'year', 'yday', 'weekday', 'month', 0 );
2329 return array_combine( $k, explode( ':', gmdate( 's:i:G:j:w:n:Y:z:l:F:U', is_null( $ts ) ? time() : $ts ) ) );
2330 }
2331
2332 /**
2333 * Gets the details for a particular cell.
2334 *
2335 * @since 1.0.0
2336 *
2337 * @param [type] $spos [description]
2338 * @param [type] $numValue [description]
2339 * @param [type] $column [description]
2340 * @return [type] [description]
2341 */
2342 protected function _getCellDetails( $spos, $numValue, $column ) {
2343 $xfindex = ord( $this->data[ $spos + 4 ] ) | ord( $this->data[ $spos + 5 ] ) << 8;
2344 $xfrecord = $this->xfRecords[ $xfindex ];
2345 $type = $xfrecord['type'];
2346
2347 $format = $xfrecord['format'];
2348 $formatIndex = $xfrecord['formatIndex'];
2349 $fontIndex = $xfrecord['fontIndex'];
2350 $formatColor = '';
2351
2352 if ( isset( $this->_columnsFormat[ $column + 1 ] ) ) {
2353 $format = $this->_columnsFormat[ $column + 1 ];
2354 }
2355
2356 if ( 'date' === $type ) {
2357 // See https://groups.google.com/forum/#!topic/php-excel-reader-discuss/nD-XkNEtjhA
2358 $rectype = 'date';
2359 // Convert numeric value into a date
2360 $utcDays = floor( $numValue - ( $this->nineteenFour ? SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904 : SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS ) );
2361 $utcValue = ( $utcDays ) * SPREADSHEET_EXCEL_READER_MSINADAY;
2362 $dateinfo = $this->gmgetdate( $utcValue );
2363
2364 $raw = $numValue;
2365 $fractionalDay = $numValue - floor( $numValue ) + 0.0000001; // The 0.0000001 is to fix for php/excel fractional diffs
2366
2367 $totalseconds = floor( SPREADSHEET_EXCEL_READER_MSINADAY * $fractionalDay );
2368 $secs = $totalseconds % 60;
2369 $totalseconds -= $secs;
2370 $hours = (int) floor( $totalseconds / ( 60 * 60 ) );
2371 $mins = (int) floor( $totalseconds / 60 ) % 60;
2372 $string = date( $format, mktime( $hours, $mins, $secs, $dateinfo['mon'], $dateinfo['mday'], $dateinfo['year'] ) );
2373 } elseif ( 'number' === $type ) {
2374 $rectype = 'number';
2375 $formatted = $this->_format_value( $format, $numValue, $formatIndex );
2376 $string = $formatted['string'];
2377 $formatColor = $formatted['formatColor'];
2378 $raw = $numValue;
2379 } else {
2380 if ( '' === $format ) {
2381 $format = $this->_defaultFormat;
2382 }
2383 $rectype = 'unknown';
2384 $formatted = $this->_format_value( $format, $numValue, $formatIndex );
2385 $string = $formatted['string'];
2386 $formatColor = $formatted['formatColor'];
2387 $raw = $numValue;
2388 }
2389
2390 return array(
2391 'string' => $string,
2392 'raw' => $raw,
2393 'rectype' => $rectype,
2394 'format' => $format,
2395 'formatIndex' => $formatIndex,
2396 'fontIndex' => $fontIndex,
2397 'formatColor' => $formatColor,
2398 'xfIndex' => $xfindex,
2399 );
2400 }
2401
2402 /**
2403 * [createNumber description]
2404 *
2405 * @since 1.0.0
2406 *
2407 * @param [type] $spos [description]
2408 * @return [type] [description]
2409 */
2410 protected function createNumber( $spos ) {
2411 $rknumhigh = $this->_GetInt4d( $this->data, $spos + 10 );
2412 $rknumlow = $this->_GetInt4d( $this->data, $spos + 6 );
2413 $sign = ( $rknumhigh & 0x80000000 ) >> 31;
2414 $exp = ( $rknumhigh & 0x7ff00000 ) >> 20;
2415 $mantissa = ( 0x100000 | ( $rknumhigh & 0x000fffff ) );
2416 $mantissalow1 = ( $rknumlow & 0x80000000 ) >> 31;
2417 $mantissalow2 = ( $rknumlow & 0x7fffffff );
2418 $value = $mantissa / pow( 2, ( 20 - ( $exp - 1023 ) ) );
2419 if ( 0 !== $mantissalow1 ) {
2420 $value += 1 / pow( 2, ( 21 - ( $exp - 1023 ) ) );
2421 }
2422 $value += $mantissalow2 / pow( 2, ( 52 - ( $exp - 1023 ) ) );
2423 if ( $sign ) {
2424 $value *= -1;
2425 }
2426 return $value;
2427 }
2428
2429 /**
2430 * [addcell description]
2431 *
2432 * @since 1.0.0
2433 *
2434 * @param [type] $row [description]
2435 * @param [type] $col [description]
2436 * @param [type] $string [description]
2437 * @param array $info Optional. [description]
2438 * @return [type] [description]
2439 */
2440 protected function addcell( $row, $col, $string, $info = null ) {
2441 $this->sheets[ $this->sn ]['maxrow'] = max( $this->sheets[ $this->sn ]['maxrow'], $row + $this->_rowoffset );
2442 $this->sheets[ $this->sn ]['maxcol'] = max( $this->sheets[ $this->sn ]['maxcol'], $col + $this->_coloffset );
2443 $this->sheets[ $this->sn ]['cells'][ $row + $this->_rowoffset ][ $col + $this->_coloffset ] = $string;
2444 if ( $this->store_extended_info && $info ) {
2445 foreach ( $info as $key => $val ) {
2446 $this->sheets[ $this->sn ]['cellsInfo'][ $row + $this->_rowoffset ][ $col + $this->_coloffset ][ $key ] = $val;
2447 }
2448 }
2449 }
2450
2451 /**
2452 * [_GetIEEE754 description]
2453 *
2454 * @since 1.0.0
2455 *
2456 * @param [type] $rknum [description]
2457 * @return [type] [description]
2458 */
2459 protected function _GetIEEE754( $rknum ) {
2460 if ( 0 !== ( $rknum & 0x02 ) ) {
2461 $value = $rknum >> 2;
2462 } else {
2463 // info on IEEE754 encoding from
2464 // http://research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
2465 // The RK format calls for using only the most significant 30 bits of the
2466 // 64 bit floating point value. The other 34 bits are assumed to be 0
2467 // So, we use the upper 30 bits of $rknum as follows...
2468 $sign = ( $rknum & 0x80000000 ) >> 31;
2469 $exp = ( $rknum & 0x7ff00000 ) >> 20;
2470 $mantissa = ( 0x100000 | ( $rknum & 0x000ffffc ) );
2471 $value = $mantissa / pow( 2, ( 20 - ( $exp - 1023 ) ) );
2472 if ( $sign ) {
2473 $value *= -1;
2474 }
2475 }
2476 if ( 0 !== ( $rknum & 0x01 ) ) {
2477 $value /= 100;
2478 }
2479 return $value;
2480 }
2481
2482 /**
2483 * [_encodeUTF16 description]
2484 *
2485 * @since 1.0.0
2486 *
2487 * @param [type] $string [description]
2488 * @return [type] [description]
2489 */
2490 protected function _encodeUTF16( $string ) {
2491 if ( $this->_defaultEncoding ) {
2492 switch ( $this->_encoderFunction ) {
2493 case 'iconv':
2494 $string = iconv( 'UTF-16LE', $this->_defaultEncoding, $string );
2495 break;
2496 case 'mb_convert_encoding':
2497 $string = mb_convert_encoding( $string, $this->_defaultEncoding, 'UTF-16LE' );
2498 break;
2499 }
2500 }
2501 return $string;
2502 }
2503
2504 /**
2505 * [_GetInt4d description]
2506 *
2507 * @since 1.0.0
2508 *
2509 * @param [type] $data [description]
2510 * @param [type] $pos [description]
2511 * @return [type] [description]
2512 */
2513 protected function _GetInt4d( $data, $pos ) {
2514 $value = ord( $data[ $pos ] ) | ( ord( $data[ $pos + 1 ] ) << 8 ) | ( ord( $data[ $pos + 2 ] ) << 16 ) | ( ord( $data[ $pos + 3 ] ) << 24 );
2515 if ( $value >= 4294967294 ) {
2516 $value = -2;
2517 }
2518 return $value;
2519 }
2520
2521 /**
2522 * [v description]
2523 *
2524 * @since 1.0.0
2525 *
2526 * @param [type] $data [description]
2527 * @param [type] $pos [description]
2528 * @return [type] [description]
2529 */
2530 protected function v( $data, $pos ) {
2531 return ord( $data[ $pos ] ) | ord( $data[ $pos + 1 ] ) << 8;
2532 }
2533
2534 } // class Spreadsheet_Excel_Reader
2535