- Created front.js for handling frontend JavaScript functionalities. - Added index.php files for both the views and templates to manage redirection and access control. - Implemented configure.tpl for admin configuration settings with AJAX functionality for field retrieval. - Developed delete.tpl for product deletion with progress tracking. - Introduced file_upload.tpl for file upload handling with drag-and-drop support. - Created import.tpl for managing product imports with progress indicators. - Added list.tpl for displaying uploaded files with action links. - Implemented temp.tpl for queuing products with visual feedback. - Enhanced index.php for module access control. - Updated templates to include necessary JavaScript for AJAX operations and user interactions.
1224 lines
39 KiB
PHP
1224 lines
39 KiB
PHP
<?php
|
|
/*
|
|
* Example
|
|
|
|
echo '<pre>';
|
|
|
|
if ( $xls = SimpleXLS::parse('excel5book.xls')) {
|
|
print_r( $xls->rows() ); // dump first sheet
|
|
print_r( $xls->rows(1)); /// dump second sheet
|
|
} else {
|
|
echo SimpleXLSX::parseError();
|
|
}
|
|
|
|
echo '</pre>';
|
|
*/
|
|
|
|
/**
|
|
* A class for reading Microsoft Excel Spreadsheets.
|
|
*
|
|
* SimpleXLS version 2016-2020 packaged by Sergey Shuchkin <sergey.shuchkin@gmail.com> from
|
|
* Spreadsheet_Excel_Reader class developed by Vadim Tkachenko <vt@phpapache.com>
|
|
*/
|
|
class SimpleXLS {
|
|
const BIFF8 = 0x600;
|
|
const BIFF7 = 0x500;
|
|
const WORKBOOKGLOBALS = 0x5;
|
|
const WORKSHEET = 0x10;
|
|
|
|
//const TYPE_BOF = 0x809;
|
|
const TYPE_EOF = 0x0a;
|
|
const TYPE_BOUNDSHEET = 0x85;
|
|
const TYPE_DIMENSION = 0x200;
|
|
const TYPE_ROW = 0x208;
|
|
const TYPE_DBCELL = 0xd7;
|
|
const TYPE_FILEPASS = 0x2f;
|
|
//const TYPE_NOTE = 0x1c;
|
|
//const TYPE_TXO = 0x1b6;
|
|
const TYPE_RK = 0x7e;
|
|
const TYPE_RK2 = 0x27e;
|
|
const TYPE_MULRK = 0xbd;
|
|
const TYPE_MULBLANK = 0xbe;
|
|
//const TYPE_INDEX = 0x20b;
|
|
const TYPE_SST = 0xfc;
|
|
//const TYPE_EXTSST = 0xff;
|
|
//const TYPE_CONTINUE = 0x3c;
|
|
const TYPE_LABEL = 0x204;
|
|
const TYPE_LABELSST = 0xfd;
|
|
const TYPE_NUMBER = 0x203;
|
|
const TYPE_NAME = 0x18;
|
|
//const TYPE_ARRAY = 0x221;
|
|
//const TYPE_STRING = 0x207;
|
|
const TYPE_FORMULA = 0x406;
|
|
const TYPE_FORMULA2 = 0x6;
|
|
const TYPE_FORMAT = 0x41e;
|
|
const TYPE_XF = 0xe0;
|
|
const TYPE_BOOLERR = 0x205;
|
|
//const TYPE_UNKNOWN = 0xffff;
|
|
const TYPE_NINETEENFOUR = 0x22;
|
|
const TYPE_MERGEDCELLS = 0xE5;
|
|
|
|
//const DEF_NUM_FORMAT = "%.2f";
|
|
const DEF_NUM_FORMAT = '%s';
|
|
|
|
// OLE
|
|
const NUM_BIG_BLOCK_DEPOT_BLOCKS_POS = 0x2c;
|
|
const SMALL_BLOCK_DEPOT_BLOCK_POS = 0x3c;
|
|
const ROOT_START_BLOCK_POS = 0x30;
|
|
const BIG_BLOCK_SIZE = 0x200;
|
|
const SMALL_BLOCK_SIZE = 0x40;
|
|
const EXTENSION_BLOCK_POS = 0x44;
|
|
const NUM_EXTENSION_BLOCK_POS = 0x48;
|
|
const PROPERTY_STORAGE_BLOCK_SIZE = 0x80;
|
|
const BIG_BLOCK_DEPOT_BLOCKS_POS = 0x4c;
|
|
const SMALL_BLOCK_THRESHOLD = 0x1000;
|
|
// property storage offsets
|
|
const SIZE_OF_NAME_POS = 0x40;
|
|
const TYPE_POS = 0x42;
|
|
const START_BLOCK_POS = 0x74;
|
|
const SIZE_POS = 0x78;
|
|
/**
|
|
* Array of worksheets found
|
|
*
|
|
* @var array
|
|
* @access public
|
|
*/
|
|
public $boundsheets = array();
|
|
|
|
/**
|
|
* Array of format records found
|
|
*
|
|
* @var array
|
|
* @access public
|
|
*/
|
|
public $formatRecords = array();
|
|
|
|
/**
|
|
*
|
|
* @var array
|
|
* @access public
|
|
*/
|
|
public $sst = array();
|
|
|
|
/**
|
|
* Array of worksheets
|
|
*
|
|
* The data is stored in 'cells' and the meta-data is stored in an array
|
|
* called 'cellsInfo'
|
|
*
|
|
* Example:
|
|
*
|
|
* $sheets --> 'cells' --> row --> column --> Interpreted value
|
|
* --> 'cellsInfo' --> row --> column --> 'type' - Can be 'date', 'number', or 'unknown'
|
|
* --> 'raw' - The raw data that Excel stores for that data cell
|
|
*
|
|
* @var array
|
|
* @access public
|
|
*/
|
|
public $sheets = array();
|
|
/**
|
|
* List of default date formats used by Excel
|
|
*
|
|
* @var array
|
|
* @access public
|
|
*/
|
|
public $dateFormats = array(
|
|
0xe => 'd/m/Y',
|
|
0xf => 'd-M-Y',
|
|
0x10 => 'd-M',
|
|
0x11 => 'M-Y',
|
|
0x12 => 'h:i a',
|
|
0x13 => 'h:i:s a',
|
|
0x14 => 'H:i',
|
|
0x15 => 'H:i:s',
|
|
0x16 => 'd/m/Y H:i',
|
|
0x2d => 'i:s',
|
|
0x2e => 'H:i:s',
|
|
0x2f => 'i:s.S'
|
|
);
|
|
/**
|
|
* Default number formats used by Excel
|
|
*
|
|
* @var array
|
|
* @access public
|
|
*/
|
|
public $numberFormats = array(
|
|
0x1 => '%1.0f', // "0"
|
|
0x2 => '%1.2f', // "0.00",
|
|
0x3 => '%1.0f', //"#,##0",
|
|
0x4 => '%1.2f', //"#,##0.00",
|
|
0x5 => '%1.0f', /*"$#,##0;($#,##0)",*/
|
|
0x6 => '$%1.0f', /*"$#,##0;($#,##0)",*/
|
|
0x7 => '$%1.2f', //"$#,##0.00;($#,##0.00)",
|
|
0x8 => '$%1.2f', //"$#,##0.00;($#,##0.00)",
|
|
0x9 => '%1.0f%%', // "0%"
|
|
0xa => '%1.2f%%', // "0.00%"
|
|
0xb => '%1.2f', // 0.00E00",
|
|
0x25 => '%1.0f', // "#,##0;(#,##0)",
|
|
0x26 => '%1.0f', //"#,##0;(#,##0)",
|
|
0x27 => '%1.2f', //"#,##0.00;(#,##0.00)",
|
|
0x28 => '%1.2f', //"#,##0.00;(#,##0.00)",
|
|
0x29 => '%1.0f', //"#,##0;(#,##0)",
|
|
0x2a => '$%1.0f', //"$#,##0;($#,##0)",
|
|
0x2b => '%1.2f', //"#,##0.00;(#,##0.00)",
|
|
0x2c => '$%1.2f', //"$#,##0.00;($#,##0.00)",
|
|
0x30 => '%1.0f'
|
|
);
|
|
private $datetimeFormat = 'Y-m-d H:i:s';
|
|
/**
|
|
* Default encoding
|
|
*
|
|
* @var string
|
|
* @access private
|
|
*/
|
|
private $defaultEncoding = 'UTF-8';
|
|
/**
|
|
* Default number format
|
|
*
|
|
* @var integer
|
|
* @access private
|
|
*/
|
|
private $defaultFormat = self::DEF_NUM_FORMAT;
|
|
/**
|
|
* List of formats to use for each column
|
|
*
|
|
* @var array
|
|
* @access private
|
|
*/
|
|
private $columnsFormat = array();
|
|
/**
|
|
*
|
|
* @var integer
|
|
* @access private
|
|
*/
|
|
private $rowoffset = 1; //"##0.0E0";
|
|
|
|
// }}}
|
|
/**
|
|
*
|
|
* @var integer
|
|
* @access private
|
|
*/
|
|
private $colOffset = 1;
|
|
private $recType;
|
|
private $nineteenFour;
|
|
private $multiplier;
|
|
private $sn;
|
|
private $curFormat;
|
|
|
|
// OLERead
|
|
private $data;
|
|
private $numBigBlockDepotBlocks;
|
|
private $sbdStartBlock;
|
|
private $rootStartBlock;
|
|
private $extensionBlock;
|
|
private $numExtensionBlocks;
|
|
private $bigBlockChain;
|
|
private $smallBlockChain;
|
|
private $rootEntry;
|
|
private $entry;
|
|
private $props;
|
|
|
|
// sergey.shuchkin@gmail.com
|
|
private $wrkbook; // false - to use excel format
|
|
private $error = false;
|
|
private $debug;
|
|
|
|
// {{{ Spreadsheet_Excel_Reader()
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param string $filename XLS Filename or xls contents
|
|
* @param bool $isData If True then $filename is contents
|
|
* @param bool $debug Trigger PHP errors?
|
|
*/
|
|
public function __construct( $filename, $isData = false, $debug = false ) {
|
|
$this->debug = $debug;
|
|
$this->_oleread( $filename, $isData );
|
|
$this->_parse();
|
|
}
|
|
public static function parse( $filename, $isData = false, $debug = false ) {
|
|
$xlsx = new self( $filename, $isData, $debug );
|
|
if ( $xlsx->success() ) {
|
|
return $xlsx;
|
|
}
|
|
self::parseError( $xlsx->error() );
|
|
|
|
return false;
|
|
}
|
|
public static function parseError( $set = false ) {
|
|
static $error = false;
|
|
return $set ? $error = $set : $error;
|
|
}
|
|
public function error( $set = false ) {
|
|
if ( $set ) {
|
|
$this->error = $set;
|
|
if ( $this->debug ) {
|
|
trigger_error( $set );
|
|
}
|
|
}
|
|
|
|
return $this->error;
|
|
}
|
|
public function success() {
|
|
return ! $this->error;
|
|
}
|
|
public function rows( $sheetNum = 0 ) {
|
|
if ( $this->sheets[ $sheetNum ] ) {
|
|
$s = $this->sheets[ $sheetNum ];
|
|
$result = array();
|
|
for ( $i = 0; $i < $s['numRows']; $i ++ ) {
|
|
$r = array();
|
|
for ( $j = 0; $j < $s['numCols']; $j ++ ) {
|
|
$r[ $j ] = isset( $s['cells'][ $i + 1 ][ $j + 1 ] ) ? $s['cells'][ $i + 1 ][ $j + 1 ] : '';
|
|
}
|
|
$result[] = $r;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
public function setDateTimeFormat( $value ) {
|
|
$this->datetimeFormat = is_string( $value) ? $value : false;
|
|
}
|
|
|
|
// }}}
|
|
|
|
private function _oleread( $sFileName, $isData = false ) {
|
|
if ( $isData ) {
|
|
$this->data = $sFileName;
|
|
} else {
|
|
// check if file exist and is readable (Darko Miljanovic)
|
|
if ( ! is_readable( $sFileName ) ) {
|
|
$this->error( 'File not is readable ' . $sFileName );
|
|
return false;
|
|
}
|
|
|
|
$this->data = file_get_contents( $sFileName );
|
|
if ( ! $this->data ) {
|
|
$this->error( 'File reading error ' . $sFileName );
|
|
return false;
|
|
}
|
|
}
|
|
//echo IDENTIFIER_OLE;
|
|
//echo 'start';
|
|
if ( $this->_strpos( $this->data, pack( 'CCCCCCCC', 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 ) ) !== 0 ) {
|
|
$this->error( 'File is not XLS' );
|
|
|
|
return false;
|
|
}
|
|
|
|
$this->numBigBlockDepotBlocks = $this->_GetInt4d( $this->data, self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS );
|
|
$this->sbdStartBlock = $this->_GetInt4d( $this->data, self::SMALL_BLOCK_DEPOT_BLOCK_POS );
|
|
$this->rootStartBlock = $this->_GetInt4d( $this->data, self::ROOT_START_BLOCK_POS );
|
|
$this->extensionBlock = $this->_GetInt4d( $this->data, self::EXTENSION_BLOCK_POS );
|
|
$this->numExtensionBlocks = $this->_GetInt4d( $this->data, self::NUM_EXTENSION_BLOCK_POS );
|
|
|
|
/*
|
|
echo $this->numBigBlockDepotBlocks." ";
|
|
echo $this->sbdStartBlock." ";
|
|
echo $this->rootStartBlock." ";
|
|
echo $this->extensionBlock." ";
|
|
echo $this->numExtensionBlocks." ";
|
|
|
|
*/
|
|
//echo "sbdStartBlock = $this->sbdStartBlock\n";
|
|
$bigBlockDepotBlocks = array();
|
|
$pos = self::BIG_BLOCK_DEPOT_BLOCKS_POS;
|
|
// echo "pos = $pos";
|
|
$bbdBlocks = $this->numBigBlockDepotBlocks;
|
|
|
|
if ( $this->numExtensionBlocks !== 0 ) {
|
|
$bbdBlocks = ( self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS ) / 4;
|
|
}
|
|
|
|
for ( $i = 0; $i < $bbdBlocks; $i ++ ) {
|
|
$bigBlockDepotBlocks[ $i ] = $this->_GetInt4d( $this->data, $pos );
|
|
$pos += 4;
|
|
}
|
|
|
|
|
|
for ( $j = 0; $j < $this->numExtensionBlocks; $j ++ ) {
|
|
$pos = ( $this->extensionBlock + 1 ) * self::BIG_BLOCK_SIZE;
|
|
$blocksToRead = min( $this->numBigBlockDepotBlocks - $bbdBlocks, self::BIG_BLOCK_SIZE / 4 - 1 );
|
|
|
|
for ( $i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; $i ++ ) {
|
|
$bigBlockDepotBlocks[ $i ] = $this->_GetInt4d( $this->data, $pos );
|
|
$pos += 4;
|
|
}
|
|
|
|
$bbdBlocks += $blocksToRead;
|
|
if ( $bbdBlocks < $this->numBigBlockDepotBlocks ) {
|
|
$this->extensionBlock = $this->_GetInt4d( $this->data, $pos );
|
|
}
|
|
}
|
|
|
|
// var_dump($bigBlockDepotBlocks);
|
|
|
|
// readBigBlockDepot
|
|
|
|
$index = 0;
|
|
$this->bigBlockChain = array();
|
|
|
|
for ( $i = 0; $i < $this->numBigBlockDepotBlocks; $i ++ ) {
|
|
$pos = ( $bigBlockDepotBlocks[ $i ] + 1 ) * self::BIG_BLOCK_SIZE;
|
|
//echo "pos = $pos";
|
|
for ( $j = 0; $j < self::BIG_BLOCK_SIZE / 4; $j ++ ) {
|
|
$this->bigBlockChain[ $index ] = $this->_GetInt4d( $this->data, $pos );
|
|
$pos += 4;
|
|
$index ++;
|
|
}
|
|
}
|
|
|
|
//var_dump($this->bigBlockChain);
|
|
//echo '=====2';
|
|
// readSmallBlockDepot();
|
|
|
|
$index = 0;
|
|
$sbdBlock = $this->sbdStartBlock;
|
|
$this->smallBlockChain = array();
|
|
|
|
while ( $sbdBlock !== - 2 ) {
|
|
|
|
$pos = ( $sbdBlock + 1 ) * self::BIG_BLOCK_SIZE;
|
|
|
|
for ( $j = 0; $j < self::BIG_BLOCK_SIZE / 4; $j ++ ) {
|
|
$this->smallBlockChain[ $index ] = $this->_GetInt4d( $this->data, $pos );
|
|
$pos += 4;
|
|
$index ++;
|
|
}
|
|
|
|
$sbdBlock = $this->bigBlockChain[ $sbdBlock ];
|
|
}
|
|
|
|
|
|
// readData(rootStartBlock)
|
|
$block = $this->rootStartBlock;
|
|
|
|
$this->entry = $this->_readData( $block );
|
|
|
|
/*
|
|
while ($block != -2) {
|
|
$pos = ($block + 1) * self::BIG_BLOCK_SIZE;
|
|
$this->entry = $this->entry.$this->_substr($this->_data, $pos, self::BIG_BLOCK_SIZE);
|
|
$block = $this->bigBlockChain[$block];
|
|
}
|
|
*/
|
|
//echo '==='.$this->entry."===";
|
|
$this->_readPropertySets();
|
|
$this->data = $this->_readWorkBook();
|
|
|
|
return true;
|
|
}
|
|
|
|
// {{{ setOutputEncoding()
|
|
|
|
public function _GetInt4d( $data, $pos ) {
|
|
$value = ord( $data[ $pos ] ) | ( ord( $data[ $pos + 1 ] ) << 8 ) | ( ord( $data[ $pos + 2 ] ) << 16 ) | ( ord( $data[ $pos + 3 ] ) << 24 );
|
|
return ($value > 0x7FFFFFFF) ? $value - 0x100000000 : $value;
|
|
}
|
|
|
|
// }}}
|
|
|
|
// {{{ setRowColOffset()
|
|
|
|
public function _readData( $bl ) {
|
|
$block = $bl;
|
|
|
|
$data = '';
|
|
|
|
while ( $block !== - 2 ) {
|
|
$pos = ( $block + 1 ) * self::BIG_BLOCK_SIZE;
|
|
$data .= $this->_substr( $this->data, $pos, self::BIG_BLOCK_SIZE );
|
|
//echo "pos = $pos data=$data\n";
|
|
$block = $this->bigBlockChain[ $block ];
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
// }}}
|
|
// {{{ setDefaultFormat()
|
|
|
|
public function _readPropertySets() {
|
|
$offset = 0;
|
|
//var_dump($this->entry);
|
|
while ( $offset < $this->_strlen( $this->entry ) ) {
|
|
$d = $this->_substr( $this->entry, $offset, self::PROPERTY_STORAGE_BLOCK_SIZE );
|
|
|
|
$nameSize = ord( $d[ self::SIZE_OF_NAME_POS ] ) | ( ord( $d[ self::SIZE_OF_NAME_POS + 1 ] ) << 8 );
|
|
|
|
$type = ord( $d[ self::TYPE_POS ] );
|
|
//$maxBlock = $this->_strlen($d) / self::BIG_BLOCK_SIZE - 1;
|
|
|
|
$startBlock = $this->_GetInt4d( $d, self::START_BLOCK_POS );
|
|
$size = $this->_GetInt4d( $d, self::SIZE_POS );
|
|
|
|
$name = '';
|
|
for ( $i = 0; $i < $nameSize; $i ++ ) {
|
|
$name .= $d[ $i ];
|
|
}
|
|
|
|
$name = str_replace( "\x00", '', $name );
|
|
|
|
$this->props[] = array(
|
|
'name' => $name,
|
|
'type' => $type,
|
|
'startBlock' => $startBlock,
|
|
'size' => $size
|
|
);
|
|
|
|
if ( ( $name === 'Workbook' ) || ( $name === 'Book' ) ) {
|
|
$this->wrkbook = count( $this->props ) - 1;
|
|
}
|
|
|
|
if ( $name === 'Root Entry' ) {
|
|
$this->rootEntry = count( $this->props ) - 1;
|
|
}
|
|
|
|
//echo "name ==$name=\n";
|
|
|
|
|
|
$offset += self::PROPERTY_STORAGE_BLOCK_SIZE;
|
|
}
|
|
|
|
}
|
|
|
|
// }}}
|
|
// {{{ setColumnFormat()
|
|
|
|
private function _readWorkBook() {
|
|
if ( $this->props[ $this->wrkbook ]['size'] < self::SMALL_BLOCK_THRESHOLD ) {
|
|
// getSmallBlockStream(PropertyStorage ps)
|
|
|
|
$rootdata = $this->_readData( $this->props[ $this->rootEntry ]['startBlock'] );
|
|
|
|
$streamData = '';
|
|
$block = (int) $this->props[ $this->wrkbook ]['startBlock'];
|
|
//$count = 0;
|
|
while ( $block !== - 2 ) {
|
|
$pos = $block * self::SMALL_BLOCK_SIZE;
|
|
$streamData .= $this->_substr( $rootdata, $pos, self::SMALL_BLOCK_SIZE );
|
|
|
|
$block = $this->smallBlockChain[ $block ];
|
|
}
|
|
|
|
return $streamData;
|
|
|
|
|
|
}
|
|
|
|
$numBlocks = $this->props[ $this->wrkbook ]['size'] / self::BIG_BLOCK_SIZE;
|
|
if ( $this->props[ $this->wrkbook ]['size'] % self::BIG_BLOCK_SIZE !== 0 ) {
|
|
$numBlocks ++;
|
|
}
|
|
|
|
if ( $numBlocks === 0 ) {
|
|
return '';
|
|
}
|
|
|
|
//echo "numBlocks = $numBlocks\n";
|
|
//byte[] streamData = new byte[numBlocks * self::BIG_BLOCK_SIZE];
|
|
//print_r($this->wrkbook);
|
|
$streamData = '';
|
|
$block = $this->props[ $this->wrkbook ]['startBlock'];
|
|
|
|
//echo "block = $block";
|
|
while ( $block !== - 2 ) {
|
|
$pos = ( $block + 1 ) * self::BIG_BLOCK_SIZE;
|
|
$streamData .= $this->_substr( $this->data, $pos, self::BIG_BLOCK_SIZE );
|
|
$block = $this->bigBlockChain[ $block ];
|
|
}
|
|
|
|
//echo 'stream'.$streamData;
|
|
return $streamData;
|
|
}
|
|
|
|
|
|
// }}}
|
|
// {{{ _parse()
|
|
|
|
/**
|
|
* Parse a workbook
|
|
*
|
|
* @access private
|
|
* @return bool
|
|
*/
|
|
public function _parse() {
|
|
$pos = 0;
|
|
|
|
// $code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8;
|
|
$length = ord( $this->data[ $pos + 2 ] ) | ord( $this->data[ $pos + 3 ] ) << 8;
|
|
|
|
$version = ord( $this->data[ $pos + 4 ] ) | ord( $this->data[ $pos + 5 ] ) << 8;
|
|
$substreamType = ord( $this->data[ $pos + 6 ] ) | ord( $this->data[ $pos + 7 ] ) << 8;
|
|
// echo "Start parse code=".base_convert($code,10,16)." version=".base_convert($version,10,16)." substreamType=".base_convert($substreamType,10,16).""."\n";
|
|
|
|
// die();
|
|
|
|
if ( ( $version !== self::BIFF8 ) &&
|
|
( $version !== self::BIFF7 )
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
if ( $substreamType !== self::WORKBOOKGLOBALS ) {
|
|
return false;
|
|
}
|
|
|
|
//print_r($rec);
|
|
$pos += $length + 4;
|
|
|
|
$code = ord( $this->data[ $pos ] ) | ord( $this->data[ $pos + 1 ] ) << 8;
|
|
$length = ord( $this->data[ $pos + 2 ] ) | ord( $this->data[ $pos + 3 ] ) << 8;
|
|
|
|
while ( $code !== self::TYPE_EOF ) {
|
|
switch ( $code ) {
|
|
case self::TYPE_SST:
|
|
//echo "Type_SST\n";
|
|
$formattingRuns = 0;
|
|
$extendedRunLength = 0;
|
|
$spos = $pos + 4;
|
|
$limitpos = $spos + $length;
|
|
$uniqueStrings = $this->_GetInt4d( $this->data, $spos + 4 );
|
|
$spos += 8;
|
|
for ( $i = 0; $i < $uniqueStrings; $i ++ ) {
|
|
// Read in the number of characters
|
|
if ( $spos === $limitpos ) {
|
|
$opcode = ord( $this->data[ $spos ] ) | ord( $this->data[ $spos + 1 ] ) << 8;
|
|
$conlength = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
|
|
if ( $opcode !== 0x3c ) {
|
|
return - 1;
|
|
}
|
|
$spos += 4;
|
|
$limitpos = $spos + $conlength;
|
|
}
|
|
$numChars = ord( $this->data[ $spos ] ) | ( ord( $this->data[ $spos + 1 ] ) << 8 );
|
|
//echo "i = $i pos = $pos numChars = $numChars ";
|
|
$spos += 2;
|
|
$optionFlags = ord( $this->data[ $spos ] );
|
|
$spos ++;
|
|
$asciiEncoding = ( ( $optionFlags & 0x01 ) === 0 );
|
|
$extendedString = ( ( $optionFlags & 0x04 ) !== 0 );
|
|
|
|
// See if string contains formatting information
|
|
$richString = ( ( $optionFlags & 0x08 ) !== 0 );
|
|
|
|
if ( $richString ) {
|
|
// Read in the crun
|
|
$formattingRuns = ord( $this->data[ $spos ] ) | ( ord( $this->data[ $spos + 1 ] ) << 8 );
|
|
$spos += 2;
|
|
}
|
|
|
|
if ( $extendedString ) {
|
|
// Read in cchExtRst
|
|
$extendedRunLength = $this->_GetInt4d( $this->data, $spos );
|
|
$spos += 4;
|
|
}
|
|
|
|
$len = $asciiEncoding ? $numChars : $numChars * 2;
|
|
if ( $spos + $len < $limitpos ) {
|
|
$retstr = $this->_substr( $this->data, $spos, $len );
|
|
$spos += $len;
|
|
} else {
|
|
// found countinue
|
|
$retstr = $this->_substr( $this->data, $spos, $limitpos - $spos );
|
|
$bytesRead = $limitpos - $spos;
|
|
$charsLeft = $numChars - ( $asciiEncoding ? $bytesRead : ( $bytesRead / 2 ) );
|
|
$spos = $limitpos;
|
|
|
|
while ( $charsLeft > 0 ) {
|
|
$opcode = ord( $this->data[ $spos ] ) | ord( $this->data[ $spos + 1 ] ) << 8;
|
|
$conlength = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
|
|
if ( $opcode !== 0x3c ) {
|
|
return - 1;
|
|
}
|
|
$spos += 4;
|
|
$limitpos = $spos + $conlength;
|
|
$option = ord( $this->data[ $spos ] );
|
|
$spos ++;
|
|
if ( $asciiEncoding && ( $option === 0 ) ) {
|
|
$len = min( $charsLeft, $limitpos - $spos ); // min($charsLeft, $conlength);
|
|
$retstr .= $this->_substr( $this->data, $spos, $len );
|
|
$charsLeft -= $len;
|
|
$asciiEncoding = true;
|
|
} elseif ( ! $asciiEncoding && ( $option !== 0 ) ) {
|
|
$len = min( $charsLeft * 2, $limitpos - $spos ); // min($charsLeft, $conlength);
|
|
$retstr .= $this->_substr( $this->data, $spos, $len );
|
|
$charsLeft -= $len / 2;
|
|
$asciiEncoding = false;
|
|
} elseif ( ! $asciiEncoding && ( $option === 0 ) ) {
|
|
// Bummer - the string starts off as Unicode, but after the
|
|
// continuation it is in straightforward ASCII encoding
|
|
$len = min( $charsLeft, $limitpos - $spos ); // min($charsLeft, $conlength);
|
|
for ( $j = 0; $j < $len; $j ++ ) {
|
|
$retstr .= $this->data[ $spos + $j ] . chr( 0 );
|
|
}
|
|
$charsLeft -= $len;
|
|
$asciiEncoding = false;
|
|
} else {
|
|
$newstr = '';
|
|
for ( $j = 0, $len_retstr = $this->_strlen( $retstr ); $j < $len_retstr; $j ++ ) {
|
|
$newstr = $retstr[ $j ] . chr( 0 );
|
|
}
|
|
$retstr = $newstr;
|
|
$len = min( $charsLeft * 2, $limitpos - $spos ); // min($charsLeft, $conlength);
|
|
$retstr .= $this->_substr( $this->data, $spos, $len );
|
|
$charsLeft -= $len / 2;
|
|
$asciiEncoding = false;
|
|
//echo "Izavrat\n";
|
|
}
|
|
$spos += $len;
|
|
|
|
}
|
|
}
|
|
$retstr = $asciiEncoding ? $retstr : $this->_encodeUTF16( $retstr );
|
|
// echo "Str $i = $retstr\n";
|
|
if ( $richString ) {
|
|
$spos += 4 * $formattingRuns;
|
|
}
|
|
|
|
// For extended strings, skip over the extended string data
|
|
if ( $extendedString ) {
|
|
$spos += $extendedRunLength;
|
|
}
|
|
//if ($retstr == 'Derby'){
|
|
// echo "bb\n";
|
|
//}
|
|
$this->sst[] = $retstr;
|
|
}
|
|
/*$continueRecords = array();
|
|
while ($this->getNextCode() == Type_CONTINUE) {
|
|
$continueRecords[] = &$this->nextRecord();
|
|
}
|
|
//echo " 1 Type_SST\n";
|
|
$this->shareStrings = new SSTRecord($r, $continueRecords);
|
|
//print_r($this->shareStrings->strings);
|
|
*/
|
|
// echo 'SST read: '.($time_end-$time_start)."\n";
|
|
break;
|
|
|
|
case self::TYPE_FILEPASS:
|
|
return false;
|
|
break;
|
|
case self::TYPE_NAME:
|
|
//echo "Type_NAME\n";
|
|
break;
|
|
case self::TYPE_FORMAT:
|
|
$indexCode = ord( $this->data[ $pos + 4 ] ) | ord( $this->data[ $pos + 5 ] ) << 8;
|
|
|
|
if ( $version === self::BIFF8 ) {
|
|
$numchars = ord( $this->data[ $pos + 6 ] ) | ord( $this->data[ $pos + 7 ] ) << 8;
|
|
if ( ord( $this->data[ $pos + 8 ] ) === 0 ) {
|
|
$formatString = $this->_substr( $this->data, $pos + 9, $numchars );
|
|
} else {
|
|
$formatString = $this->_substr( $this->data, $pos + 9, $numchars * 2 );
|
|
}
|
|
} else {
|
|
$numchars = ord( $this->data[ $pos + 6 ] );
|
|
$formatString = $this->_substr( $this->data, $pos + 7, $numchars * 2 );
|
|
}
|
|
|
|
$this->formatRecords[ $indexCode ] = $formatString;
|
|
// echo "Type.FORMAT\n";
|
|
break;
|
|
case self::TYPE_XF:
|
|
$formatstr = '';
|
|
//global $dateFormats, $numberFormats;
|
|
$indexCode = ord( $this->data[ $pos + 6 ] ) | ord( $this->data[ $pos + 7 ] ) << 8;
|
|
//echo "\nType.XF ".count($this->formatRecords['xfrecords'])." $indexCode ";
|
|
if ( array_key_exists( $indexCode, $this->dateFormats ) ) {
|
|
//echo "isdate ".$dateFormats[$indexCode];
|
|
$this->formatRecords['xfrecords'][] = array(
|
|
'type' => 'date',
|
|
'format' => $this->dateFormats[ $indexCode ]
|
|
);
|
|
} elseif ( array_key_exists( $indexCode, $this->numberFormats ) ) {
|
|
//echo "isnumber ".$this->numberFormats[$indexCode];
|
|
$this->formatRecords['xfrecords'][] = array(
|
|
'type' => 'number',
|
|
'format' => $this->numberFormats[ $indexCode ]
|
|
);
|
|
} else {
|
|
$isdate = false;
|
|
if ( $indexCode > 0 ) {
|
|
if ( isset( $this->formatRecords[ $indexCode ] ) ) {
|
|
$formatstr = $this->formatRecords[ $indexCode ];
|
|
}
|
|
//echo '.other.';
|
|
// echo "\ndate-time=$formatstr=\n";
|
|
if ( $formatstr && preg_match( "/^[hmsday\/\-:\s]+$/i", $formatstr ) === 1 ) { // found day and time format
|
|
$isdate = true;
|
|
$formatstr = str_replace( array( 'mm', 'h' ), array( 'i', 'H' ), $formatstr );
|
|
//echo "\ndate-time $formatstr \n";
|
|
}
|
|
}
|
|
|
|
if ( $isdate ) {
|
|
$this->formatRecords['xfrecords'][] = array(
|
|
'type' => 'date',
|
|
'format' => $formatstr,
|
|
);
|
|
} else {
|
|
$this->formatRecords['xfrecords'][] = array(
|
|
'type' => 'other',
|
|
'format' => '',
|
|
'code' => $indexCode
|
|
);
|
|
}
|
|
}
|
|
//echo "\n";
|
|
break;
|
|
case self::TYPE_NINETEENFOUR:
|
|
//echo "Type.NINETEENFOUR\n";
|
|
$this->nineteenFour = ( ord( $this->data[ $pos + 4 ] ) === 1 );
|
|
break;
|
|
case self::TYPE_BOUNDSHEET:
|
|
//echo "Type.BOUNDSHEET\n";
|
|
$rec_offset = $this->_GetInt4d( $this->data, $pos + 4 );
|
|
// $rec_typeFlag = ord($this->_data[$pos + 8]);
|
|
// $rec_visibilityFlag = ord($this->_data[$pos + 9]);
|
|
$rec_length = ord( $this->data[ $pos + 10 ] );
|
|
$rec_name = '';
|
|
if ( $version === self::BIFF8 ) {
|
|
$chartype = ord( $this->data[ $pos + 11 ] );
|
|
if ( $chartype === 0 ) {
|
|
$rec_name = $this->_substr( $this->data, $pos + 12, $rec_length );
|
|
} else {
|
|
$rec_name = $this->_encodeUTF16( $this->_substr( $this->data, $pos + 12, $rec_length * 2 ) );
|
|
}
|
|
} elseif ( $version === self::BIFF7 ) {
|
|
$rec_name = $this->_substr( $this->data, $pos + 11, $rec_length );
|
|
}
|
|
$this->boundsheets[] = array(
|
|
'name' => $rec_name,
|
|
'offset' => $rec_offset
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
//echo "Code = ".base_convert($r['code'],10,16)."\n";
|
|
$pos += $length + 4;
|
|
$code = ord( $this->data[ $pos ] ) | ord( $this->data[ $pos + 1 ] ) << 8;
|
|
$length = ord( $this->data[ $pos + 2 ] ) | ord( $this->data[ $pos + 3 ] ) << 8;
|
|
|
|
//$r = &$this->nextRecord();
|
|
//echo "1 Code = ".base_convert($r['code'],10,16)."\n";
|
|
}
|
|
|
|
foreach ( $this->boundsheets as $key => $val ) {
|
|
$this->sn = $key;
|
|
$this->_parsesheet( $val['offset'] );
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
public function _encodeUTF16( $string ) {
|
|
$result = $string;
|
|
if ( $this->defaultEncoding ) {
|
|
$result = mb_convert_encoding( $string, $this->defaultEncoding, 'UTF-16LE' );
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
public function _parsesheet( $spos ) {
|
|
$cont = true;
|
|
// read BOF
|
|
// $code = ord($this->_data[$spos]) | ord($this->_data[$spos + 1]) << 8;
|
|
$length = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
|
|
|
|
$version = ord( $this->data[ $spos + 4 ] ) | ord( $this->data[ $spos + 5 ] ) << 8;
|
|
$substreamType = ord( $this->data[ $spos + 6 ] ) | ord( $this->data[ $spos + 7 ] ) << 8;
|
|
|
|
if ( ( $version !== self::BIFF8 ) && ( $version !== self::BIFF7 ) ) {
|
|
return - 1;
|
|
}
|
|
|
|
if ( $substreamType !== self::WORKSHEET ) {
|
|
return - 2;
|
|
}
|
|
//echo "Start parse code=".base_convert($code,10,16)." version=".base_convert($version,10,16)." substreamType=".base_convert($substreamType,10,16).""."\n";
|
|
$spos += $length + 4;
|
|
//var_dump($this->formatRecords);
|
|
//echo "code $code $length";
|
|
while ( $cont ) {
|
|
//echo "mem= ".memory_get_usage()."\n";
|
|
// $r = &$this->file->nextRecord();
|
|
$lowcode = ord( $this->data[ $spos ] );
|
|
if ( $lowcode === self::TYPE_EOF ) {
|
|
break;
|
|
}
|
|
$code = $lowcode | ord( $this->data[ $spos + 1 ] ) << 8;
|
|
$length = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
|
|
$spos += 4;
|
|
$this->sheets[ $this->sn ]['maxrow'] = $this->rowoffset - 1;
|
|
$this->sheets[ $this->sn ]['maxcol'] = $this->colOffset - 1;
|
|
//echo "Code=".base_convert($code,10,16)." $code\n";
|
|
unset( $this->recType );
|
|
$this->multiplier = 1; // need for format with %
|
|
switch ( $code ) {
|
|
case self::TYPE_DIMENSION:
|
|
//echo 'Type_DIMENSION ';
|
|
if ( ! isset( $this->numRows ) ) {
|
|
if ( ( $length === 10 ) || ( $version === self::BIFF7 ) ) {
|
|
$this->sheets[ $this->sn ]['numRows'] = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
|
|
$this->sheets[ $this->sn ]['numCols'] = ord( $this->data[ $spos + 6 ] ) | ord( $this->data[ $spos + 7 ] ) << 8;
|
|
} else {
|
|
$this->sheets[ $this->sn ]['numRows'] = ord( $this->data[ $spos + 4 ] ) | ord( $this->data[ $spos + 5 ] ) << 8;
|
|
$this->sheets[ $this->sn ]['numCols'] = ord( $this->data[ $spos + 10 ] ) | ord( $this->data[ $spos + 11 ] ) << 8;
|
|
}
|
|
}
|
|
//echo 'numRows '.$this->numRows.' '.$this->numCols."\n";
|
|
break;
|
|
case self::TYPE_MERGEDCELLS:
|
|
$cellRanges = ord( $this->data[ $spos ] ) | ord( $this->data[ $spos + 1 ] ) << 8;
|
|
for ( $i = 0; $i < $cellRanges; $i ++ ) {
|
|
$fr = ord( $this->data[ $spos + 8 * $i + 2 ] ) | ord( $this->data[ $spos + 8 * $i + 3 ] ) << 8;
|
|
$lr = ord( $this->data[ $spos + 8 * $i + 4 ] ) | ord( $this->data[ $spos + 8 * $i + 5 ] ) << 8;
|
|
$fc = ord( $this->data[ $spos + 8 * $i + 6 ] ) | ord( $this->data[ $spos + 8 * $i + 7 ] ) << 8;
|
|
$lc = ord( $this->data[ $spos + 8 * $i + 8 ] ) | ord( $this->data[ $spos + 8 * $i + 9 ] ) << 8;
|
|
//$this->sheets[$this->sn]['mergedCells'][] = array($fr + 1, $fc + 1, $lr + 1, $lc + 1);
|
|
if ( $lr - $fr > 0 ) {
|
|
$this->sheets[ $this->sn ]['cellsInfo'][ $fr + 1 ][ $fc + 1 ]['rowspan'] = $lr - $fr + 1;
|
|
}
|
|
if ( $lc - $fc > 0 ) {
|
|
$this->sheets[ $this->sn ]['cellsInfo'][ $fr + 1 ][ $fc + 1 ]['colspan'] = $lc - $fc + 1;
|
|
}
|
|
}
|
|
//echo "Merged Cells $cellRanges $lr $fr $lc $fc\n";
|
|
break;
|
|
case self::TYPE_RK:
|
|
case self::TYPE_RK2:
|
|
//echo 'self::TYPE_RK'."\n";
|
|
$row = ord( $this->data[ $spos ] ) | ord( $this->data[ $spos + 1 ] ) << 8;
|
|
$column = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
|
|
$rknum = $this->_GetInt4d( $this->data, $spos + 6 );
|
|
$numValue = $this->_GetIEEE754( $rknum );
|
|
//echo $numValue." ";
|
|
if ( $this->isDate( $spos ) ) {
|
|
list( $string, $raw ) = $this->createDate( $numValue );
|
|
} else {
|
|
$raw = $numValue;
|
|
if ( isset( $this->columnsFormat[ $column + 1 ] ) ) {
|
|
$this->curFormat = $this->columnsFormat[ $column + 1 ];
|
|
}
|
|
$string = sprintf( $this->curFormat, $numValue * $this->multiplier );
|
|
//$this->addcell(RKRecord($r));
|
|
}
|
|
$this->addcell( $row, $column, $string, $raw );
|
|
//echo "Type_RK $row $column $string $raw {$this->curformat}\n";
|
|
break;
|
|
case self::TYPE_LABELSST:
|
|
$row = ord( $this->data[ $spos ] ) | ord( $this->data[ $spos + 1 ] ) << 8;
|
|
$column = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
|
|
// $xfindex = ord($this->_data[$spos + 4]) | ord($this->_data[$spos + 5]) << 8;
|
|
$index = $this->_GetInt4d( $this->data, $spos + 6 );
|
|
//var_dump($this->sst);
|
|
$this->addcell( $row, $column, $this->sst[ $index ] );
|
|
//echo "LabelSST $row $column $string\n";
|
|
break;
|
|
case self::TYPE_MULRK:
|
|
$row = ord( $this->data[ $spos ] ) | ord( $this->data[ $spos + 1 ] ) << 8;
|
|
$colFirst = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
|
|
$colLast = ord( $this->data[ $spos + $length - 2 ] ) | ord( $this->data[ $spos + $length - 1 ] ) << 8;
|
|
$columns = $colLast - $colFirst + 1;
|
|
$tmppos = $spos + 4;
|
|
for ( $i = 0; $i < $columns; $i ++ ) {
|
|
$numValue = $this->_GetIEEE754( $this->_GetInt4d( $this->data, $tmppos + 2 ) );
|
|
if ( $this->isDate( $tmppos - 4 ) ) {
|
|
list( $string, $raw ) = $this->createDate( $numValue );
|
|
} else {
|
|
$raw = $numValue;
|
|
if ( isset( $this->columnsFormat[ $colFirst + $i + 1 ] ) ) {
|
|
$this->curFormat = $this->columnsFormat[ $colFirst + $i + 1 ];
|
|
}
|
|
$string = sprintf( $this->curFormat, $numValue * $this->multiplier );
|
|
}
|
|
//$rec['rknumbers'][$i]['xfindex'] = ord($rec['data'][$pos]) | ord($rec['data'][$pos+1]) << 8;
|
|
$tmppos += 6;
|
|
$this->addcell( $row, $colFirst + $i, $string, $raw );
|
|
//echo "MULRK $row ".($colFirst + $i)." $string\n";
|
|
}
|
|
//MulRKRecord($r);
|
|
// Get the individual cell records from the multiple record
|
|
//$num = ;
|
|
|
|
break;
|
|
case self::TYPE_NUMBER:
|
|
$row = ord( $this->data[ $spos ] ) | ord( $this->data[ $spos + 1 ] ) << 8;
|
|
$column = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
|
|
$tmp = unpack( 'ddouble', $this->_substr( $this->data, $spos + 6, 8 ) ); // It machine machine dependent
|
|
if ( $this->isDate( $spos ) ) {
|
|
list( $string, $raw ) = $this->createDate( $tmp['double'] );
|
|
// $this->addcell(DateRecord($r, 1));
|
|
} else {
|
|
//$raw = $tmp[''];
|
|
if ( isset( $this->columnsFormat[ $column + 1 ] ) ) {
|
|
$this->curFormat = $this->columnsFormat[ $column + 1 ];
|
|
}
|
|
$raw = $this->createNumber( $spos );
|
|
$string = sprintf( $this->curFormat, $raw * $this->multiplier );
|
|
|
|
// $this->addcell(NumberRecord($r));
|
|
}
|
|
$this->addcell( $row, $column, $string, $raw );
|
|
//echo "Number $row $column $string\n";
|
|
break;
|
|
case self::TYPE_FORMULA:
|
|
case self::TYPE_FORMULA2:
|
|
$row = ord( $this->data[ $spos ] ) | ord( $this->data[ $spos + 1 ] ) << 8;
|
|
$column = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
|
|
/*
|
|
$byte6 = ord($this->_data[$spos + 6]);
|
|
$byte12 = ord($this->_data[$spos + 12]);
|
|
$byte13 = ord($this->_data[$spos + 13]);
|
|
|
|
if ( $byte6 === 0 && $byte12 === 255 && $byte13 === 255 ) {
|
|
//String formula. Result follows in a STRING record
|
|
//echo "FORMULA $row $column Formula with a string<br>\n";
|
|
} else if ($byte6 === 1 && $byte12 === 255 && $byte13 === 255 ) {
|
|
//Boolean formula. Result is in +2; 0=false,1=true
|
|
} else if ($byte6 === 2 && $byte12 === 255 && $byte13 === 255) {
|
|
//Error formula. Error code is in +2;
|
|
} else if ( $byte6 === 3 && $byte12 === 255 && $byte13 === 255) {
|
|
//Formula result is a null string.
|
|
*/
|
|
if ( ! ( ord( $this->data[ $spos + 6 ] ) < 4 && ord( $this->data[ $spos + 12 ] ) === 255 && ord( $this->data[ $spos + 13 ] ) === 255 ) ) {
|
|
// result is a number, so first 14 bytes are just like a _NUMBER record
|
|
$tmp = unpack( 'ddouble', $this->_substr( $this->data, $spos + 6, 8 ) ); // It machine machine dependent
|
|
if ( $this->isDate( $spos ) ) {
|
|
list( $string, $raw ) = $this->createDate( $tmp['double'] );
|
|
// $this->addcell(DateRecord($r, 1));
|
|
} else {
|
|
//$raw = $tmp[''];
|
|
if ( isset( $this->columnsFormat[ $column + 1 ] ) ) {
|
|
$this->curFormat = $this->columnsFormat[ $column + 1 ];
|
|
}
|
|
$raw = $this->createNumber( $spos );
|
|
$string = sprintf( $this->curFormat, $raw * $this->multiplier );
|
|
|
|
// $this->addcell(NumberRecord($r));
|
|
}
|
|
$this->addcell( $row, $column, $string, $raw );
|
|
//echo "Number $row $column $string\n";
|
|
}
|
|
break;
|
|
case self::TYPE_BOOLERR:
|
|
$row = ord( $this->data[ $spos ] ) | ord( $this->data[ $spos + 1 ] ) << 8;
|
|
$column = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
|
|
$string = ord( $this->data[ $spos + 6 ] );
|
|
$this->addcell( $row, $column, $string );
|
|
//echo 'Type_BOOLERR '."\n";
|
|
break;
|
|
case self::TYPE_ROW:
|
|
case self::TYPE_DBCELL:
|
|
case self::TYPE_MULBLANK:
|
|
break;
|
|
case self::TYPE_LABEL:
|
|
$row = ord( $this->data[ $spos ] ) | ord( $this->data[ $spos + 1 ] ) << 8;
|
|
$column = ord( $this->data[ $spos + 2 ] ) | ord( $this->data[ $spos + 3 ] ) << 8;
|
|
$this->addcell( $row, $column, $this->_substr( $this->data, $spos + 8, ord( $this->data[ $spos + 6 ] ) | ord( $this->data[ $spos + 7 ] ) << 8 ) );
|
|
|
|
// $this->addcell(LabelRecord($r));
|
|
break;
|
|
|
|
case self::TYPE_EOF:
|
|
$cont = false;
|
|
break;
|
|
default:
|
|
//echo ' unknown :'.base_convert($r['code'],10,16)."\n";
|
|
break;
|
|
|
|
}
|
|
$spos += $length;
|
|
}
|
|
|
|
if ( ! isset( $this->sheets[ $this->sn ]['numRows'] ) ) {
|
|
$this->sheets[ $this->sn ]['numRows'] = $this->sheets[ $this->sn ]['maxrow'];
|
|
}
|
|
if ( ! isset( $this->sheets[ $this->sn ]['numCols'] ) ) {
|
|
$this->sheets[ $this->sn ]['numCols'] = $this->sheets[ $this->sn ]['maxcol'];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//}}}
|
|
//{{{ createDate()
|
|
|
|
public function _GetIEEE754( $rknum ) {
|
|
if ( ( $rknum & 0x02 ) !== 0 ) {
|
|
$value = $rknum >> 2;
|
|
} else {
|
|
//mmp
|
|
// first comment out the previously existing 7 lines of code here
|
|
// $tmp = unpack("d", pack("VV", 0, ($rknum & 0xfffffffc)));
|
|
// //$value = $tmp[''];
|
|
// if (array_key_exists(1, $tmp)) {
|
|
// $value = $tmp[1];
|
|
// } else {
|
|
// $value = $tmp[''];
|
|
// }
|
|
// I got my info on IEEE754 encoding from
|
|
// http://research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
|
|
// The RK format calls for using only the most significant 30 bits of the
|
|
// 64 bit floating point value. The other 34 bits are assumed to be 0
|
|
// So, we use the upper 30 bits of $rknum as follows...
|
|
$sign = ( $rknum & 0x80000000 ) >> 31;
|
|
$exp = ( $rknum & 0x7ff00000 ) >> 20;
|
|
$mantissa = ( 0x100000 | ( $rknum & 0x000ffffc ) );
|
|
$value = $mantissa / pow( 2, 20 - ( $exp - 1023 ) );
|
|
if ( $sign ) {
|
|
$value = - 1 * $value;
|
|
}
|
|
//end of changes by mmp
|
|
|
|
}
|
|
|
|
if ( ( $rknum & 0x01 ) !== 0 ) {
|
|
$value /= 100;
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
public function isDate( $spos ) {
|
|
//$xfindex = GetInt2d(, 4);
|
|
$xfindex = ord( $this->data[ $spos + 4 ] ) | ord( $this->data[ $spos + 5 ] ) << 8;
|
|
//echo 'check is date '.$xfindex.' '.$this->formatRecords['xfrecords'][$xfindex]['type']."\n";
|
|
//var_dump($this->formatRecords['xfrecords'][$xfindex]);
|
|
if ( $this->formatRecords['xfrecords'][ $xfindex ]['type'] === 'date' ) {
|
|
$this->curFormat = $this->formatRecords['xfrecords'][ $xfindex ]['format'];
|
|
$this->recType = 'date';
|
|
|
|
return true;
|
|
}
|
|
|
|
if ( $this->formatRecords['xfrecords'][ $xfindex ]['type'] === 'number' ) {
|
|
$this->curFormat = $this->formatRecords['xfrecords'][ $xfindex ]['format'];
|
|
$this->recType = 'number';
|
|
if ( ( $xfindex === 0x9 ) || ( $xfindex === 0xa ) ) {
|
|
$this->multiplier = 100;
|
|
}
|
|
} else {
|
|
$this->curFormat = $this->defaultFormat;
|
|
$this->recType = 'unknown';
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Convert the raw Excel date into a human readable format
|
|
*
|
|
* Dates in Excel are stored as number of seconds from an epoch. On
|
|
* Windows, the epoch is 30/12/1899 and on Mac it's 01/01/1904
|
|
*
|
|
* @param integer $timevalue The raw Excel value to convert
|
|
*
|
|
* @return array First element is the converted date, the second element is number a unix timestamp
|
|
*/
|
|
public function createDate( $timevalue ) {
|
|
// $offset = ($timeoffset===null)? date('Z') : $timeoffset * 3600;
|
|
if ($timevalue > 1) {
|
|
$timevalue -= ( $this->nineteenFour ? 24107 : 25569 );
|
|
}
|
|
$ts = round($timevalue * 24 * 3600);
|
|
$string = $this->datetimeFormat ? gmdate( $this->datetimeFormat, $ts ) : gmdate( $this->curFormat, $ts );
|
|
return array( $string, $ts );
|
|
|
|
}
|
|
|
|
public function addcell( $row, $col, $string, $raw = '' ) {
|
|
//echo "ADD cel $row-$col $string\n";
|
|
$this->sheets[ $this->sn ]['maxrow'] = max( $this->sheets[ $this->sn ]['maxrow'], $row + $this->rowoffset );
|
|
$this->sheets[ $this->sn ]['maxcol'] = max( $this->sheets[ $this->sn ]['maxcol'], $col + $this->colOffset );
|
|
$this->sheets[ $this->sn ]['cells'][ $row + $this->rowoffset ][ $col + $this->colOffset ] = $string;
|
|
if ( $raw ) {
|
|
$this->sheets[ $this->sn ]['cellsInfo'][ $row + $this->rowoffset ][ $col + $this->colOffset ]['raw'] = $raw;
|
|
}
|
|
if ( isset( $this->recType ) ) {
|
|
$this->sheets[ $this->sn ]['cellsInfo'][ $row + $this->rowoffset ][ $col + $this->colOffset ]['type'] = $this->recType;
|
|
}
|
|
|
|
}
|
|
|
|
public function createNumber( $spos ) {
|
|
$rknumhigh = $this->_GetInt4d( $this->data, $spos + 10 );
|
|
$rknumlow = $this->_GetInt4d( $this->data, $spos + 6 );
|
|
//for ($i=0; $i<8; $i++) { echo ord($this->_data[$i+$spos+6]) . " "; } echo "<br>";
|
|
$sign = ( $rknumhigh & 0x80000000 ) >> 31;
|
|
$exp = ( $rknumhigh & 0x7ff00000 ) >> 20;
|
|
$mantissa = ( 0x100000 | ( $rknumhigh & 0x000fffff ) );
|
|
$mantissalow1 = ( $rknumlow & 0x80000000 ) >> 31;
|
|
$mantissalow2 = ( $rknumlow & 0x7fffffff );
|
|
$value = $mantissa / pow( 2, 20 - ( $exp - 1023 ) );
|
|
if ( $mantissalow1 !== 0 ) {
|
|
$value += 1 / pow( 2, 21 - ( $exp - 1023 ) );
|
|
}
|
|
$value += $mantissalow2 / pow( 2, 52 - ( $exp - 1023 ) );
|
|
//echo "Sign = $sign, Exp = $exp, mantissahighx = $mantissa, mantissalow1 = $mantissalow1, mantissalow2 = $mantissalow2<br>\n";
|
|
if ( $sign ) {
|
|
$value = - 1 * $value;
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Set the encoding method
|
|
*
|
|
* @param string $encoding Encoding to use
|
|
*
|
|
* @access public
|
|
*/
|
|
public function setOutputEncoding( $encoding ) {
|
|
$this->defaultEncoding = $encoding;
|
|
}
|
|
|
|
public function setRowColOffset( $iOffset ) {
|
|
$this->rowoffset = $iOffset;
|
|
$this->colOffset = $iOffset;
|
|
}
|
|
|
|
/**
|
|
* Set the default number format
|
|
*
|
|
* @access public
|
|
*
|
|
* @param string $sFormat Default format
|
|
*/
|
|
public function setDefaultFormat( $sFormat ) {
|
|
$this->defaultFormat = $sFormat;
|
|
}
|
|
|
|
/**
|
|
* Force a column to use a certain format
|
|
*
|
|
* @access public
|
|
*
|
|
* @param integer $column Column number
|
|
* @param string $sFormat Format
|
|
*/
|
|
public function setColumnFormat( $column, $sFormat ) {
|
|
$this->columnsFormat[ $column ] = $sFormat;
|
|
}
|
|
private function _strlen( $str ) {
|
|
return (ini_get('mbstring.func_overload') & 2) ? mb_strlen($str , '8bit') : strlen($str);
|
|
}
|
|
private function _strpos( $haystack, $needle, $offset = 0 ) {
|
|
return (ini_get('mbstring.func_overload') & 2) ? mb_strpos( $haystack, $needle, $offset , '8bit') : strpos($haystack, $needle, $offset);
|
|
}
|
|
private function _substr( $str, $start, $length = null ) {
|
|
return (ini_get('mbstring.func_overload') & 2) ? mb_substr( $str, $start, ($length === null) ? mb_strlen($str,'8bit') : $length, '8bit') : substr($str, $start, ($length === null) ? strlen($str) : $length );
|
|
}
|
|
}
|
|
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |