7e47ce238b
添加了管理后台所需的图片资源、Excel文件、安装程序以及设计相关的图片文件
1612 lines
58 KiB
PHP
1612 lines
58 KiB
PHP
<?php
|
|
define('ABC_CRITICAL', 0);
|
|
define('ABC_ERROR', 1);
|
|
define('ABC_ALERT', 2);
|
|
define('ABC_WARNING', 3);
|
|
define('ABC_NOTICE', 4);
|
|
define('ABC_INFO', 5);
|
|
define('ABC_DEBUG', 6);
|
|
define('ABC_TRACE', 7);
|
|
define('ABC_VAR_DUMP', 8);
|
|
define('ABC_NO_LOG', -1);
|
|
$php_version = split( "\.", phpversion() );
|
|
if( $php_version[0] == 4 && $php_version[1] <= 1 ) {
|
|
if( !function_exists('var_export') ) {
|
|
function var_export( $exp, $ret ) {
|
|
ob_start();
|
|
var_dump( $exp );
|
|
$result = ob_get_contents();
|
|
ob_end_clean();
|
|
return $result;
|
|
}
|
|
}
|
|
}
|
|
function print_bt()
|
|
{
|
|
print "<code>\n";
|
|
$cs = debug_backtrace();
|
|
for( $i = 1; $i < count($cs) ; $i++ )
|
|
{
|
|
$item = $cs[ $i ];
|
|
for( $j = 0; $j < count($item['args']); $j++ )
|
|
if( is_string($item['args'][$j]) )
|
|
$item['args'][$j] = "\"" . $item['args'][$j] . "\"";
|
|
$args = join(",", $item['args'] );
|
|
if( isset( $item['class'] ) )
|
|
$str = sprintf("%s(%d): %s%s%s(%s)",
|
|
$item['file'],
|
|
$item['line'],
|
|
$item['class'],
|
|
$item['type'],
|
|
$item['function'],
|
|
$args );
|
|
else
|
|
$str = sprintf("%s(%d): %s(%s)",
|
|
$item['file'],
|
|
$item['line'],
|
|
$item['function'],
|
|
$args );
|
|
echo $str . "<br>\n";
|
|
}
|
|
print "</code>\n";
|
|
}
|
|
function _die( $str )
|
|
{
|
|
print "Script died with reason: $str<br>\n";
|
|
print_bt();
|
|
exit();
|
|
}
|
|
|
|
class DebugOut
|
|
{
|
|
var $priorities = array(ABC_CRITICAL => 'critical',
|
|
ABC_ERROR => 'error',
|
|
ABC_ALERT => 'alert',
|
|
ABC_WARNING => 'warning',
|
|
ABC_NOTICE => 'notice',
|
|
ABC_INFO => 'info',
|
|
ABC_DEBUG => 'debug',
|
|
ABC_TRACE => 'trace',
|
|
ABC_VAR_DUMP => 'dump'
|
|
);
|
|
var $_ready = false;
|
|
var $_currentPriority = ABC_DEBUG;
|
|
var $_consumers = array();
|
|
var $_filename;
|
|
var $_fp;
|
|
var $_logger_name;
|
|
|
|
function DebugOut($name, $logger_name, $level )
|
|
{
|
|
$this->_filename = $name;
|
|
$this->_currentPriority = $level;
|
|
$this->_logger_name = $logger_name;
|
|
if ($level > ABC_NO_LOG){
|
|
$this->_openfile();
|
|
} /*Destructor Registering*/
|
|
register_shutdown_function(array($this,"close"));
|
|
}
|
|
function log($message, $priority = ABC_INFO)
|
|
{
|
|
// Abort early if the priority is above the maximum logging level.
|
|
if ($priority > $this->_currentPriority) {
|
|
return false;
|
|
} // Add to loglines array
|
|
return $this->_writeLine($message, $priority, strftime('%b %d %H:%M:%S'));
|
|
}
|
|
function dump($variable,$name)
|
|
{
|
|
$priority = ABC_VAR_DUMP;
|
|
if ($priority > $this->_currentPriority ) {
|
|
return false;
|
|
}
|
|
$time = strftime('%b %d %H:%M:%S');
|
|
$message = var_export($variable,true);
|
|
return fwrite($this->_fp,
|
|
sprintf("%s %s [%s] variable %s = %s \r\n",
|
|
$time,
|
|
$this->_logger_name,
|
|
$this->priorities[$priority],
|
|
$name,
|
|
$message)
|
|
);
|
|
}
|
|
function info($message)
|
|
{
|
|
return $this->log($message, ABC_INFO);
|
|
}
|
|
function debug($message)
|
|
{
|
|
return $this->log($message, ABC_DEBUG);
|
|
}
|
|
function notice($message)
|
|
{
|
|
return $this->log($message, ABC_NOTICE);
|
|
}
|
|
function warning($message)
|
|
{
|
|
return $this->log($message, ABC_WARNING);
|
|
}
|
|
function trace($message)
|
|
{
|
|
return $this->log($message, ABC_TRACE);
|
|
}
|
|
function error($message)
|
|
{
|
|
return $this->log($message, ABC_ERROR);
|
|
} /**
|
|
* Writes a line to the logfile
|
|
*
|
|
* @param string $line The line to write
|
|
* @param integer $priority The priority of this line/msg
|
|
* @return integer Number of bytes written or -1 on error
|
|
* @access private
|
|
*/
|
|
function _writeLine($message, $priority, $time)
|
|
{
|
|
if( fwrite($this->_fp, sprintf("%s %s [%s] %s\r\n", $time, $this->_logger_name, $this->priorities[$priority], $message)) )
|
|
{
|
|
return fflush($this->_fp);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
function _openfile()
|
|
{
|
|
if (($this->_fp = @fopen($this->_filename, 'a')) == false)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
function close()
|
|
{
|
|
if($this->_currentPriority != ABC_NO_LOG)
|
|
{
|
|
$this->info("Logger stoped");
|
|
return fclose($this->_fp);
|
|
}
|
|
} /*
|
|
* Managerial Functions.
|
|
*
|
|
*/
|
|
function Factory($name, $logger_name, $level)
|
|
{
|
|
$instance = new DebugOut($name, $logger_name, $level);
|
|
return $instance;
|
|
}
|
|
function &getWriterSingleton($name, $logger_name, $level = ABC_DEBUG)
|
|
{
|
|
static $instances;
|
|
if (!isset($instances))
|
|
{
|
|
$instances = array();
|
|
}
|
|
$signature = serialize(array($name, $level));
|
|
if (!isset($instances[$signature]))
|
|
{
|
|
$instances[$signature] = DebugOut::Factory($name, $logger_name, $level);
|
|
}
|
|
return $instances[$signature];
|
|
}
|
|
function attach(&$logObserver)
|
|
{
|
|
if (!is_object($logObserver))
|
|
{
|
|
return false;
|
|
}
|
|
$logObserver->_listenerID = uniqid(rand());
|
|
$this->_listeners[$logObserver->_listenerID] = &$logObserver;
|
|
}
|
|
}
|
|
|
|
define ('ABC_BAD_DATE', -1);
|
|
class ExcelDateUtil
|
|
{
|
|
/*
|
|
* return 1900 Date as integer TIMESTAMP.
|
|
* for UNIX date must be
|
|
*
|
|
*/
|
|
function xls2tstamp($date)
|
|
{
|
|
$date=$date>25568?$date:25569;
|
|
/*There was a bug if Converting date before 1-1-1970 (tstamp 0)*/
|
|
$ofs=(70 * 365 + 17+2) * 86400;
|
|
return ($date * 86400) - $ofs;
|
|
}
|
|
function getDateArray($xls_date)
|
|
{
|
|
$ret = array();
|
|
// leap year bug
|
|
if ($xls_date == 60)
|
|
{
|
|
$ret['day'] = 29;
|
|
$ret['month'] = 2;
|
|
$ret['year'] = 1900;
|
|
return $ret;
|
|
}else if ($xls_date < 60)
|
|
{
|
|
// 29-02-1900 bug
|
|
$xls_date++;
|
|
} // Modified Julian to DMY calculation with an addition of 2415019
|
|
$l = $xls_date + 68569 + 2415019;
|
|
$n = (int)(( 4 * $l ) / 146097);
|
|
$l = $l - (int)(( 146097 * $n + 3 ) / 4);
|
|
$i = (int)(( 4000 * ( $l + 1 ) ) / 1461001);
|
|
$l = $l - (int)(( 1461 * $i ) / 4) + 31;
|
|
$j = (int)(( 80 * $l ) / 2447);
|
|
$ret['day'] = $l - (int)(( 2447 * $j ) / 80);
|
|
$l = (int)($j / 11);
|
|
$ret['month'] = $j + 2 - ( 12 * $l );
|
|
$ret['year'] = 100 * ( $n - 49 ) + $i + $l;
|
|
return $ret;
|
|
}
|
|
function isInternalDateFormat($format)
|
|
{
|
|
$retval =false;
|
|
switch(format)
|
|
{
|
|
// Internal Date Formats as described on page 427 in
|
|
// Microsoft Excel Dev's Kit...
|
|
case 0x0e:
|
|
case 0x0f:
|
|
case 0x10:
|
|
case 0x11:
|
|
case 0x12:
|
|
case 0x13:
|
|
case 0x14:
|
|
case 0x15:
|
|
case 0x16:
|
|
case 0x2d:
|
|
case 0x2e:
|
|
case 0x2f:
|
|
// Additional internal date formats found by inspection
|
|
// Using Excel v.X 10.1.0 (Mac)
|
|
case 0xa4:
|
|
case 0xa5:
|
|
case 0xa6:
|
|
case 0xa7:
|
|
case 0xa8:
|
|
case 0xa9:
|
|
case 0xaa:
|
|
case 0xab:
|
|
case 0xac:
|
|
case 0xad:
|
|
$retval = true;
|
|
break;
|
|
default: $retval = false;
|
|
break;
|
|
}
|
|
return $retval;
|
|
}
|
|
}
|
|
|
|
define('EXCEL_FONT_RID',0x31);
|
|
define('XF_SCRIPT_NONE',0);
|
|
define('XF_SCRIPT_SUPERSCRIPT',1);
|
|
define('XF_SCRIPT_SUBSCRIPT',2);
|
|
define('XF_UNDERLINE_NONE',0x0);
|
|
define('XF_UNDERLINE_SINGLE',0x1);
|
|
define('XF_UNDERLINE_DOUBLE',0x2);
|
|
define('XF_UNDERLINE_SINGLE_ACCOUNTING',0x3);
|
|
define('XF_UNDERLINE_DOUBLE_ACCOUNTING',0x4);
|
|
define('XF_STYLE_ITALIC', 0x2);
|
|
define('XF_STYLE_STRIKEOUT', 0x8);
|
|
define('XF_BOLDNESS_REGULAR',0x190);
|
|
define('XF_BOLDNESS_BOLD',0x2BC);
|
|
|
|
class ExcelFont
|
|
{
|
|
function basicFontRecord()
|
|
{
|
|
return array('size' => 10,
|
|
'script' => XF_SCRIPT_NONE,
|
|
'undeline' => XF_UNDERLINE_NONE,
|
|
'italic' => false,
|
|
'strikeout'=> false,
|
|
'bold' => false,
|
|
'boldness' => XF_BOLDNESS_REGULAR,
|
|
'palete' => 0,
|
|
'name' => 'Arial');
|
|
}
|
|
function getFontRecord(&$wb,$ptr)
|
|
{
|
|
$retval = array('size' => 0,
|
|
'script' => XF_SCRIPT_NONE,
|
|
'undeline' => XF_UNDERLINE_NONE,
|
|
'italic' => false,
|
|
'strikeout'=> false,
|
|
'bold' => false,
|
|
'boldness' => XF_BOLDNESS_REGULAR,
|
|
'palete' => 0,
|
|
'name' => '');
|
|
$retval['size'] = (ord($wb[$ptr])+ 256*ord($wb[$ptr+1]))/20;
|
|
$style=ord($wb[$ptr+2]);
|
|
if (($style & XF_STYLE_ITALIC) != 0)
|
|
{
|
|
$retval['italic'] = true;
|
|
}
|
|
if (($style & XF_STYLE_STRIKEOUT) != 0)
|
|
{
|
|
$retval['strikeout'] = true;
|
|
}
|
|
$retval['palete'] = ord($wb[$ptr+4])+256*ord($wb[$ptr+5]);
|
|
$retval['boldness'] = ord($wb[$ptr+6])+256*ord($wb[$ptr+7]);
|
|
$retval['bold'] = $retval['boldness'] == XF_BOLDNESS_REGULAR ? false:true;
|
|
$retval['script'] = ord($wb[$ptr+8])+256*ord($wb[$ptr+9]);
|
|
$retval['underline'] = ord($wb[$ptr+10]);
|
|
$length = ord($wb[$ptr+14]);
|
|
if($length >0)
|
|
{
|
|
if(ord($wb[$ptr+15]) == 0)
|
|
{
|
|
// Compressed Unicode
|
|
$retval['name'] = substr($wb,$ptr+16,$length);
|
|
} else {
|
|
// Uncompressed Unicode
|
|
$retval['name'] = ExcelFont::getUnicodeString($wb,$ptr+15,$length);
|
|
}
|
|
}
|
|
return $retval;
|
|
}
|
|
function toString(&$record,$index)
|
|
{
|
|
$retval = sprintf("Font Index = %d \nFont Size =%d\nItalic = %s\nStrikeoout=%s\nPalete=%s\nBoldness = %s Bold=%s\n Script = %d\n Underline = %d\n FontName=%s<hr>",
|
|
$index,
|
|
$record['size'],
|
|
$record['italic'] == true?"true":"false",
|
|
$record['strikeout'] == true?"true":"false",
|
|
$record['palete'],
|
|
$record['boldness'],
|
|
$record['bold'] == true?"true":"false",
|
|
$record['script'],
|
|
$record['underline'],
|
|
$record['name']
|
|
);
|
|
return $retval;
|
|
}
|
|
function getUnicodeString(&$string,$offset,$length)
|
|
{
|
|
$bstring = "";
|
|
$index = $offset + 1; // start with low bits.
|
|
for ($k = 0; $k < $length; $k++)
|
|
{
|
|
$bstring = $bstring.$string[$index];
|
|
$index += 2;
|
|
}
|
|
return substr($bstring,0,$length);
|
|
}
|
|
function ExcelToCSS($rec, $app_font=true, $app_size=true, $app_italic=true, $app_bold=true)
|
|
{
|
|
$ret = "";
|
|
if($app_font==true)
|
|
{
|
|
$ret = $ret."font-family:".$rec['name']."; ";
|
|
}
|
|
if($app_size==true)
|
|
{
|
|
$ret = $ret."font-size:".$rec['size']."pt; ";
|
|
}
|
|
if($app_bold==true)
|
|
{
|
|
if($rec['bold']==true)
|
|
{
|
|
$ret = $ret."font-weight:bold; ";
|
|
} else {
|
|
$ret = $ret."font-weight:normal; ";
|
|
}
|
|
}
|
|
if($app_italic==true)
|
|
{
|
|
if($rec['italic']==true)
|
|
{
|
|
$ret = $ret."font-style:italic; ";
|
|
} else {
|
|
$ret = $ret."font-style:normal; ";
|
|
}
|
|
}
|
|
return $ret;
|
|
}
|
|
}
|
|
|
|
define ( DP_EMPTY, 0 );
|
|
define ( DP_STRING_SOURCE, 1 );
|
|
define ( DP_FILE_SOURCE, 2 );
|
|
//------------------------------------------------------------------------
|
|
class ExcelParserUtil
|
|
{
|
|
function str2long($str)
|
|
{
|
|
return ord($str[0]) + 256*(ord($str[1]) +
|
|
256*(ord($str[2]) + 256*(ord($str[3])) ));
|
|
}
|
|
}//------------------------------------------------------------------------
|
|
|
|
class DataProvider
|
|
{
|
|
function DataProvider( $data, $dataType )
|
|
{
|
|
switch( $dataType )
|
|
{
|
|
case DP_FILE_SOURCE:
|
|
if( !( $this->_data = @fopen( $data, "rb" )) )
|
|
return;
|
|
$this->_size = @filesize( $data );
|
|
if( !$this->_size )
|
|
_die("Failed to determine file size.");
|
|
break;
|
|
case DP_STRING_SOURCE:
|
|
$this->_data = $data;
|
|
$this->_size = strlen( $data );
|
|
break;
|
|
default:
|
|
_die("Invalid data type provided.");
|
|
}
|
|
$this->_type = $dataType;
|
|
register_shutdown_function( array( $this, "close") );
|
|
}
|
|
function get( $offset, $length )
|
|
{
|
|
if( !$this->isValid() )
|
|
_die("Data provider is empty.");
|
|
if( $this->_baseOfs + $offset + $length > $this->_size )
|
|
_die("Invalid offset/length.");
|
|
switch( $this->_type )
|
|
{
|
|
case DP_FILE_SOURCE:
|
|
{
|
|
if( @fseek( $this->_data, $this->_baseOfs + $offset, SEEK_SET ) == -1 )
|
|
_die("Failed to seek file position specified by offest.");
|
|
return @fread( $this->_data, $length );
|
|
}
|
|
case DP_STRING_SOURCE:
|
|
{
|
|
$rc = substr( $this->_data, $this->_baseOfs + $offset, $length );
|
|
return $rc;
|
|
}
|
|
default:
|
|
_die("Invalid data type or class was not initialized.");
|
|
}
|
|
}
|
|
function getByte( $offset )
|
|
{
|
|
return $this->get( $offset, 1 );
|
|
}
|
|
function getOrd( $offset )
|
|
{
|
|
return ord( $this->getByte( $offset ) );
|
|
}
|
|
function getLong( $offset )
|
|
{
|
|
$str = $this->get( $offset, 4 );
|
|
return ExcelParserUtil::str2long( $str );
|
|
}
|
|
function getSize()
|
|
{
|
|
if( !$this->isValid() )
|
|
_die("Data provider is empty.");
|
|
return $this->_size;
|
|
}
|
|
function getBlocks()
|
|
{
|
|
if( !$this->isValid() )
|
|
_die("Data provider is empty.");
|
|
return (int)(($this->_size - 1) / 0x200) - 1;
|
|
}
|
|
function ReadFromFat( $chain, $gran = 0x200 )
|
|
{
|
|
$rc = '';
|
|
for( $i = 0; $i < count($chain); $i++ )
|
|
$rc .= $this->get( $chain[$i] * $gran, $gran );
|
|
return $rc;
|
|
}
|
|
function close()
|
|
{
|
|
switch($this->_type )
|
|
{
|
|
case DP_FILE_SOURCE:
|
|
@fclose( $this->_data );
|
|
case DP_STRING_SOURCE:
|
|
$this->_data = null;
|
|
default:
|
|
$_type = DP_EMPTY;
|
|
break;
|
|
}
|
|
}
|
|
function isValid()
|
|
{
|
|
return $this->_type != DP_EMPTY;
|
|
}
|
|
var $_type = DP_EMPTY;
|
|
var $_data = null;
|
|
var $_size = -1;
|
|
var $_baseOfs = 0;
|
|
}
|
|
|
|
class ExcelFileParser
|
|
{
|
|
var $dp = null;
|
|
var $max_blocks;
|
|
var $max_sblocks;
|
|
// Internal variables
|
|
var $fat;
|
|
var $sfat;
|
|
// Removed: var $sbd;
|
|
// Removed: var $syear;
|
|
var $formats;
|
|
var $xf;
|
|
var $fonts;
|
|
var $dbglog;
|
|
|
|
function ExcelFileParser($logfile="",$level=ABC_NO_LOG)
|
|
{
|
|
$this->dbglog = &DebugOut::getWriterSingleton($logfile,"",$level);
|
|
$this->dbglog->info("Logger started");
|
|
}
|
|
function populateFormat()
|
|
{
|
|
$this->dbglog->trace(" populateFormat() function call");
|
|
$ret = array (
|
|
0=> "General",
|
|
1=> "0",
|
|
2=> "0.00",
|
|
3=> "#,##0",
|
|
4=> "#,##0.00",
|
|
5=> "($#,##0_);($#,##0)",
|
|
6=> "($#,##0_);[Red]($#,##0)",
|
|
7=> "($#,##0.00);($#,##0.00)",
|
|
8=> "($#,##0.00_);[Red]($#,##0.00)",
|
|
9=> "0%",
|
|
0xa=> "0.00%",
|
|
0xb=> "0.00E+00",
|
|
0xc=> "# ?/?",
|
|
0xd=> "# ??/??",
|
|
0xe=> "m/d/yy",
|
|
0xf=> "d-mmm-yy",
|
|
0x10=> "d-mmm",
|
|
0x11=> "mmm-yy",
|
|
0x12=> "h:mm AM/PM",
|
|
0x13=> "h:mm:ss AM/PM",
|
|
0x14=> "h:mm",
|
|
0x15=> "h:mm:ss",
|
|
0x16=> "m/d/yy h:mm",
|
|
// 0x17 - 0x24 reserved for international and undocumented
|
|
0x17=> "0x17",
|
|
0x18=> "0x18",
|
|
0x19=> "0x19",
|
|
0x1a=> "0x1a",
|
|
0x1b=> "0x1b",
|
|
0x1c=> "0x1c",
|
|
0x1d=> "0x1d",
|
|
0x1e=> "0x1e",
|
|
0x1f=> "0x1f",
|
|
0x20=> "0x20",
|
|
0x21=> "0x21",
|
|
0x22=> "0x22",
|
|
0x23=> "0x23",
|
|
0x24=> "0x24",
|
|
// 0x17 - 0x24 reserved for international and undocumented
|
|
0x25=> "(#,##0_);(#,##0)",
|
|
0x26=> "(#,##0_);[Red](#,##0)",
|
|
0x27=> "(#,##0.00_);(#,##0.00)",
|
|
0x28=> "(#,##0.00_);[Red](#,##0.00)",
|
|
0x29=> "_(*#,##0_);_(*(#,##0);_(* \"-\"_);_(@_)",
|
|
0x2a=> "_($*#,##0_);_($*(#,##0);_($* \"-\"_);_(@_)",
|
|
0x2b=> "_(*#,##0.00_);_(*(#,##0.00);_(*\"-\"??_);_(@_)",
|
|
0x2c=> "_($*#,##0.00_);_($*(#,##0.00);_($*\"-\"??_);_(@_)",
|
|
0x2d=> "mm:ss",
|
|
0x2e=> "[h]:mm:ss",
|
|
0x2f=> "mm:ss.0",
|
|
0x30=> "##0.0E+0",
|
|
0x31=> "@");
|
|
$this->dbglog->dump($ret,"\$ret");
|
|
$this->dbglog->trace("populateFormat() function return");
|
|
return $ret;
|
|
}
|
|
function xls2tstamp($date)
|
|
{
|
|
$date=$date>25568?$date:25569;
|
|
/*There was a bug if Converting date before 1-1-1970 (tstamp 0)*/
|
|
$ofs=(70 * 365 + 17+2) * 86400;
|
|
return ($date * 86400) - $ofs;
|
|
}
|
|
function getDateArray($date)
|
|
{
|
|
return ExcelDateUtil::getDateArray($date);
|
|
}
|
|
function isDateFormat($val)
|
|
{
|
|
$f_i=$this->xf['format'][$val];
|
|
if(preg_match("/[m|d|y]/i",$this->format[$f_i])!=0)
|
|
{
|
|
if(strrpos($this->format[$f_i],'[')!=FALSE)
|
|
{
|
|
$tmp = preg_replace("/(\[\/?)(\w+)([^\]]*\])/","'\\1'.''.'\\3'",$this->format[$f_i]);
|
|
if(preg_match("/[m|d|y]/i",$tmp)!=0)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
} else
|
|
return FALSE;
|
|
}
|
|
function getUnicodeString($str,$ofs)
|
|
{
|
|
$size=0;
|
|
$i_ofs=0;
|
|
/* if (ord($str[$ofs])==255) {
|
|
$size=ord($str[$ofs])+ 256*(ord($str[$ofs+1]));
|
|
$i_ofs=2;
|
|
} else {*/
|
|
$size=ord($str[$ofs]);
|
|
$i_ofs=1;
|
|
/* }*/
|
|
return substr($str,$ofs+$i_ofs+1,$size);
|
|
}
|
|
function getByteString($str,$ofs)
|
|
{
|
|
$size=0;
|
|
$i_ofs=0;
|
|
// if (ord($str[$ofs])==255) {
|
|
// $size=ord($str[$ofs])+ 256*(ord($str[$ofs+1]));
|
|
// $i_ofs=2;
|
|
// } else {
|
|
$size=ord($str[$ofs]);
|
|
$i_ofs=1;
|
|
// } return substr($str,$ofs+$i_ofs+1,$size);
|
|
}
|
|
/*
|
|
* Get blocks chain
|
|
*/
|
|
function get_blocks_chain($start,$small_fat=false)
|
|
{
|
|
$this->dbglog->trace("get_blocks_chain(".var_export($start,true).",".var_export($small_fat,true).") function call ");
|
|
$chain = array();
|
|
$next_block = $start;
|
|
if( !$small_fat )
|
|
{
|
|
while( ($next_block!=0xfffffffe) &&
|
|
($next_block <= $this->max_blocks) &&
|
|
($next_block < count($this->fat)) )
|
|
{
|
|
$chain[] = $next_block;
|
|
$next_block = $this->fat[$next_block];
|
|
}
|
|
} else {
|
|
while( ($next_block!=0xfffffffe) &&
|
|
($next_block <= $this->max_sblocks) &&
|
|
($next_block < count($this->sfat)) )
|
|
{
|
|
$chain[] = $next_block;
|
|
$next_block = $this->sfat[$next_block];
|
|
}
|
|
}
|
|
if( $next_block != 0xfffffffe )
|
|
return false;
|
|
$this->dbglog->dump($chain,"\$chain");
|
|
$this->dbglog->trace("get_blocks_chain() function return");
|
|
return $chain;
|
|
}
|
|
/* Find stream by name
|
|
*
|
|
*/
|
|
function find_stream( $dir, $item_name,$item_num=0)
|
|
{
|
|
$this->dbglog->trace("find_stream(".var_export($dir,true).",".var_export($item_name,true).",".var_export($item_num,true).") function call ");
|
|
$dt = $dir->getOrd( $item_num * 0x80 + 0x42 );
|
|
$prev = $dir->getLong( $item_num * 0x80 + 0x44 );
|
|
$next = $dir->getLong( $item_num * 0x80 + 0x48 );
|
|
$dir_ = $dir->getLong( $item_num * 0x80 + 0x4c );
|
|
$curr_name = '';
|
|
if( ($dt==2) || ($dt==5) )
|
|
for( $i=0;
|
|
$i < ( $dir->getOrd( $item_num * 0x80 + 0x40 ) +
|
|
256 * $dir->getOrd( $item_num * 0x80 + 0x41 ) )/2-1;
|
|
$i++ )
|
|
$curr_name .= $dir->getByte( $item_num * 0x80 + $i * 2 );
|
|
if( (($dt==2) || ($dt==5)) && (strcmp($curr_name,$item_name)==0) )
|
|
{
|
|
$this->dbglog->trace("find_stream() function return with ".var_export($item_num,true));
|
|
return $item_num;
|
|
}
|
|
if( $prev != 0xffffffff )
|
|
{
|
|
$i = $this->find_stream( $dir, $item_name, $prev);
|
|
if( $i>=0 )
|
|
{
|
|
$this->dbglog->trace("find_stream() function return with ".var_export($i,true));
|
|
return $i;
|
|
}
|
|
}
|
|
if( $next != 0xffffffff )
|
|
{
|
|
$i = $this->find_stream( $dir, $item_name, $next);
|
|
if( $i>=0 )
|
|
{
|
|
$this->dbglog->trace("find_stream() function return with ".var_export($i,true));
|
|
return $i;
|
|
}
|
|
}
|
|
if( $dir_ != 0xffffffff )
|
|
{
|
|
$i = $this->find_stream( $dir, $item_name, $dir_ );
|
|
if( $i>=0 )
|
|
{
|
|
$this->dbglog->trace("find_stream() function return with ".var_export($i,true));
|
|
return $i;
|
|
}
|
|
}
|
|
$this->dbglog->trace("find_stream() function return with -1");
|
|
return -1;
|
|
}
|
|
function rk_decode($rk)
|
|
{
|
|
$this->dbglog->trace("rk_decode(".var_export($rk,true).") function call");
|
|
$res = array();
|
|
if( $rk & 2 )
|
|
{
|
|
// integer
|
|
$val = ($rk & 0xfffffffc) >> 2;
|
|
if( $rk & 1 ) $val = $val / 100;
|
|
if (((float)$val) == floor((float)$val))
|
|
{
|
|
$res['val'] = (int)$val;
|
|
$res['type'] = 1;
|
|
} else {
|
|
$res['val'] = (float)$val;
|
|
$res['type'] = 2;
|
|
}
|
|
} else {
|
|
// float
|
|
$res['type'] = 2;
|
|
$frk = $rk;
|
|
$fexp = (($frk & 0x7ff00000) >> 20) - 1023;
|
|
$val = 1+(($frk & 0x000fffff) >> 2)/262144;
|
|
if( $fexp > 0 )
|
|
{
|
|
for( $i=0; $i<$fexp; $i++ )
|
|
$val *= 2;
|
|
} else {
|
|
if( $fexp==-1023 )
|
|
{
|
|
$val=0;
|
|
} else {
|
|
for( $i=0; $i<abs($fexp); $i++ )
|
|
$val /= 2;
|
|
}
|
|
}
|
|
if( $rk & 1 ) $val = $val / 100;
|
|
if( $rk & 0x80000000 ) $val = -$val;
|
|
$res['val'] = (float)$val;
|
|
}
|
|
$this->dbglog->trace("rk_decode() function returns");
|
|
return $res;
|
|
}
|
|
// Parse worksheet
|
|
//-----------------
|
|
function parse_worksheet($ws)
|
|
{
|
|
$this->dbglog->debug("parse_worksheet(DATA) function");
|
|
if( strlen($ws) <= 0 )
|
|
{
|
|
$this->dbglog->trace("parse_worksheet() function returns 7 (Data not Found)");
|
|
return 7;
|
|
}
|
|
if( strlen($ws) < 4 )
|
|
{
|
|
$this->dbglog->trace("parse_worksheet() function returns 6 (File Corrupted)");
|
|
return 6;
|
|
}
|
|
// parse workbook header
|
|
if( strlen($ws) < 256*ord($ws[3])+ord($ws[2]) ) return 6;
|
|
if( ord($ws[0]) != 0x09 ) return 6;
|
|
$vers = ord($ws[1]);
|
|
if( ($vers!=0) && ($vers!=2) && ($vers!=4) && ($vers!=8) )
|
|
return 8;
|
|
if( $vers!=8 )
|
|
{
|
|
$biff_ver = ($ver+4)/2;
|
|
} else {
|
|
if( strlen($ws) < 12 ) return 6;
|
|
switch( ord($ws[4])+256*ord($ws[5]) )
|
|
{
|
|
case 0x0500:
|
|
if( ord($ws[0x0a])+256*ord($ws[0x0b]) < 1994 )
|
|
{
|
|
$biff_ver = 5;
|
|
} else {
|
|
switch(ord( $ws[8])+256*ord($ws[9]) )
|
|
{
|
|
case 2412:
|
|
case 3218:
|
|
case 3321:
|
|
$this->dbglog->debug("Parsed BIFF version is 5");
|
|
$biff_ver = 5;
|
|
break;
|
|
default:
|
|
$this->dbglog->debug("Parsed BIFF version is 7");
|
|
$biff_ver = 7;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 0x0600:
|
|
/*DBG*/
|
|
$this->dbglog->debug("Parsed BIFF version is 8");
|
|
$biff_ver = 8;
|
|
break;
|
|
default:
|
|
return 8;
|
|
}
|
|
}
|
|
if( $biff_ver < 5 )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->debug("parse_worksheet() function found ($biff_ver < 5) return 8");
|
|
return 8;
|
|
}
|
|
$ptr = 0;
|
|
$data = array('biff_version' => $biff_ver );
|
|
while( (ord($ws[$ptr])!=0x0a) && ($ptr<strlen($ws)) )
|
|
{
|
|
switch (ord($ws[$ptr])+256*ord($ws[$ptr+1]))
|
|
{
|
|
// Number
|
|
case 0x0203:
|
|
/*DBG*/
|
|
$this->dbglog->trace("found NUMBER");
|
|
if( ($biff_ver < 3) )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->trace("$biff_ver < 3 break;");
|
|
break;
|
|
}
|
|
if( (ord($ws[$ptr+2])+256*ord($ws[$ptr+3])) < 14 )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->debug("parse_worksheet() return 6");
|
|
return 6;
|
|
}
|
|
$row = ord($ws[$ptr+4])+256*ord($ws[$ptr+5]);
|
|
$col = ord($ws[$ptr+6])+256*ord($ws[$ptr+7]);
|
|
$num_lo = ExcelParserUtil::str2long(substr($ws,$ptr+10,4));
|
|
$num_hi = ExcelParserUtil::str2long(substr($ws,$ptr+14,4));
|
|
$xf_i = ord($ws[$ptr+8])+256*ord($ws[$ptr+9]);
|
|
if($this->isDateFormat($xf_i))
|
|
{
|
|
$data['cell'][$row][$col]['type'] = 3;
|
|
} else {
|
|
$data['cell'][$row][$col]['type'] = 2;
|
|
}
|
|
$fonti = $this->xf['font'][$xf_i];
|
|
$data['cell'][$row][$fc+$i]['font'] = $fonti;
|
|
|
|
$fexp = (($num_hi & 0x7ff00000) >> 20) - 1023;
|
|
$val = 1+(($num_hi & 0x000fffff)+$num_lo/4294967296)/1048576;
|
|
if( $fexp > 0 )
|
|
{
|
|
for( $i=0; $i<$fexp; $i++ )
|
|
$val *= 2;
|
|
} else {
|
|
for( $i=0; $i<abs($fexp); $i++ )
|
|
$val /= 2;
|
|
}
|
|
if( $num_hi & 0x80000000 ) $val = -$val;
|
|
$data['cell'][$row][$col]['data'] = (float)$val;
|
|
if( !isset($data['max_row']) ||
|
|
($data['max_row'] < $row) )
|
|
$data['max_row'] = $row;
|
|
if( !isset($data['max_col']) ||
|
|
($data['max_col'] < $col) )
|
|
$data['max_col'] = $col;
|
|
break;
|
|
// RK
|
|
case 0x027e:
|
|
/*DBG*/
|
|
$this->dbglog->trace("found RK");
|
|
if( ($biff_ver < 3) ) break;
|
|
if( (ord($ws[$ptr+2])+256*ord($ws[$ptr+3])) < 0x0a )
|
|
return 6;
|
|
$row = ord($ws[$ptr+4])+256*ord($ws[$ptr+5]);
|
|
$col = ord($ws[$ptr+6])+256*ord($ws[$ptr+7]);
|
|
$xf_i = ord($ws[$ptr+8])+256*ord($ws[$ptr+9]);
|
|
$val = $this->rk_decode(
|
|
ExcelParserUtil::str2long(substr($ws,$ptr+10,4))
|
|
);
|
|
if($this->isDateFormat($xf_i)==TRUE)
|
|
{
|
|
$data['cell'][$row][$col]['type'] = 3;
|
|
} else {
|
|
$data['cell'][$row][$col]['type'] = $val['type'];
|
|
}
|
|
$fonti = $this->xf['font'][$xf_i];
|
|
$data['cell'][$row][$col]['font'] = $fonti;
|
|
$data['cell'][$row][$col]['data'] = $val['val'];
|
|
if( !isset($data['max_row']) ||
|
|
($data['max_row'] < $row) )
|
|
$data['max_row'] = $row;
|
|
if( !isset($data['max_col']) ||
|
|
($data['max_col'] < $col) )
|
|
$data['max_col'] = $col;
|
|
break;
|
|
// MULRK
|
|
case 0x00bd:
|
|
/*DBG*/
|
|
$this->dbglog->trace("found MULL RK");
|
|
if( ($biff_ver < 5) ) break;
|
|
$sz = ord($ws[$ptr+2])+256*ord($ws[$ptr+3]);
|
|
if( $sz < 6 ) return 6;
|
|
$row = ord($ws[$ptr+4])+256*ord($ws[$ptr+5]);
|
|
$fc = ord($ws[$ptr+6])+256*ord($ws[$ptr+7]);
|
|
$lc = ord($ws[$ptr+$sz+2])+256*ord($ws[$ptr+$sz+3]);
|
|
for( $i=0; $i<=$lc-$fc; $i++)
|
|
{
|
|
$val = $this->rk_decode(
|
|
ExcelParserUtil::str2long(substr($ws,$ptr+10+$i*6,4))
|
|
);
|
|
$xf_i=ord($ws[$ptr+8+$i*6])+256*ord($ws[($ptr+9+$i*6)]);
|
|
if($this->isDateFormat($xf_i)==TRUE)
|
|
{
|
|
$data['cell'][$row][$fc+$i]['type'] = 3;
|
|
} else {
|
|
$data['cell'][$row][$fc+$i]['type'] = $val['type'];
|
|
}
|
|
$fonti = $this->xf['font'][$xf_i];
|
|
$data['cell'][$row][$fc+$i]['font'] = $fonti;
|
|
$data['cell'][$row][$fc+$i]['data'] = $val['val'];
|
|
}
|
|
if( !isset($data['max_row']) ||
|
|
($data['max_row'] < $row) )
|
|
$data['max_row'] = $row;
|
|
if( !isset($data['max_col']) ||
|
|
($data['max_col'] < $lc) )
|
|
$data['max_col'] = $lc;
|
|
break;
|
|
// LABEL
|
|
case 0x0204:
|
|
/*DBG*/
|
|
$this->dbglog->trace("found LABEL");
|
|
if( ($biff_ver < 3) )
|
|
{
|
|
break;
|
|
}
|
|
if( (ord($ws[$ptr+2])+256*ord($ws[$ptr+3])) < 8 )
|
|
{
|
|
return 6;
|
|
}
|
|
$row = ord($ws[$ptr+4])+256*ord($ws[$ptr+5]);
|
|
$col = ord($ws[$ptr+6])+256*ord($ws[$ptr+7]);
|
|
$xf = ord($ws[$ptr+8])+256*ord($ws[$ptr+9]);
|
|
$fonti = $this->xf['font'][$xf];
|
|
$font = $this->fonts[$fonti];
|
|
$str_len = ord($ws[$ptr+10])+256*ord($ws[$ptr+11]);
|
|
if( $ptr+12+$str_len > strlen($ws) ) return 6;
|
|
$this->sst['unicode'][] = false;
|
|
$this->sst['data'][] = substr($ws,$ptr+12,$str_len);
|
|
$data['cell'][$row][$col]['type'] = 0;
|
|
$sst_ind = count($this->sst['data'])-1;
|
|
$data['cell'][$row][$col]['data'] = $sst_ind;
|
|
$data['cell'][$row][$col]['font'] = $fonti;
|
|
/*echo str_replace("\n","<br>\n", ExcelFont::toString($font,$fonti));
|
|
echo "FontRecord for sting ".$this->sst['data'][$sst_ind]."<br>";*/
|
|
if( !isset($data['max_row']) ||
|
|
($data['max_row'] < $row) )
|
|
$data['max_row'] = $row;
|
|
if( !isset($data['max_col']) ||
|
|
($data['max_col'] < $col) )
|
|
$data['max_col'] = $col;
|
|
break;
|
|
// LABELSST
|
|
case 0x00fd:
|
|
if( $biff_ver < 8 ) break;
|
|
if( (ord($ws[$ptr+2])+256*ord($ws[$ptr+3])) < 0x0a )
|
|
return 6;
|
|
$row = ord($ws[$ptr+4])+256*ord($ws[$ptr+5]);
|
|
$col = ord($ws[$ptr+6])+256*ord($ws[$ptr+7]);
|
|
$xf = ord($ws[$ptr+8])+256*ord($ws[$ptr+9]);
|
|
$fonti = $this->xf['font'][$xf];
|
|
$font = &$this->fonts[$fonti];
|
|
$data['cell'][$row][$col]['type'] = 0;
|
|
$sst_ind = ExcelParserUtil::str2long(substr($ws,$ptr+10,4));
|
|
$data['cell'][$row][$col]['data'] = $sst_ind;
|
|
$data['cell'][$row][$col]['font'] = $fonti;
|
|
/* echo "FontRecord for sting at $row,$col<br>";
|
|
echo str_replace("\n","<br>\n", ExcelFont::toString($font,$fonti));*/
|
|
if( !isset($data['max_row']) ||
|
|
($data['max_row'] < $row) )
|
|
$data['max_row'] = $row;
|
|
if( !isset($data['max_col']) ||
|
|
($data['max_col'] < $col) )
|
|
$data['max_col'] = $col;
|
|
break;
|
|
// unknown, unsupported or unused opcode
|
|
default:
|
|
break;
|
|
}
|
|
$ptr += 4+256*ord($ws[$ptr+3])+ord($ws[$ptr+2]);
|
|
}
|
|
/*DEBUG*/
|
|
$this->dbglog->debug("parse_worksheet() function returns ".var_export($data,true)); /*DEBUG*/
|
|
return $data;
|
|
}// Parse workbook
|
|
//----------------
|
|
function parse_workbook( $f_header, $dp )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->debug("parse_workbook() function");
|
|
$root_entry_block = $f_header->getLong(0x30);
|
|
$num_fat_blocks = $f_header->getLong(0x2c);
|
|
/*TRC*/
|
|
$this->dbglog->trace("Header parsed");
|
|
$this->fat = array();
|
|
for( $i = 0; $i < $num_fat_blocks; $i++ )
|
|
{
|
|
/*TRC*/
|
|
$this->dbglog->trace("FOR LOOP iteration i =".$i);
|
|
$fat_block = $f_header->getLong( 0x4c + 4 * $i );
|
|
$fatbuf = $dp->get( $fat_block * 0x200, 0x200 );
|
|
$fat = new DataProvider( $fatbuf, DP_STRING_SOURCE );
|
|
if( $fat->getSize() < 0x200 )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->debug("parse_workbook() function found (strlen($fat) < 0x200) returns 6");
|
|
return 6;
|
|
}
|
|
for( $j=0; $j<0x80; $j++ )
|
|
$this->fat[] = $fat->getLong( $j * 4 );
|
|
$fat->close();
|
|
unset( $fat_block, $fatbuf, $fat );
|
|
}
|
|
/*DBG*/
|
|
$this->dbglog->dump( $this->fat, "\$fat" );
|
|
if( count($this->fat) < $num_fat_blocks )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->debug("parse_workbook() function found (count($this->fat) < $num_fat_blocks) returns 6");
|
|
return 6;
|
|
}
|
|
$chain = $this->get_blocks_chain($root_entry_block);
|
|
$dir = new DataProvider( $dp->ReadFromFat( $chain ), DP_STRING_SOURCE );
|
|
unset( $chain );
|
|
$this->sfat = array();
|
|
$small_block = $f_header->getLong( 0x3c );
|
|
if( $small_block != 0xfeffffff )
|
|
{
|
|
$root_entry_index = $this->find_stream( $dir, 'Root Entry');
|
|
if( $root_entry_index < 0 )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->debug("parse_workbook() function dont found Root Entry returns 6");
|
|
return 6;
|
|
}
|
|
$sdc_start_block = $dir->getLong( $root_entry_index * 0x80 + 0x74 );
|
|
$small_data_chain = $this->get_blocks_chain($sdc_start_block);
|
|
$this->max_sblocks = count($small_data_chain) * 8;
|
|
$schain = $this->get_blocks_chain($small_block);
|
|
for( $i = 0; $i < count( $schain ); $i++ )
|
|
{
|
|
$sfatbuf = $dp->get( $schain[$i] * 0x200, 0x200 );
|
|
$sfat = new DataProvider( $sfatbuf, DP_STRING_SOURCE );
|
|
//$this->dbglog->dump( strlen($sfatbuf), "strlen(\$sftabuf)");
|
|
//$this->dbglog->dump( $sfat, "\$sfat");
|
|
if( $sfat->getSize() < 0x200 )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->debug("parse_workbook() function found (strlen($sfat) < 0x200) returns 6");
|
|
return 6;
|
|
}
|
|
for( $j=0; $j<0x80; $j++ )
|
|
$this->sfat[] = $sfat->getLong( $j * 4 );
|
|
$sfat->close();
|
|
unset( $sfatbuf, $sfat );
|
|
}
|
|
unset( $schain );
|
|
$sfcbuf = $dp->ReadFromFat( $small_data_chain );
|
|
$sdp = new DataProvider( $sfcbuf, DP_STRING_SOURCE );
|
|
unset( $sfcbuf, $small_data_chain );
|
|
}
|
|
$workbook_index = $this->find_stream( $dir, 'Workbook' );
|
|
if( $workbook_index<0 )
|
|
{
|
|
$workbook_index = $this->find_stream( $dir, 'Book' );
|
|
if( $workbook_index<0 )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->debug("parse_workbook() function workbook index not found returns 7");
|
|
return 7;
|
|
}
|
|
}
|
|
$workbook_start_block = $dir->getLong( $workbook_index * 0x80 + 0x74 );
|
|
$workbook_length = $dir->getLong( $workbook_index * 0x80 + 0x78 );
|
|
$wb = '';
|
|
if( $workbook_length > 0 )
|
|
{
|
|
if( $workbook_length >= 0x1000 )
|
|
{
|
|
$chain = $this->get_blocks_chain($workbook_start_block);
|
|
$wb = $dp->ReadFromFat( $chain );
|
|
} else {
|
|
$chain = $this->get_blocks_chain($workbook_start_block,true);
|
|
$wb = $sdp->ReadFromFat( $chain, 0x40 );
|
|
unset( $sdp );
|
|
}
|
|
$wb = substr($wb,0,$workbook_length);
|
|
if( strlen($wb) != $workbook_length )
|
|
return 6;
|
|
unset( $chain );
|
|
}
|
|
// Unset fat arrays
|
|
unset( $this->fat, $this->sfat );
|
|
if( strlen($wb) <= 0 )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->debug("parse_workbook() function workbook found (strlen($wb) <= 0) returns 7");
|
|
return 7;
|
|
}
|
|
if( strlen($wb) < 4 )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->debug("parse_workbook() function workbook found (strlen($wb) < 4) returns 6");
|
|
return 6;
|
|
}
|
|
// parse workbook header
|
|
if( strlen($wb) < 256*ord($wb[3])+ord($wb[2]) )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->debug("parse_workbook() function workbook found (strlen($wb) < 256*ord($wb[3])+ord($wb[2])) < 4) returns 6");
|
|
return 6;
|
|
}
|
|
if( ord($wb[0]) != 0x09 )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->debug("parse_workbook() function workbook found (ord($wb[0]) != 0x09) returns 6");
|
|
return 6;
|
|
}
|
|
$vers = ord($wb[1]);
|
|
if( ($vers!=0) && ($vers!=2) && ($vers!=4) && ($vers!=8) )
|
|
{
|
|
return 8;
|
|
}
|
|
if( $vers!=8 )
|
|
$biff_ver = ($ver+4)/2;
|
|
else {
|
|
if( strlen($wb) < 12 ) return 6;
|
|
switch( ord($wb[4])+256*ord($wb[5]) )
|
|
{
|
|
case 0x0500:
|
|
if( ord($wb[0x0a])+256*ord($wb[0x0b]) < 1994 )
|
|
$biff_ver = 5;
|
|
else {
|
|
switch(ord( $wb[8])+256*ord($wb[9]) )
|
|
{
|
|
case 2412:
|
|
case 3218:
|
|
case 3321:
|
|
$biff_ver = 5;
|
|
break;
|
|
default:
|
|
$biff_ver = 7;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 0x0600:
|
|
$biff_ver = 8;
|
|
break;
|
|
default:
|
|
return 8;
|
|
}
|
|
}
|
|
if( $biff_ver < 5 ) return 8;
|
|
$ptr = 0;
|
|
$this->worksheet['offset'] = array();
|
|
$this->worksheet['options'] = array();
|
|
$this->worksheet['unicode'] = array();
|
|
$this->worksheet['name'] = array();
|
|
$this->worksheet['data'] = array();
|
|
$this->format = $this->populateFormat();
|
|
$this->fonts = array();
|
|
$this->fonts[0] = ExcelFont::basicFontRecord();
|
|
$this->xf = array();
|
|
$this->xf['format'] = array();
|
|
$this->xf['font'] = array();
|
|
$this->xf['type_prot'] = array();
|
|
$this->xf['alignment'] = array();
|
|
$this->xf['decoration'] = array();
|
|
$xf_cnt=0;
|
|
$this->sst['unicode'] = array();
|
|
$this->sst['data'] = array();
|
|
$opcode = 0;
|
|
$sst_defined = false;
|
|
$wblen = strlen($wb);
|
|
while( (ord($wb[$ptr])!=0x0a) && ($ptr<$wblen) )
|
|
{
|
|
$oc = ord($wb[$ptr])+256*ord($wb[$ptr+1]);
|
|
if( $oc != 0x3c ) $opcode = $oc;
|
|
switch ($opcode)
|
|
{
|
|
case 0x0085:
|
|
$ofs = ExcelParserUtil::str2long(substr($wb,$ptr+4,4));
|
|
$this->worksheet['offset'][] = $ofs;
|
|
$this->worksheet['options'][] = ord($wb[$ptr+8])+256*ord($wb[$ptr+9]);
|
|
if( $biff_ver==8 )
|
|
{
|
|
$len = ord($wb[$ptr+10]);
|
|
if( (ord($wb[$ptr+11]) & 1) > 0 )
|
|
{
|
|
$this->worksheet['unicode'][] = true;
|
|
$len = $len*2;
|
|
} else {
|
|
$this->worksheet['unicode'][] = false;
|
|
}
|
|
$this->worksheet['name'][] = substr($wb,$ptr+12,$len);
|
|
} else {
|
|
$this->worksheet['unicode'][] = false;
|
|
$len = ord($wb[$ptr+10]);
|
|
$this->worksheet['name'][] = substr($wb,$ptr+11,$len);
|
|
}
|
|
$pws = $this->parse_worksheet(substr($wb,$ofs));
|
|
if( is_array($pws) ) $this->worksheet['data'][] = $pws;
|
|
else return $pws;
|
|
break;
|
|
// Format
|
|
case 0x041e:
|
|
$fidx = ord($wb[$ptr+4])+256*ord($wb[$ptr+5]);
|
|
if($fidx<0x31 ||$fidx==0x31 )
|
|
break;
|
|
elseif($biff_ver>7)
|
|
$this->format[$fidx] = $this->getUnicodeString($wb,$ptr+6);
|
|
break;
|
|
// FONT 0x31
|
|
case EXCEL_FONT_RID:
|
|
$rec = ExcelFont::getFontRecord($wb,$ptr+4);
|
|
$this->fonts[count($this->fonts)] = $rec;
|
|
/*echo str_replace("\n","<br>\n",ExcelFont::toString($rec,count($this->fonts)-1));
|
|
echo "FontRecord<br>" */;
|
|
break;
|
|
// XF
|
|
case 0x00e0:
|
|
$this->xf['font'][$xf_cnt] = ord($wb[$ptr+4])+256*ord($wb[$ptr+5]);
|
|
$this->xf['format'][$xf_cnt] = ord($wb[$ptr+6])+256*ord($wb[$ptr+7]);
|
|
$this->xf['type'][$xf_cnt] = "1";
|
|
$this->xf['bitmask'][$xf_cnt] = "1";
|
|
$xf_cnt++;
|
|
break;
|
|
// SST
|
|
case 0x00fc:
|
|
if( $biff_ver < 8 ) break;
|
|
$sbuflen = ord($wb[$ptr+2]) + 256*ord($wb[$ptr+3]);
|
|
if( $oc != 0x3c )
|
|
{
|
|
if( $sst_defined ) return 6;
|
|
$snum = ExcelParserUtil::str2long(substr($wb,$ptr+8,4));
|
|
$sptr = $ptr+12;
|
|
$sst_defined = true;
|
|
} else {
|
|
if( $rslen > $slen )
|
|
{
|
|
$sptr = $ptr+4;
|
|
$rslen -= $slen;
|
|
$slen = $rslen;
|
|
if( (ord($wb[$sptr]) & 1) > 0 )
|
|
{
|
|
if( $char_bytes == 1 )
|
|
{
|
|
$sstr = '';
|
|
for( $i=0; $i<strlen($str); $i++ )
|
|
$sstr .= $str[$i].chr(0);
|
|
$str = $sstr;
|
|
$char_bytes=2;
|
|
}
|
|
$schar_bytes = 2;
|
|
} else {
|
|
$schar_bytes = 1;
|
|
}
|
|
if( $sptr+$slen*$schar_bytes > $ptr+4+$sbuflen ) $slen = ($ptr+$sbuflen-$sptr+3)/$schar_bytes;
|
|
$sstr = substr($wb,$sptr+1,$slen*$schar_bytes);
|
|
if(($char_bytes == 2) && ($schar_bytes == 1))
|
|
{
|
|
$sstr2 = '';
|
|
for( $i=0; $i<strlen($sstr); $i++ ) $sstr2 .= $sstr[$i].chr(0);
|
|
$sstr = $sstr2;
|
|
}
|
|
$str .= $sstr;
|
|
$sptr += $slen*$schar_bytes+1+4*$rt+$fesz;
|
|
if( $slen < $rslen )
|
|
{
|
|
if( ($sptr >= strlen($wb)) ||($sptr < $ptr+4+$sbuflen)||(ord($wb[$sptr]) != 0x3c) )
|
|
{
|
|
return 6;
|
|
}
|
|
break;
|
|
} else {
|
|
if( $char_bytes == 2 )
|
|
{
|
|
$this->sst['unicode'][] = true;
|
|
} else {
|
|
$this->sst['unicode'][] = false;
|
|
}
|
|
$this->sst['data'][] = $str;
|
|
$snum--;
|
|
}
|
|
} else {
|
|
$sptr = $ptr+4;
|
|
if( $sptr > $ptr ) $sptr += 4*$rt+$fesz;
|
|
}
|
|
}
|
|
while( ($sptr < $ptr+4+$sbuflen) && ($sptr < strlen($wb))&&($snum > 0) )
|
|
{
|
|
$rslen = ord($wb[$sptr])+256*ord($wb[$sptr+1]);
|
|
$slen = $rslen;
|
|
if((ord($wb[$sptr+2]) & 1) > 0 )
|
|
{
|
|
$char_bytes = 2;
|
|
} else {
|
|
$char_bytes = 1;
|
|
}
|
|
$rt = 0;
|
|
$fesz = 0;
|
|
switch (ord($wb[$sptr+2]) & 0x0c)
|
|
{
|
|
// Rich-Text with Far-East
|
|
case 0x0c:
|
|
$rt = ord($wb[$sptr+3])+256*(ord($wb[$sptr+4]));
|
|
$fesz = ExcelParserUtil::str2long(substr($wb,$sptr+5,4));
|
|
if( $sptr+9+$slen*$char_bytes > $ptr+4+$sbuflen )
|
|
$slen = ($ptr+$sbuflen-$sptr-5)/$char_bytes;
|
|
$str = substr($wb,$sptr+9,$slen*$char_bytes);
|
|
$sptr += $slen*$char_bytes+9;
|
|
break;
|
|
// Rich-Text
|
|
case 8:
|
|
$rt = ord($wb[$sptr+3])+256*(ord($wb[$sptr+4]));
|
|
if( $sptr+5+$slen*$char_bytes > $ptr+4+$sbuflen )
|
|
$slen = ($ptr+$sbuflen-$sptr-1)/$char_bytes;
|
|
$str = substr($wb,$sptr+5,$slen*$char_bytes);
|
|
$sptr += $slen*$char_bytes+5;
|
|
break;
|
|
// Far-East
|
|
case 4:
|
|
$fesz = ExcelParserUtil::str2long(substr($wb,$sptr+3,4));
|
|
if( $sptr+7+$slen*$char_bytes > $ptr+4+$sbuflen )
|
|
$slen = ($ptr+$sbuflen-$sptr-3)/$char_bytes;
|
|
$str = substr($wb,$sptr+7,$slen*$char_bytes);
|
|
$sptr += $slen*$char_bytes+7;
|
|
break;
|
|
// Compressed or uncompressed unicode
|
|
case 0:
|
|
if( $sptr+3+$slen*$char_bytes > $ptr+4+$sbuflen )
|
|
$slen = ($ptr+$sbuflen-$sptr+1)/$char_bytes;
|
|
$str = substr($wb,$sptr+3,$slen*$char_bytes);
|
|
$sptr += $slen*$char_bytes+3;
|
|
break;
|
|
}
|
|
if( $slen < $rslen )
|
|
{
|
|
if( ($sptr >= strlen($wb)) ||($sptr < $ptr+4+$sbuflen)||(ord($wb[$sptr]) != 0x3c) ) return 6;
|
|
} else {
|
|
if( $char_bytes == 2 )
|
|
{
|
|
$this->sst['unicode'][] = true;
|
|
} else {
|
|
$this->sst['unicode'][] = false;
|
|
}
|
|
$sptr += 4*$rt+$fesz;
|
|
$this->sst['data'][] = $str;
|
|
$snum--;
|
|
}
|
|
}
|
|
// switch
|
|
break;
|
|
} // switch
|
|
// !!! Optimization:
|
|
// $this->wsb[] = substr($wb,$ptr,4+256*ord($wb[$ptr+3])+ord($wb[$ptr+2]));
|
|
$ptr += 4+256*ord($wb[$ptr+3])+ord($wb[$ptr+2]);
|
|
} // while
|
|
// !!! Optimization:
|
|
// $this->workbook = $wb;
|
|
$this->biff_version = $biff_ver;
|
|
/*DBG*/
|
|
$this->dbglog->debug("parse_workbook() function returns 0");
|
|
return 0;
|
|
}// ParseFromString & ParseFromFile
|
|
//---------------------------------
|
|
//
|
|
// IN:
|
|
//string contents - File contents
|
|
//string filename - File name of an existing Excel file.
|
|
//
|
|
// OUT:
|
|
//0 - success
|
|
//1 - can't open file
|
|
//2 - file too small to be an Excel file
|
|
//3 - error reading header
|
|
//4 - error reading file
|
|
//5 - This is not an Excel file or file stored in < Excel 5.0
|
|
//6 - file corrupted
|
|
//7 - data not found
|
|
//8 - Unsupported file version
|
|
function ParseFromString( $contents )
|
|
{
|
|
$this->dbglog->info("ParseFromString() enter.");
|
|
$this->dp = new DataProvider( $contents, DP_STRING_SOURCE );
|
|
return $this->InitParser();
|
|
}
|
|
function ParseFromFile( $filename )
|
|
{
|
|
$this->dbglog->info("ParseFromFile() enter.");
|
|
$this->dp = new DataProvider( $filename, DP_FILE_SOURCE );
|
|
return $this->InitParser();
|
|
}
|
|
function InitParser()
|
|
{
|
|
$this->dbglog->info("InitParser() enter.");
|
|
if( !$this->dp->isValid() )
|
|
{
|
|
$this->dbglog->error("InitParser() Failed to open file.");
|
|
$this->dbglog->error("InitParser() function returns 1");
|
|
return 1;
|
|
}
|
|
if( $this->dp->getSize() <= 0x200 )
|
|
{
|
|
$this->dbglog->error("InitParser() File too small to be an Excel file.");
|
|
$this->dbglog->error("InitParser() function returns 2");
|
|
return 2;
|
|
}
|
|
$this->max_blocks = $this->dp->getBlocks();
|
|
// read file header
|
|
$hdrbuf = $this->dp->get( 0, 0x200 );
|
|
if( strlen( $hdrbuf ) < 0x200 )
|
|
{
|
|
$this->dbglog->error("InitParser() Error reading header.");
|
|
$this->dbglog->error("InitParser() function returns 3");
|
|
return 3;
|
|
}
|
|
// check file header
|
|
$header_sig = array(0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1);
|
|
for( $i = 0; $i < count($header_sig); $i++ )
|
|
if( $header_sig[$i] != ord( $hdrbuf[$i] ) )
|
|
{
|
|
/*DBG*/
|
|
$this->dbglog->error("InitParser() function founds invalid header");
|
|
/*DBG*/
|
|
$this->dbglog->error("InitParser() function returns 5");
|
|
return 5;
|
|
}
|
|
$f_header = new DataProvider( $hdrbuf, DP_STRING_SOURCE );
|
|
unset( $hdrbuf, $header_sig, $i );
|
|
$this->dp->_baseOfs = 0x200;
|
|
$rc = $this->parse_workbook( $f_header, $this->dp );
|
|
unset( $f_header );
|
|
unset( $this->dp, $this->max_blocks, $this->max_sblocks );
|
|
return $rc;
|
|
}
|
|
}
|
|
|
|
function uc2html($str)
|
|
{
|
|
/* $ret = '';
|
|
for( $i=0; $i<strlen($str)/2; $i++ )
|
|
{
|
|
$charcode = ord($str[$i*2])+256*ord($str[$i*2+1]);
|
|
$ret .= '&#'.$charcode;
|
|
}
|
|
return $ret;
|
|
*/
|
|
return iconv("UTF-16LE","GB2312",$str);
|
|
}
|
|
|
|
//------------------------读取Excel文件
|
|
function Read_Excel_File($ExcelFile,&$result)
|
|
{
|
|
$exc = new ExcelFileParser("", ABC_NO_LOG );
|
|
$res=$exc->ParseFromFile($ExcelFile);
|
|
$result=null;
|
|
switch ($res)
|
|
{
|
|
case 0: break;
|
|
case 1: $err="无法打开文件"; break;
|
|
case 2: $err="文件太小,可能不是Excel文件"; break;
|
|
case 3: $err="文件头读取错误"; break;
|
|
case 4: $err="读取文件时出错"; break;
|
|
case 5: $err="这不是一个Excel文件或者是Excel5.0以前版本文件"; break;
|
|
case 6: $err="文件损坏"; break;
|
|
case 7: $err="在文件中没有发现Excel数据"; break;
|
|
case 8: $err="不支持的文件版本"; break;
|
|
default:
|
|
$err="未知错误"; break;
|
|
}
|
|
|
|
for( $ws_num=0; $ws_num<count($exc->worksheet['name']); $ws_num++ )
|
|
{
|
|
$Sheetname=$exc->worksheet['name'][$ws_num];
|
|
//if($exc->worksheet['unicode'][$ws_num]) $Sheetname=iconv("UTF-16LE","GB2312",$str);
|
|
$ws = $exc->worksheet['data'][$ws_num];
|
|
for( $j=0; $j<=$ws['max_row']; $j++ )
|
|
{
|
|
for( $i=0; $i<=$ws['max_col']; $i++ )
|
|
{
|
|
$data = $ws['cell'][$j][$i];
|
|
switch ($data['type'])
|
|
{
|
|
// string
|
|
case 0:
|
|
$ind = $data['data'];
|
|
if( $exc->sst['unicode'][$ind] )
|
|
{
|
|
$s = uc2html($exc->sst['data'][$ind]);
|
|
} else $s = $exc->sst['data'][$ind];
|
|
if( strlen(trim($s))==0 ) $V="";
|
|
else $V=$s;
|
|
break;
|
|
// integer number
|
|
case 1:
|
|
$V=(int)($data['data']);
|
|
break;
|
|
// float number
|
|
case 2:
|
|
$V=(float)($data['data']);
|
|
break;
|
|
// date
|
|
case 3:
|
|
$ret = $exc->getDateArray($data['data']);
|
|
$V=$ret['year']."-".$ret['month']."-".$ret['day']." ".$ret['hour'];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
$result[$Sheetname][$j][$i]=$V;
|
|
}
|
|
}
|
|
}
|
|
if ($err=='') {return $exc->worksheet;} else {return $err;}
|
|
}
|
|
|
|
|
|
//------------------------建立Excel文件
|
|
function Create_Excel_File($ExcelFile,$Data)
|
|
{
|
|
header ('Content-type: application/x-msexcel');
|
|
header ("Content-Disposition: attachment; filename=$ExcelFile" );
|
|
function xlsBOF()
|
|
{
|
|
echo pack("ssssss", 0x809, 0x8, 0x0, 0x10, 0x0, 0x0);
|
|
return;
|
|
}
|
|
function xlsEOF()
|
|
{
|
|
echo pack("ss", 0x0A, 0x00);
|
|
return;
|
|
}
|
|
function xlsWriteNumber($Row, $Col, $Value)
|
|
{
|
|
echo pack("sssss", 0x203, 14, $Row, $Col, 0x0);
|
|
echo pack("d", $Value);
|
|
return;
|
|
}
|
|
function xlsWriteLabel($Row, $Col, $Value )
|
|
{
|
|
$L = strlen($Value);
|
|
echo pack("ssssss", 0x204, 8 + $L, $Row, $Col, 0x0, $L);
|
|
echo $Value;
|
|
return;
|
|
}
|
|
|
|
xlsBOF();
|
|
for ($i=0;$i<count($Data[0]);$i++)
|
|
{
|
|
for ($j=0;$j<count($Data);$j++)
|
|
{
|
|
$v=$Data[$j][$i];
|
|
xlsWriteLabel($j,$i,$v);
|
|
}
|
|
}
|
|
xlsEOF();
|
|
}
|
|
?>
|