| 1 |
<?php |
| 2 |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| 3 |
|
| 4 |
/** |
| 5 |
* A class for reading Microsoft Excel Spreadsheets. |
| 6 |
* |
| 7 |
* Originally developed by Vadim Tkachenko under the name PHPExcelReader. |
| 8 |
* (http://sourceforge.net/projects/phpexcelreader) |
| 9 |
* Based on the Java version by Andy Khan (http://www.andykhan.com). Now |
| 10 |
* maintained by David Sanders. Reads only Biff 7 and Biff 8 formats. |
| 11 |
* |
| 12 |
* PHP versions 4 and 5 |
| 13 |
* |
| 14 |
* LICENSE: This source file is subject to version 3.0 of the PHP license |
| 15 |
* that is available through the world-wide-web at the following URI: |
| 16 |
* http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| 17 |
* the PHP License and are unable to obtain it through the web, please |
| 18 |
* send a note to license@php.net so we can mail you a copy immediately. |
| 19 |
* |
| 20 |
* @category Spreadsheet |
| 21 |
* @package Spreadsheet_Excel_Reader |
| 22 |
* @author Vadim Tkachenko <vt@apachephp.com> |
| 23 |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| 24 |
* @version CVS: $Id: reader.php 19 2007-03-13 12:42:41Z shangxiao $ |
| 25 |
* @link http://pear.php.net/package/Spreadsheet_Excel_Reader |
| 26 |
* @see OLE, Spreadsheet_Excel_Writer |
| 27 |
*/ |
| 28 |
|
| 29 |
|
| 30 |
//require_once 'PEAR.php'; |
| 31 |
//require_once 'Spreadsheet/Excel/Reader/OLERead.php'; |
| 32 |
//require_once 'OLE.php'; |
| 33 |
require_once "oleread.inc"; |
| 34 |
|
| 35 |
define('SPREADSHEET_EXCEL_READER_BIFF8', 0x600); |
| 36 |
define('SPREADSHEET_EXCEL_READER_BIFF7', 0x500); |
| 37 |
define('SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS', 0x5); |
| 38 |
define('SPREADSHEET_EXCEL_READER_WORKSHEET', 0x10); |
| 39 |
|
| 40 |
define('SPREADSHEET_EXCEL_READER_TYPE_BOF', 0x809); |
| 41 |
define('SPREADSHEET_EXCEL_READER_TYPE_EOF', 0x0a); |
| 42 |
define('SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET', 0x85); |
| 43 |
define('SPREADSHEET_EXCEL_READER_TYPE_DIMENSION', 0x200); |
| 44 |
define('SPREADSHEET_EXCEL_READER_TYPE_ROW', 0x208); |
| 45 |
define('SPREADSHEET_EXCEL_READER_TYPE_DBCELL', 0xd7); |
| 46 |
define('SPREADSHEET_EXCEL_READER_TYPE_FILEPASS', 0x2f); |
| 47 |
define('SPREADSHEET_EXCEL_READER_TYPE_NOTE', 0x1c); |
| 48 |
define('SPREADSHEET_EXCEL_READER_TYPE_TXO', 0x1b6); |
| 49 |
define('SPREADSHEET_EXCEL_READER_TYPE_RK', 0x7e); |
| 50 |
define('SPREADSHEET_EXCEL_READER_TYPE_RK2', 0x27e); |
| 51 |
define('SPREADSHEET_EXCEL_READER_TYPE_MULRK', 0xbd); |
| 52 |
define('SPREADSHEET_EXCEL_READER_TYPE_MULBLANK', 0xbe); |
| 53 |
define('SPREADSHEET_EXCEL_READER_TYPE_INDEX', 0x20b); |
| 54 |
define('SPREADSHEET_EXCEL_READER_TYPE_SST', 0xfc); |
| 55 |
define('SPREADSHEET_EXCEL_READER_TYPE_EXTSST', 0xff); |
| 56 |
define('SPREADSHEET_EXCEL_READER_TYPE_CONTINUE', 0x3c); |
| 57 |
define('SPREADSHEET_EXCEL_READER_TYPE_LABEL', 0x204); |
| 58 |
define('SPREADSHEET_EXCEL_READER_TYPE_LABELSST', 0xfd); |
| 59 |
define('SPREADSHEET_EXCEL_READER_TYPE_NUMBER', 0x203); |
| 60 |
define('SPREADSHEET_EXCEL_READER_TYPE_NAME', 0x18); |
| 61 |
define('SPREADSHEET_EXCEL_READER_TYPE_ARRAY', 0x221); |
| 62 |
define('SPREADSHEET_EXCEL_READER_TYPE_STRING', 0x207); |
| 63 |
define('SPREADSHEET_EXCEL_READER_TYPE_FORMULA', 0x406); |
| 64 |
define('SPREADSHEET_EXCEL_READER_TYPE_FORMULA2', 0x6); |
| 65 |
define('SPREADSHEET_EXCEL_READER_TYPE_FORMAT', 0x41e); |
| 66 |
define('SPREADSHEET_EXCEL_READER_TYPE_XF', 0xe0); |
| 67 |
define('SPREADSHEET_EXCEL_READER_TYPE_BOOLERR', 0x205); |
| 68 |
define('SPREADSHEET_EXCEL_READER_TYPE_UNKNOWN', 0xffff); |
| 69 |
define('SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR', 0x22); |
| 70 |
define('SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS', 0xE5); |
| 71 |
|
| 72 |
define('SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS' , 25569); |
| 73 |
define('SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904', 24107); |
| 74 |
define('SPREADSHEET_EXCEL_READER_MSINADAY', 86400); |
| 75 |
//define('SPREADSHEET_EXCEL_READER_MSINADAY', 24 * 60 * 60); |
| 76 |
|
| 77 |
//define('SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT', "%.2f"); |
| 78 |
define('SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT', "%s"); |
| 79 |
|
| 80 |
|
| 81 |
/* |
| 82 |
* Place includes, constant defines and $_GLOBAL settings here. |
| 83 |
* Make sure they have appropriate docblocks to avoid phpDocumentor |
| 84 |
* construing they are documented by the page-level docblock. |
| 85 |
*/ |
| 86 |
|
| 87 |
/** |
| 88 |
* A class for reading Microsoft Excel Spreadsheets. |
| 89 |
* |
| 90 |
* Originally developed by Vadim Tkachenko under the name PHPExcelReader. |
| 91 |
* (http://sourceforge.net/projects/phpexcelreader) |
| 92 |
* Based on the Java version by Andy Khan (http://www.andykhan.com). Now |
| 93 |
* maintained by David Sanders. Reads only Biff 7 and Biff 8 formats. |
| 94 |
* |
| 95 |
* @category Spreadsheet |
| 96 |
* @package Spreadsheet_Excel_Reader |
| 97 |
* @author Vadim Tkachenko <vt@phpapache.com> |
| 98 |
* @copyright 1997-2005 The PHP Group |
| 99 |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| 100 |
* @version Release: @package_version@ |
| 101 |
* @link http://pear.php.net/package/PackageName |
| 102 |
* @see OLE, Spreadsheet_Excel_Writer |
| 103 |
*/ |
| 104 |
class Spreadsheet_Excel_Reader |
| 105 |
{ |
| 106 |
/** |
| 107 |
* Array of worksheets found |
| 108 |
* |
| 109 |
* @var array |
| 110 |
* @access public |
| 111 |
*/ |
| 112 |
var $boundsheets = array(); |
| 113 |
|
| 114 |
/** |
| 115 |
* Array of format records found |
| 116 |
* |
| 117 |
* @var array |
| 118 |
* @access public |
| 119 |
*/ |
| 120 |
var $formatRecords = array(); |
| 121 |
|
| 122 |
/** |
| 123 |
* todo |
| 124 |
* |
| 125 |
* @var array |
| 126 |
* @access public |
| 127 |
*/ |
| 128 |
var $sst = array(); |
| 129 |
|
| 130 |
/** |
| 131 |
* Array of worksheets |
| 132 |
* |
| 133 |
* The data is stored in 'cells' and the meta-data is stored in an array |
| 134 |
* called 'cellsInfo' |
| 135 |
* |
| 136 |
* Example: |
| 137 |
* |
| 138 |
* $sheets --> 'cells' --> row --> column --> Interpreted value |
| 139 |
* --> 'cellsInfo' --> row --> column --> 'type' - Can be 'date', 'number', or 'unknown' |
| 140 |
* --> 'raw' - The raw data that Excel stores for that data cell |
| 141 |
* |
| 142 |
* @var array |
| 143 |
* @access public |
| 144 |
*/ |
| 145 |
var $sheets = array(); |
| 146 |
|
| 147 |
/** |
| 148 |
* The data returned by OLE |
| 149 |
* |
| 150 |
* @var string |
| 151 |
* @access public |
| 152 |
*/ |
| 153 |
var $data; |
| 154 |
|
| 155 |
/** |
| 156 |
* OLE object for reading the file |
| 157 |
* |
| 158 |
* @var OLE object |
| 159 |
* @access private |
| 160 |
*/ |
| 161 |
var $_ole; |
| 162 |
|
| 163 |
/** |
| 164 |
* Default encoding |
| 165 |
* |
| 166 |
* @var string |
| 167 |
* @access private |
| 168 |
*/ |
| 169 |
var $_defaultEncoding; |
| 170 |
|
| 171 |
/** |
| 172 |
* Default number format |
| 173 |
* |
| 174 |
* @var integer |
| 175 |
* @access private |
| 176 |
*/ |
| 177 |
var $_defaultFormat = SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT; |
| 178 |
|
| 179 |
/** |
| 180 |
* todo |
| 181 |
* List of formats to use for each column |
| 182 |
* |
| 183 |
* @var array |
| 184 |
* @access private |
| 185 |
*/ |
| 186 |
var $_columnsFormat = array(); |
| 187 |
|
| 188 |
/** |
| 189 |
* todo |
| 190 |
* |
| 191 |
* @var integer |
| 192 |
* @access private |
| 193 |
*/ |
| 194 |
var $_rowoffset = 1; |
| 195 |
|
| 196 |
/** |
| 197 |
* todo |
| 198 |
* |
| 199 |
* @var integer |
| 200 |
* @access private |
| 201 |
*/ |
| 202 |
var $_coloffset = 1; |
| 203 |
|
| 204 |
/** |
| 205 |
* List of default date formats used by Excel |
| 206 |
* |
| 207 |
* @var array |
| 208 |
* @access public |
| 209 |
*/ |
| 210 |
var $dateFormats = array ( |
| 211 |
0xe => "d/m/Y", |
| 212 |
0xf => "d-M-Y", |
| 213 |
0x10 => "d-M", |
| 214 |
0x11 => "M-Y", |
| 215 |
0x12 => "h:i a", |
| 216 |
0x13 => "h:i:s a", |
| 217 |
0x14 => "H:i", |
| 218 |
0x15 => "H:i:s", |
| 219 |
0x16 => "d/m/Y H:i", |
| 220 |
0x2d => "i:s", |
| 221 |
0x2e => "H:i:s", |
| 222 |
0x2f => "i:s.S"); |
| 223 |
|
| 224 |
/** |
| 225 |
* Default number formats used by Excel |
| 226 |
* |
| 227 |
* @var array |
| 228 |
* @access public |
| 229 |
*/ |
| 230 |
var $numberFormats = array( |
| 231 |
0x1 => "%1.0f", // "0" |
| 232 |
0x2 => "%1.2f", // "0.00", |
| 233 |
0x3 => "%1.0f", //"#,##0", |
| 234 |
0x4 => "%1.2f", //"#,##0.00", |
| 235 |
0x5 => "%1.0f", /*"$#,##0;($#,##0)",*/ |
| 236 |
0x6 => '$%1.0f', /*"$#,##0;($#,##0)",*/ |
| 237 |
0x7 => '$%1.2f', //"$#,##0.00;($#,##0.00)", |
| 238 |
0x8 => '$%1.2f', //"$#,##0.00;($#,##0.00)", |
| 239 |
0x9 => '%1.0f%%', // "0%" |
| 240 |
0xa => '%1.2f%%', // "0.00%" |
| 241 |
0xb => '%1.2f', // 0.00E00", |
| 242 |
0x25 => '%1.0f', // "#,##0;(#,##0)", |
| 243 |
0x26 => '%1.0f', //"#,##0;(#,##0)", |
| 244 |
0x27 => '%1.2f', //"#,##0.00;(#,##0.00)", |
| 245 |
0x28 => '%1.2f', //"#,##0.00;(#,##0.00)", |
| 246 |
0x29 => '%1.0f', //"#,##0;(#,##0)", |
| 247 |
0x2a => '$%1.0f', //"$#,##0;($#,##0)", |
| 248 |
0x2b => '%1.2f', //"#,##0.00;(#,##0.00)", |
| 249 |
0x2c => '$%1.2f', //"$#,##0.00;($#,##0.00)", |
| 250 |
0x30 => '%1.0f'); //"##0.0E0"; |
| 251 |
|
| 252 |
// }}} |
| 253 |
// {{{ Spreadsheet_Excel_Reader() |
| 254 |
|
| 255 |
/** |
| 256 |
* Constructor |
| 257 |
* |
| 258 |
* Some basic initialisation |
| 259 |
*/ |
| 260 |
function Spreadsheet_Excel_Reader() |
| 261 |
{ |
| 262 |
$this->_ole =& new OLERead(); |
| 263 |
$this->setUTFEncoder('iconv'); |
| 264 |
} |
| 265 |
|
| 266 |
// }}} |
| 267 |
// {{{ setOutputEncoding() |
| 268 |
|
| 269 |
/** |
| 270 |
* Set the encoding method |
| 271 |
* |
| 272 |
* @param string Encoding to use |
| 273 |
* @access public |
| 274 |
*/ |
| 275 |
function setOutputEncoding($encoding) |
| 276 |
{ |
| 277 |
$this->_defaultEncoding = $encoding; |
| 278 |
} |
| 279 |
|
| 280 |
// }}} |
| 281 |
// {{{ setUTFEncoder() |
| 282 |
|
| 283 |
/** |
| 284 |
* $encoder = 'iconv' or 'mb' |
| 285 |
* set iconv if you would like use 'iconv' for encode UTF-16LE to your encoding |
| 286 |
* set mb if you would like use 'mb_convert_encoding' for encode UTF-16LE to your encoding |
| 287 |
* |
| 288 |
* @access public |
| 289 |
* @param string Encoding type to use. Either 'iconv' or 'mb' |
| 290 |
*/ |
| 291 |
function setUTFEncoder($encoder = 'iconv') |
| 292 |
{ |
| 293 |
$this->_encoderFunction = ''; |
| 294 |
|
| 295 |
if ($encoder == 'iconv') { |
| 296 |
$this->_encoderFunction = function_exists('iconv') ? 'iconv' : ''; |
| 297 |
} elseif ($encoder == 'mb') { |
| 298 |
$this->_encoderFunction = function_exists('mb_convert_encoding') ? |
| 299 |
'mb_convert_encoding' : |
| 300 |
''; |
| 301 |
} |
| 302 |
} |
| 303 |
|
| 304 |
// }}} |
| 305 |
// {{{ setRowColOffset() |
| 306 |
|
| 307 |
/** |
| 308 |
* todo |
| 309 |
* |
| 310 |
* @access public |
| 311 |
* @param offset |
| 312 |
*/ |
| 313 |
function setRowColOffset($iOffset) |
| 314 |
{ |
| 315 |
$this->_rowoffset = $iOffset; |
| 316 |
$this->_coloffset = $iOffset; |
| 317 |
} |
| 318 |
|
| 319 |
// }}} |
| 320 |
// {{{ setDefaultFormat() |
| 321 |
|
| 322 |
/** |
| 323 |
* Set the default number format |
| 324 |
* |
| 325 |
* @access public |
| 326 |
* @param Default format |
| 327 |
*/ |
| 328 |
function setDefaultFormat($sFormat) |
| 329 |
{ |
| 330 |
$this->_defaultFormat = $sFormat; |
| 331 |
} |
| 332 |
|
| 333 |
// }}} |
| 334 |
// {{{ setColumnFormat() |
| 335 |
|
| 336 |
/** |
| 337 |
* Force a column to use a certain format |
| 338 |
* |
| 339 |
* @access public |
| 340 |
* @param integer Column number |
| 341 |
* @param string Format |
| 342 |
*/ |
| 343 |
function setColumnFormat($column, $sFormat) |
| 344 |
{ |
| 345 |
$this->_columnsFormat[$column] = $sFormat; |
| 346 |
} |
| 347 |
|
| 348 |
|
| 349 |
// }}} |
| 350 |
// {{{ read() |
| 351 |
|
| 352 |
/** |
| 353 |
* Read the spreadsheet file using OLE, then parse |
| 354 |
* |
| 355 |
* @access public |
| 356 |
* @param filename |
| 357 |
* @todo return a valid value |
| 358 |
*/ |
| 359 |
function read($sFileName) |
| 360 |
{ |
| 361 |
/* |
| 362 |
require_once 'OLE.php'; |
| 363 |
$ole = new OLE(); |
| 364 |
$ole->read($sFileName); |
| 365 |
|
| 366 |
foreach ($ole->_list as $i => $pps) { |
| 367 |
if (($pps->Name == 'Workbook' || $pps->Name == 'Book') && |
| 368 |
$pps->Size >= SMALL_BLOCK_THRESHOLD) { |
| 369 |
|
| 370 |
$this->data = $ole->getData($i, 0, $ole->getDataLength($i)); |
| 371 |
} elseif ($pps->Name == 'Root Entry') { |
| 372 |
$this->data = $ole->getData($i, 0, $ole->getDataLength($i)); |
| 373 |
} |
| 374 |
//var_dump(strlen($ole->getData($i, 0, $ole->getDataLength($i))), $pps->Name, md5($this->data), $ole->getDataLength($i)); |
| 375 |
} |
| 376 |
//exit; |
| 377 |
$this->_parse(); |
| 378 |
|
| 379 |
return sizeof($this->sheets) > 0; |
| 380 |
*/ |
| 381 |
|
| 382 |
$res = $this->_ole->read($sFileName); |
| 383 |
|
| 384 |
// oops, something goes wrong (Darko Miljanovic) |
| 385 |
if($res === false) { |
| 386 |
// check error code |
| 387 |
if($this->_ole->error == 1) { |
| 388 |
// bad file |
| 389 |
die('The filename ' . $sFileName . ' is not readable'); |
| 390 |
} |
| 391 |
// check other error codes here (eg bad fileformat, etc...) |
| 392 |
} |
| 393 |
|
| 394 |
$this->data = $this->_ole->getWorkBook(); |
| 395 |
|
| 396 |
|
| 397 |
/* |
| 398 |
$res = $this->_ole->read($sFileName); |
| 399 |
|
| 400 |
if ($this->isError($res)) { |
| 401 |
// var_dump($res); |
| 402 |
return $this->raiseError($res); |
| 403 |
} |
| 404 |
|
| 405 |
$total = $this->_ole->ppsTotal(); |
| 406 |
for ($i = 0; $i < $total; $i++) { |
| 407 |
if ($this->_ole->isFile($i)) { |
| 408 |
$type = unpack("v", $this->_ole->getData($i, 0, 2)); |
| 409 |
if ($type[''] == 0x0809) { // check if it's a BIFF stream |
| 410 |
$this->_index = $i; |
| 411 |
$this->data = $this->_ole->getData($i, 0, $this->_ole->getDataLength($i)); |
| 412 |
break; |
| 413 |
} |
| 414 |
} |
| 415 |
} |
| 416 |
|
| 417 |
if ($this->_index === null) { |
| 418 |
return $this->raiseError("$file doesn't seem to be an Excel file"); |
| 419 |
} |
| 420 |
|
| 421 |
*/ |
| 422 |
|
| 423 |
//echo "data =".$this->data; |
| 424 |
//$this->readRecords(); |
| 425 |
$this->_parse(); |
| 426 |
} |
| 427 |
|
| 428 |
|
| 429 |
// }}} |
| 430 |
// {{{ _parse() |
| 431 |
|
| 432 |
/** |
| 433 |
* Parse a workbook |
| 434 |
* |
| 435 |
* @access private |
| 436 |
* @return bool |
| 437 |
*/ |
| 438 |
function _parse() |
| 439 |
{ |
| 440 |
$pos = 0; |
| 441 |
|
| 442 |
$code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8; |
| 443 |
$length = ord($this->data[$pos+2]) | ord($this->data[$pos+3])<<8; |
| 444 |
|
| 445 |
$version = ord($this->data[$pos + 4]) | ord($this->data[$pos + 5])<<8; |
| 446 |
$substreamType = ord($this->data[$pos + 6]) | ord($this->data[$pos + 7])<<8; |
| 447 |
//echo "Start parse code=".base_convert($code,10,16)." version=".base_convert($version,10,16)." substreamType=".base_convert($substreamType,10,16).""."\n"; |
| 448 |
|
| 449 |
if (($version != SPREADSHEET_EXCEL_READER_BIFF8) && |
| 450 |
($version != SPREADSHEET_EXCEL_READER_BIFF7)) { |
| 451 |
return false; |
| 452 |
} |
| 453 |
|
| 454 |
if ($substreamType != SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS){ |
| 455 |
return false; |
| 456 |
} |
| 457 |
|
| 458 |
//print_r($rec); |
| 459 |
$pos += $length + 4; |
| 460 |
|
| 461 |
$code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8; |
| 462 |
$length = ord($this->data[$pos+2]) | ord($this->data[$pos+3])<<8; |
| 463 |
|
| 464 |
while ($code != SPREADSHEET_EXCEL_READER_TYPE_EOF) { |
| 465 |
switch ($code) { |
| 466 |
case SPREADSHEET_EXCEL_READER_TYPE_SST: |
| 467 |
//echo "Type_SST\n"; |
| 468 |
$spos = $pos + 4; |
| 469 |
$limitpos = $spos + $length; |
| 470 |
$uniqueStrings = $this->_GetInt4d($this->data, $spos+4); |
| 471 |
$spos += 8; |
| 472 |
for ($i = 0; $i < $uniqueStrings; $i++) { |
| 473 |
// Read in the number of characters |
| 474 |
if ($spos == $limitpos) { |
| 475 |
$opcode = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| 476 |
$conlength = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| 477 |
if ($opcode != 0x3c) { |
| 478 |
return -1; |
| 479 |
} |
| 480 |
$spos += 4; |
| 481 |
$limitpos = $spos + $conlength; |
| 482 |
} |
| 483 |
$numChars = ord($this->data[$spos]) | (ord($this->data[$spos+1]) << 8); |
| 484 |
//echo "i = $i pos = $pos numChars = $numChars "; |
| 485 |
$spos += 2; |
| 486 |
$optionFlags = ord($this->data[$spos]); |
| 487 |
$spos++; |
| 488 |
$asciiEncoding = (($optionFlags & 0x01) == 0) ; |
| 489 |
$extendedString = ( ($optionFlags & 0x04) != 0); |
| 490 |
|
| 491 |
// See if string contains formatting information |
| 492 |
$richString = ( ($optionFlags & 0x08) != 0); |
| 493 |
|
| 494 |
if ($richString) { |
| 495 |
// Read in the crun |
| 496 |
$formattingRuns = ord($this->data[$spos]) | (ord($this->data[$spos+1]) << 8); |
| 497 |
$spos += 2; |
| 498 |
} |
| 499 |
|
| 500 |
if ($extendedString) { |
| 501 |
// Read in cchExtRst |
| 502 |
$extendedRunLength = $this->_GetInt4d($this->data, $spos); |
| 503 |
$spos += 4; |
| 504 |
} |
| 505 |
|
| 506 |
$len = ($asciiEncoding)? $numChars : $numChars*2; |
| 507 |
if ($spos + $len < $limitpos) { |
| 508 |
$retstr = substr($this->data, $spos, $len); |
| 509 |
$spos += $len; |
| 510 |
}else{ |
| 511 |
// found countinue |
| 512 |
$retstr = substr($this->data, $spos, $limitpos - $spos); |
| 513 |
$bytesRead = $limitpos - $spos; |
| 514 |
$charsLeft = $numChars - (($asciiEncoding) ? $bytesRead : ($bytesRead / 2)); |
| 515 |
$spos = $limitpos; |
| 516 |
|
| 517 |
while ($charsLeft > 0){ |
| 518 |
$opcode = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| 519 |
$conlength = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| 520 |
if ($opcode != 0x3c) { |
| 521 |
return -1; |
| 522 |
} |
| 523 |
$spos += 4; |
| 524 |
$limitpos = $spos + $conlength; |
| 525 |
$option = ord($this->data[$spos]); |
| 526 |
$spos += 1; |
| 527 |
if ($asciiEncoding && ($option == 0)) { |
| 528 |
$len = min($charsLeft, $limitpos - $spos); // min($charsLeft, $conlength); |
| 529 |
$retstr .= substr($this->data, $spos, $len); |
| 530 |
$charsLeft -= $len; |
| 531 |
$asciiEncoding = true; |
| 532 |
}elseif (!$asciiEncoding && ($option != 0)){ |
| 533 |
$len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength); |
| 534 |
$retstr .= substr($this->data, $spos, $len); |
| 535 |
$charsLeft -= $len/2; |
| 536 |
$asciiEncoding = false; |
| 537 |
}elseif (!$asciiEncoding && ($option == 0)) { |
| 538 |
// Bummer - the string starts off as Unicode, but after the |
| 539 |
// continuation it is in straightforward ASCII encoding |
| 540 |
$len = min($charsLeft, $limitpos - $spos); // min($charsLeft, $conlength); |
| 541 |
for ($j = 0; $j < $len; $j++) { |
| 542 |
$retstr .= $this->data[$spos + $j].chr(0); |
| 543 |
} |
| 544 |
$charsLeft -= $len; |
| 545 |
$asciiEncoding = false; |
| 546 |
}else{ |
| 547 |
$newstr = ''; |
| 548 |
for ($j = 0; $j < strlen($retstr); $j++) { |
| 549 |
$newstr = $retstr[$j].chr(0); |
| 550 |
} |
| 551 |
$retstr = $newstr; |
| 552 |
$len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength); |
| 553 |
$retstr .= substr($this->data, $spos, $len); |
| 554 |
$charsLeft -= $len/2; |
| 555 |
$asciiEncoding = false; |
| 556 |
//echo "Izavrat\n"; |
| 557 |
} |
| 558 |
$spos += $len; |
| 559 |
|
| 560 |
} |
| 561 |
} |
| 562 |
$retstr = ($asciiEncoding) ? $retstr : $this->_encodeUTF16($retstr); |
| 563 |
// echo "Str $i = $retstr\n"; |
| 564 |
if ($richString){ |
| 565 |
$spos += 4 * $formattingRuns; |
| 566 |
} |
| 567 |
|
| 568 |
// For extended strings, skip over the extended string data |
| 569 |
if ($extendedString) { |
| 570 |
$spos += $extendedRunLength; |
| 571 |
} |
| 572 |
//if ($retstr == 'Derby'){ |
| 573 |
// echo "bb\n"; |
| 574 |
//} |
| 575 |
$this->sst[]=$retstr; |
| 576 |
} |
| 577 |
/*$continueRecords = array(); |
| 578 |
while ($this->getNextCode() == Type_CONTINUE) { |
| 579 |
$continueRecords[] = &$this->nextRecord(); |
| 580 |
} |
| 581 |
//echo " 1 Type_SST\n"; |
| 582 |
$this->shareStrings = new SSTRecord($r, $continueRecords); |
| 583 |
//print_r($this->shareStrings->strings); |
| 584 |
*/ |
| 585 |
// echo 'SST read: '.($time_end-$time_start)."\n"; |
| 586 |
break; |
| 587 |
|
| 588 |
case SPREADSHEET_EXCEL_READER_TYPE_FILEPASS: |
| 589 |
return false; |
| 590 |
break; |
| 591 |
case SPREADSHEET_EXCEL_READER_TYPE_NAME: |
| 592 |
//echo "Type_NAME\n"; |
| 593 |
break; |
| 594 |
case SPREADSHEET_EXCEL_READER_TYPE_FORMAT: |
| 595 |
$indexCode = ord($this->data[$pos+4]) | ord($this->data[$pos+5]) << 8; |
| 596 |
|
| 597 |
if ($version == SPREADSHEET_EXCEL_READER_BIFF8) { |
| 598 |
$numchars = ord($this->data[$pos+6]) | ord($this->data[$pos+7]) << 8; |
| 599 |
if (ord($this->data[$pos+8]) == 0){ |
| 600 |
$formatString = substr($this->data, $pos+9, $numchars); |
| 601 |
} else { |
| 602 |
$formatString = substr($this->data, $pos+9, $numchars*2); |
| 603 |
} |
| 604 |
} else { |
| 605 |
$numchars = ord($this->data[$pos+6]); |
| 606 |
$formatString = substr($this->data, $pos+7, $numchars*2); |
| 607 |
} |
| 608 |
|
| 609 |
$this->formatRecords[$indexCode] = $formatString; |
| 610 |
// echo "Type.FORMAT\n"; |
| 611 |
break; |
| 612 |
case SPREADSHEET_EXCEL_READER_TYPE_XF: |
| 613 |
//global $dateFormats, $numberFormats; |
| 614 |
$indexCode = ord($this->data[$pos+6]) | ord($this->data[$pos+7]) << 8; |
| 615 |
//echo "\nType.XF ".count($this->formatRecords['xfrecords'])." $indexCode "; |
| 616 |
if (array_key_exists($indexCode, $this->dateFormats)) { |
| 617 |
//echo "isdate ".$dateFormats[$indexCode]; |
| 618 |
$this->formatRecords['xfrecords'][] = array( |
| 619 |
'type' => 'date', |
| 620 |
'format' => $this->dateFormats[$indexCode] |
| 621 |
); |
| 622 |
}elseif (array_key_exists($indexCode, $this->numberFormats)) { |
| 623 |
//echo "isnumber ".$this->numberFormats[$indexCode]; |
| 624 |
$this->formatRecords['xfrecords'][] = array( |
| 625 |
'type' => 'number', |
| 626 |
'format' => $this->numberFormats[$indexCode] |
| 627 |
); |
| 628 |
}else{ |
| 629 |
$isdate = FALSE; |
| 630 |
if ($indexCode > 0){ |
| 631 |
if (isset($this->formatRecords[$indexCode])) |
| 632 |
$formatstr = $this->formatRecords[$indexCode]; |
| 633 |
//echo '.other.'; |
| 634 |
//echo "\ndate-time=$formatstr=\n"; |
| 635 |
if ($formatstr) |
| 636 |
if (preg_match("/[^hmsday\/\-:\s]/i", $formatstr) == 0) { // found day and time format |
| 637 |
$isdate = TRUE; |
| 638 |
$formatstr = str_replace('mm', 'i', $formatstr); |
| 639 |
$formatstr = str_replace('h', 'H', $formatstr); |
| 640 |
//echo "\ndate-time $formatstr \n"; |
| 641 |
} |
| 642 |
} |
| 643 |
|
| 644 |
if ($isdate){ |
| 645 |
$this->formatRecords['xfrecords'][] = array( |
| 646 |
'type' => 'date', |
| 647 |
'format' => $formatstr, |
| 648 |
); |
| 649 |
}else{ |
| 650 |
$this->formatRecords['xfrecords'][] = array( |
| 651 |
'type' => 'other', |
| 652 |
'format' => '', |
| 653 |
'code' => $indexCode |
| 654 |
); |
| 655 |
} |
| 656 |
} |
| 657 |
//echo "\n"; |
| 658 |
break; |
| 659 |
case SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR: |
| 660 |
//echo "Type.NINETEENFOUR\n"; |
| 661 |
$this->nineteenFour = (ord($this->data[$pos+4]) == 1); |
| 662 |
break; |
| 663 |
case SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET: |
| 664 |
//echo "Type.BOUNDSHEET\n"; |
| 665 |
$rec_offset = $this->_GetInt4d($this->data, $pos+4); |
| 666 |
$rec_typeFlag = ord($this->data[$pos+8]); |
| 667 |
$rec_visibilityFlag = ord($this->data[$pos+9]); |
| 668 |
$rec_length = ord($this->data[$pos+10]); |
| 669 |
|
| 670 |
if ($version == SPREADSHEET_EXCEL_READER_BIFF8){ |
| 671 |
$chartype = ord($this->data[$pos+11]); |
| 672 |
if ($chartype == 0){ |
| 673 |
$rec_name = substr($this->data, $pos+12, $rec_length); |
| 674 |
} else { |
| 675 |
$rec_name = $this->_encodeUTF16(substr($this->data, $pos+12, $rec_length*2)); |
| 676 |
} |
| 677 |
}elseif ($version == SPREADSHEET_EXCEL_READER_BIFF7){ |
| 678 |
$rec_name = substr($this->data, $pos+11, $rec_length); |
| 679 |
} |
| 680 |
$this->boundsheets[] = array('name'=>$rec_name, |
| 681 |
'offset'=>$rec_offset); |
| 682 |
|
| 683 |
break; |
| 684 |
|
| 685 |
} |
| 686 |
|
| 687 |
//echo "Code = ".base_convert($r['code'],10,16)."\n"; |
| 688 |
$pos += $length + 4; |
| 689 |
$code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8; |
| 690 |
$length = ord($this->data[$pos+2]) | ord($this->data[$pos+3])<<8; |
| 691 |
|
| 692 |
//$r = &$this->nextRecord(); |
| 693 |
//echo "1 Code = ".base_convert($r['code'],10,16)."\n"; |
| 694 |
} |
| 695 |
|
| 696 |
foreach ($this->boundsheets as $key=>$val){ |
| 697 |
$this->sn = $key; |
| 698 |
$this->_parsesheet($val['offset']); |
| 699 |
} |
| 700 |
return true; |
| 701 |
|
| 702 |
} |
| 703 |
|
| 704 |
/** |
| 705 |
* Parse a worksheet |
| 706 |
* |
| 707 |
* @access private |
| 708 |
* @param todo |
| 709 |
* @todo fix return codes |
| 710 |
*/ |
| 711 |
function _parsesheet($spos) |
| 712 |
{ |
| 713 |
$cont = true; |
| 714 |
// read BOF |
| 715 |
$code = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| 716 |
$length = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| 717 |
|
| 718 |
$version = ord($this->data[$spos + 4]) | ord($this->data[$spos + 5])<<8; |
| 719 |
$substreamType = ord($this->data[$spos + 6]) | ord($this->data[$spos + 7])<<8; |
| 720 |
|
| 721 |
if (($version != SPREADSHEET_EXCEL_READER_BIFF8) && ($version != SPREADSHEET_EXCEL_READER_BIFF7)) { |
| 722 |
return -1; |
| 723 |
} |
| 724 |
|
| 725 |
if ($substreamType != SPREADSHEET_EXCEL_READER_WORKSHEET){ |
| 726 |
return -2; |
| 727 |
} |
| 728 |
//echo "Start parse code=".base_convert($code,10,16)." version=".base_convert($version,10,16)." substreamType=".base_convert($substreamType,10,16).""."\n"; |
| 729 |
$spos += $length + 4; |
| 730 |
//var_dump($this->formatRecords); |
| 731 |
//echo "code $code $length"; |
| 732 |
while($cont) { |
| 733 |
//echo "mem= ".memory_get_usage()."\n"; |
| 734 |
// $r = &$this->file->nextRecord(); |
| 735 |
$lowcode = ord($this->data[$spos]); |
| 736 |
if ($lowcode == SPREADSHEET_EXCEL_READER_TYPE_EOF) break; |
| 737 |
$code = $lowcode | ord($this->data[$spos+1])<<8; |
| 738 |
$length = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| 739 |
$spos += 4; |
| 740 |
$this->sheets[$this->sn]['maxrow'] = $this->_rowoffset - 1; |
| 741 |
$this->sheets[$this->sn]['maxcol'] = $this->_coloffset - 1; |
| 742 |
//echo "Code=".base_convert($code,10,16)." $code\n"; |
| 743 |
unset($this->rectype); |
| 744 |
$this->multiplier = 1; // need for format with % |
| 745 |
switch ($code) { |
| 746 |
case SPREADSHEET_EXCEL_READER_TYPE_DIMENSION: |
| 747 |
//echo 'Type_DIMENSION '; |
| 748 |
if (!isset($this->numRows)) { |
| 749 |
if (($length == 10) || ($version == SPREADSHEET_EXCEL_READER_BIFF7)){ |
| 750 |
$this->sheets[$this->sn]['numRows'] = ord($this->data[$spos+2]) | ord($this->data[$spos+3]) << 8; |
| 751 |
$this->sheets[$this->sn]['numCols'] = ord($this->data[$spos+6]) | ord($this->data[$spos+7]) << 8; |
| 752 |
} else { |
| 753 |
$this->sheets[$this->sn]['numRows'] = ord($this->data[$spos+4]) | ord($this->data[$spos+5]) << 8; |
| 754 |
$this->sheets[$this->sn]['numCols'] = ord($this->data[$spos+10]) | ord($this->data[$spos+11]) << 8; |
| 755 |
} |
| 756 |
} |
| 757 |
//echo 'numRows '.$this->numRows.' '.$this->numCols."\n"; |
| 758 |
break; |
| 759 |
case SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS: |
| 760 |
$cellRanges = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| 761 |
for ($i = 0; $i < $cellRanges; $i++) { |
| 762 |
$fr = ord($this->data[$spos + 8*$i + 2]) | ord($this->data[$spos + 8*$i + 3])<<8; |
| 763 |
$lr = ord($this->data[$spos + 8*$i + 4]) | ord($this->data[$spos + 8*$i + 5])<<8; |
| 764 |
$fc = ord($this->data[$spos + 8*$i + 6]) | ord($this->data[$spos + 8*$i + 7])<<8; |
| 765 |
$lc = ord($this->data[$spos + 8*$i + 8]) | ord($this->data[$spos + 8*$i + 9])<<8; |
| 766 |
//$this->sheets[$this->sn]['mergedCells'][] = array($fr + 1, $fc + 1, $lr + 1, $lc + 1); |
| 767 |
if ($lr - $fr > 0) { |
| 768 |
$this->sheets[$this->sn]['cellsInfo'][$fr+1][$fc+1]['rowspan'] = $lr - $fr + 1; |
| 769 |
} |
| 770 |
if ($lc - $fc > 0) { |
| 771 |
$this->sheets[$this->sn]['cellsInfo'][$fr+1][$fc+1]['colspan'] = $lc - $fc + 1; |
| 772 |
} |
| 773 |
} |
| 774 |
//echo "Merged Cells $cellRanges $lr $fr $lc $fc\n"; |
| 775 |
break; |
| 776 |
case SPREADSHEET_EXCEL_READER_TYPE_RK: |
| 777 |
case SPREADSHEET_EXCEL_READER_TYPE_RK2: |
| 778 |
//echo 'SPREADSHEET_EXCEL_READER_TYPE_RK'."\n"; |
| 779 |
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| 780 |
$column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| 781 |
$rknum = $this->_GetInt4d($this->data, $spos + 6); |
| 782 |
$numValue = $this->_GetIEEE754($rknum); |
| 783 |
//echo $numValue." "; |
| 784 |
if ($this->isDate($spos)) { |
| 785 |
list($string, $raw) = $this->createDate($numValue); |
| 786 |
}else{ |
| 787 |
$raw = $numValue; |
| 788 |
if (isset($this->_columnsFormat[$column + 1])){ |
| 789 |
$this->curformat = $this->_columnsFormat[$column + 1]; |
| 790 |
} |
| 791 |
$string = sprintf($this->curformat, $numValue * $this->multiplier); |
| 792 |
//$this->addcell(RKRecord($r)); |
| 793 |
} |
| 794 |
$this->addcell($row, $column, $string, $raw); |
| 795 |
//echo "Type_RK $row $column $string $raw {$this->curformat}\n"; |
| 796 |
break; |
| 797 |
case SPREADSHEET_EXCEL_READER_TYPE_LABELSST: |
| 798 |
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| 799 |
$column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| 800 |
$xfindex = ord($this->data[$spos+4]) | ord($this->data[$spos+5])<<8; |
| 801 |
$index = $this->_GetInt4d($this->data, $spos + 6); |
| 802 |
//var_dump($this->sst); |
| 803 |
$this->addcell($row, $column, $this->sst[$index]); |
| 804 |
//echo "LabelSST $row $column $string\n"; |
| 805 |
break; |
| 806 |
case SPREADSHEET_EXCEL_READER_TYPE_MULRK: |
| 807 |
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| 808 |
$colFirst = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| 809 |
$colLast = ord($this->data[$spos + $length - 2]) | ord($this->data[$spos + $length - 1])<<8; |
| 810 |
$columns = $colLast - $colFirst + 1; |
| 811 |
$tmppos = $spos+4; |
| 812 |
for ($i = 0; $i < $columns; $i++) { |
| 813 |
$numValue = $this->_GetIEEE754($this->_GetInt4d($this->data, $tmppos + 2)); |
| 814 |
if ($this->isDate($tmppos-4)) { |
| 815 |
list($string, $raw) = $this->createDate($numValue); |
| 816 |
}else{ |
| 817 |
$raw = $numValue; |
| 818 |
if (isset($this->_columnsFormat[$colFirst + $i + 1])){ |
| 819 |
$this->curformat = $this->_columnsFormat[$colFirst + $i + 1]; |
| 820 |
} |
| 821 |
$string = sprintf($this->curformat, $numValue * $this->multiplier); |
| 822 |
} |
| 823 |
//$rec['rknumbers'][$i]['xfindex'] = ord($rec['data'][$pos]) | ord($rec['data'][$pos+1]) << 8; |
| 824 |
$tmppos += 6; |
| 825 |
$this->addcell($row, $colFirst + $i, $string, $raw); |
| 826 |
//echo "MULRK $row ".($colFirst + $i)." $string\n"; |
| 827 |
} |
| 828 |
//MulRKRecord($r); |
| 829 |
// Get the individual cell records from the multiple record |
| 830 |
//$num = ; |
| 831 |
|
| 832 |
break; |
| 833 |
case SPREADSHEET_EXCEL_READER_TYPE_NUMBER: |
| 834 |
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| 835 |
$column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| 836 |
$tmp = unpack("ddouble", substr($this->data, $spos + 6, 8)); // It machine machine dependent |
| 837 |
if ($this->isDate($spos)) { |
| 838 |
list($string, $raw) = $this->createDate($tmp['double']); |
| 839 |
// $this->addcell(DateRecord($r, 1)); |
| 840 |
}else{ |
| 841 |
//$raw = $tmp['']; |
| 842 |
if (isset($this->_columnsFormat[$column + 1])){ |
| 843 |
$this->curformat = $this->_columnsFormat[$column + 1]; |
| 844 |
} |
| 845 |
$raw = $this->createNumber($spos); |
| 846 |
$string = sprintf($this->curformat, $raw * $this->multiplier); |
| 847 |
|
| 848 |
// $this->addcell(NumberRecord($r)); |
| 849 |
} |
| 850 |
$this->addcell($row, $column, $string, $raw); |
| 851 |
//echo "Number $row $column $string\n"; |
| 852 |
break; |
| 853 |
case SPREADSHEET_EXCEL_READER_TYPE_FORMULA: |
| 854 |
case SPREADSHEET_EXCEL_READER_TYPE_FORMULA2: |
| 855 |
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| 856 |
$column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| 857 |
if ((ord($this->data[$spos+6])==0) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) { |
| 858 |
//String formula. Result follows in a STRING record |
| 859 |
//echo "FORMULA $row $column Formula with a string<br>\n"; |
| 860 |
} elseif ((ord($this->data[$spos+6])==1) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) { |
| 861 |
//Boolean formula. Result is in +2; 0=false,1=true |
| 862 |
} elseif ((ord($this->data[$spos+6])==2) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) { |
| 863 |
//Error formula. Error code is in +2; |
| 864 |
} elseif ((ord($this->data[$spos+6])==3) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) { |
| 865 |
//Formula result is a null string. |
| 866 |
} else { |
| 867 |
// result is a number, so first 14 bytes are just like a _NUMBER record |
| 868 |
$tmp = unpack("ddouble", substr($this->data, $spos + 6, 8)); // It machine machine dependent |
| 869 |
if ($this->isDate($spos)) { |
| 870 |
list($string, $raw) = $this->createDate($tmp['double']); |
| 871 |
// $this->addcell(DateRecord($r, 1)); |
| 872 |
}else{ |
| 873 |
//$raw = $tmp['']; |
| 874 |
if (isset($this->_columnsFormat[$column + 1])){ |
| 875 |
$this->curformat = $this->_columnsFormat[$column + 1]; |
| 876 |
} |
| 877 |
$raw = $this->createNumber($spos); |
| 878 |
$string = sprintf($this->curformat, $raw * $this->multiplier); |
| 879 |
|
| 880 |
// $this->addcell(NumberRecord($r)); |
| 881 |
} |
| 882 |
$this->addcell($row, $column, $string, $raw); |
| 883 |
//echo "Number $row $column $string\n"; |
| 884 |
} |
| 885 |
break; |
| 886 |
case SPREADSHEET_EXCEL_READER_TYPE_BOOLERR: |
| 887 |
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| 888 |
$column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| 889 |
$string = ord($this->data[$spos+6]); |
| 890 |
$this->addcell($row, $column, $string); |
| 891 |
//echo 'Type_BOOLERR '."\n"; |
| 892 |
break; |
| 893 |
case SPREADSHEET_EXCEL_READER_TYPE_ROW: |
| 894 |
case SPREADSHEET_EXCEL_READER_TYPE_DBCELL: |
| 895 |
case SPREADSHEET_EXCEL_READER_TYPE_MULBLANK: |
| 896 |
break; |
| 897 |
case SPREADSHEET_EXCEL_READER_TYPE_LABEL: |
| 898 |
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; |
| 899 |
$column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; |
| 900 |
$this->addcell($row, $column, substr($this->data, $spos + 8, ord($this->data[$spos + 6]) | ord($this->data[$spos + 7])<<8)); |
| 901 |
|
| 902 |
// $this->addcell(LabelRecord($r)); |
| 903 |
break; |
| 904 |
|
| 905 |
case SPREADSHEET_EXCEL_READER_TYPE_EOF: |
| 906 |
$cont = false; |
| 907 |
break; |
| 908 |
default: |
| 909 |
//echo ' unknown :'.base_convert($r['code'],10,16)."\n"; |
| 910 |
break; |
| 911 |
|
| 912 |
} |
| 913 |
$spos += $length; |
| 914 |
} |
| 915 |
|
| 916 |
if (!isset($this->sheets[$this->sn]['numRows'])) |
| 917 |
$this->sheets[$this->sn]['numRows'] = $this->sheets[$this->sn]['maxrow']; |
| 918 |
if (!isset($this->sheets[$this->sn]['numCols'])) |
| 919 |
$this->sheets[$this->sn]['numCols'] = $this->sheets[$this->sn]['maxcol']; |
| 920 |
|
| 921 |
} |
| 922 |
|
| 923 |
/** |
| 924 |
* Check whether the current record read is a date |
| 925 |
* |
| 926 |
* @param todo |
| 927 |
* @return boolean True if date, false otherwise |
| 928 |
*/ |
| 929 |
function isDate($spos) |
| 930 |
{ |
| 931 |
//$xfindex = GetInt2d(, 4); |
| 932 |
$xfindex = ord($this->data[$spos+4]) | ord($this->data[$spos+5]) << 8; |
| 933 |
//echo 'check is date '.$xfindex.' '.$this->formatRecords['xfrecords'][$xfindex]['type']."\n"; |
| 934 |
//var_dump($this->formatRecords['xfrecords'][$xfindex]); |
| 935 |
if ($this->formatRecords['xfrecords'][$xfindex]['type'] == 'date') { |
| 936 |
$this->curformat = $this->formatRecords['xfrecords'][$xfindex]['format']; |
| 937 |
$this->rectype = 'date'; |
| 938 |
return true; |
| 939 |
} else { |
| 940 |
if ($this->formatRecords['xfrecords'][$xfindex]['type'] == 'number') { |
| 941 |
$this->curformat = $this->formatRecords['xfrecords'][$xfindex]['format']; |
| 942 |
$this->rectype = 'number'; |
| 943 |
if (($xfindex == 0x9) || ($xfindex == 0xa)){ |
| 944 |
$this->multiplier = 100; |
| 945 |
} |
| 946 |
}else{ |
| 947 |
$this->curformat = $this->_defaultFormat; |
| 948 |
$this->rectype = 'unknown'; |
| 949 |
} |
| 950 |
return false; |
| 951 |
} |
| 952 |
} |
| 953 |
|
| 954 |
//}}} |
| 955 |
//{{{ createDate() |
| 956 |
|
| 957 |
/** |
| 958 |
* Convert the raw Excel date into a human readable format |
| 959 |
* |
| 960 |
* Dates in Excel are stored as number of seconds from an epoch. On |
| 961 |
* Windows, the epoch is 30/12/1899 and on Mac it's 01/01/1904 |
| 962 |
* |
| 963 |
* @access private |
| 964 |
* @param integer The raw Excel value to convert |
| 965 |
* @return array First element is the converted date, the second element is number a unix timestamp |
| 966 |
*/ |
| 967 |
function createDate($numValue) |
| 968 |
{ |
| 969 |
if ($numValue > 1) { |
| 970 |
$utcDays = $numValue - ($this->nineteenFour ? SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904 : SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS); |
| 971 |
$utcValue = round(($utcDays+1) * SPREADSHEET_EXCEL_READER_MSINADAY); |
| 972 |
$string = date ($this->curformat, $utcValue); |
| 973 |
$raw = $utcValue; |
| 974 |
} else { |
| 975 |
$raw = $numValue; |
| 976 |
$hours = floor($numValue * 24); |
| 977 |
$mins = floor($numValue * 24 * 60) - $hours * 60; |
| 978 |
$secs = floor($numValue * SPREADSHEET_EXCEL_READER_MSINADAY) - $hours * 60 * 60 - $mins * 60; |
| 979 |
$string = date ($this->curformat, mktime($hours, $mins, $secs)); |
| 980 |
} |
| 981 |
|
| 982 |
return array($string, $raw); |
| 983 |
} |
| 984 |
|
| 985 |
function createNumber($spos) |
| 986 |
{ |
| 987 |
$rknumhigh = $this->_GetInt4d($this->data, $spos + 10); |
| 988 |
$rknumlow = $this->_GetInt4d($this->data, $spos + 6); |
| 989 |
//for ($i=0; $i<8; $i++) { echo ord($this->data[$i+$spos+6]) . " "; } echo "<br>"; |
| 990 |
$sign = ($rknumhigh & 0x80000000) >> 31; |
| 991 |
$exp = ($rknumhigh & 0x7ff00000) >> 20; |
| 992 |
$mantissa = (0x100000 | ($rknumhigh & 0x000fffff)); |
| 993 |
$mantissalow1 = ($rknumlow & 0x80000000) >> 31; |
| 994 |
$mantissalow2 = ($rknumlow & 0x7fffffff); |
| 995 |
$value = $mantissa / pow( 2 , (20- ($exp - 1023))); |
| 996 |
if ($mantissalow1 != 0) $value += 1 / pow (2 , (21 - ($exp - 1023))); |
| 997 |
$value += $mantissalow2 / pow (2 , (52 - ($exp - 1023))); |
| 998 |
//echo "Sign = $sign, Exp = $exp, mantissahighx = $mantissa, mantissalow1 = $mantissalow1, mantissalow2 = $mantissalow2<br>\n"; |
| 999 |
if ($sign) {$value = -1 * $value;} |
| 1000 |
return $value; |
| 1001 |
} |
| 1002 |
|
| 1003 |
function addcell($row, $col, $string, $raw = '') |
| 1004 |
{ |
| 1005 |
//echo "ADD cel $row-$col $string\n"; |
| 1006 |
$this->sheets[$this->sn]['maxrow'] = max($this->sheets[$this->sn]['maxrow'], $row + $this->_rowoffset); |
| 1007 |
$this->sheets[$this->sn]['maxcol'] = max($this->sheets[$this->sn]['maxcol'], $col + $this->_coloffset); |
| 1008 |
$this->sheets[$this->sn]['cells'][$row + $this->_rowoffset][$col + $this->_coloffset] = $string; |
| 1009 |
if ($raw) |
| 1010 |
$this->sheets[$this->sn]['cellsInfo'][$row + $this->_rowoffset][$col + $this->_coloffset]['raw'] = $raw; |
| 1011 |
if (isset($this->rectype)) |
| 1012 |
$this->sheets[$this->sn]['cellsInfo'][$row + $this->_rowoffset][$col + $this->_coloffset]['type'] = $this->rectype; |
| 1013 |
|
| 1014 |
} |
| 1015 |
|
| 1016 |
|
| 1017 |
function _GetIEEE754($rknum) |
| 1018 |
{ |
| 1019 |
if (($rknum & 0x02) != 0) { |
| 1020 |
$value = $rknum >> 2; |
| 1021 |
} else { |
| 1022 |
//mmp |
| 1023 |
// first comment out the previously existing 7 lines of code here |
| 1024 |
// $tmp = unpack("d", pack("VV", 0, ($rknum & 0xfffffffc))); |
| 1025 |
// //$value = $tmp['']; |
| 1026 |
// if (array_key_exists(1, $tmp)) { |
| 1027 |
// $value = $tmp[1]; |
| 1028 |
// } else { |
| 1029 |
// $value = $tmp['']; |
| 1030 |
// } |
| 1031 |
// I got my info on IEEE754 encoding from |
| 1032 |
// http://research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html |
| 1033 |
// The RK format calls for using only the most significant 30 bits of the |
| 1034 |
// 64 bit floating point value. The other 34 bits are assumed to be 0 |
| 1035 |
// So, we use the upper 30 bits of $rknum as follows... |
| 1036 |
$sign = ($rknum & 0x80000000) >> 31; |
| 1037 |
$exp = ($rknum & 0x7ff00000) >> 20; |
| 1038 |
$mantissa = (0x100000 | ($rknum & 0x000ffffc)); |
| 1039 |
$value = $mantissa / pow( 2 , (20- ($exp - 1023))); |
| 1040 |
if ($sign) {$value = -1 * $value;} |
| 1041 |
//end of changes by mmp |
| 1042 |
|
| 1043 |
} |
| 1044 |
|
| 1045 |
if (($rknum & 0x01) != 0) { |
| 1046 |
$value /= 100; |
| 1047 |
} |
| 1048 |
return $value; |
| 1049 |
} |
| 1050 |
|
| 1051 |
function _encodeUTF16($string) |
| 1052 |
{ |
| 1053 |
$result = $string; |
| 1054 |
if ($this->_defaultEncoding){ |
| 1055 |
switch ($this->_encoderFunction){ |
| 1056 |
case 'iconv' : $result = iconv('UTF-16LE', $this->_defaultEncoding, $string); |
| 1057 |
break; |
| 1058 |
case 'mb_convert_encoding' : $result = mb_convert_encoding($string, $this->_defaultEncoding, 'UTF-16LE' ); |
| 1059 |
break; |
| 1060 |
} |
| 1061 |
} |
| 1062 |
return $result; |
| 1063 |
} |
| 1064 |
|
| 1065 |
function _GetInt4d($data, $pos) |
| 1066 |
{ |
| 1067 |
$value = ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | (ord($data[$pos+3]) << 24); |
| 1068 |
if ($value>=4294967294) |
| 1069 |
{ |
| 1070 |
$value=-2; |
| 1071 |
} |
| 1072 |
return $value; |
| 1073 |
} |
| 1074 |
|
| 1075 |
} |
| 1076 |
|
| 1077 |
/* |
| 1078 |
* Local variables: |
| 1079 |
* tab-width: 4 |
| 1080 |
* c-basic-offset: 4 |
| 1081 |
* c-hanging-comment-ender-p: nil |
| 1082 |
* End: |
| 1083 |
*/ |
| 1084 |
|
| 1085 |
?> |
| 1086 |
|