| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308 |
- <?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 SimpleXLS::parseError();
- }
- echo '</pre>';
- */
- class simpleXLS {
- public const BIFF8 = 0x600;
- public const BIFF7 = 0x500;
- public const WORKBOOKGLOBALS = 0x5;
- public const WORKSHEET = 0x10;
- //const TYPE_BOF = 0x809;
- public const TYPE_EOF = 0x0a;
- public const TYPE_BOUNDSHEET = 0x85;
- public const TYPE_DIMENSION = 0x200;
- public const TYPE_ROW = 0x208;
- public const TYPE_DBCELL = 0xd7;
- public const TYPE_FILEPASS = 0x2f;
- //const TYPE_NOTE = 0x1c;
- //const TYPE_TXO = 0x1b6;
- public const TYPE_RK = 0x7e;
- public const TYPE_RK2 = 0x27e;
- public const TYPE_MULRK = 0xbd;
- public const TYPE_MULBLANK = 0xbe;
- //const TYPE_INDEX = 0x20b;
- public const TYPE_SST = 0xfc;
- //const TYPE_EXTSST = 0xff;
- //const TYPE_CONTINUE = 0x3c;
- public const TYPE_LABEL = 0x204;
- public const TYPE_LABELSST = 0xfd;
- public const TYPE_NUMBER = 0x203;
- public const TYPE_NAME = 0x18;
- //const TYPE_ARRAY = 0x221;
- //const TYPE_STRING = 0x207;
- public const TYPE_FORMULA = 0x406;
- public const TYPE_FORMULA2 = 0x6;
- public const TYPE_FORMAT = 0x41e;
- public const TYPE_XF = 0xe0;
- public const TYPE_BOOLERR = 0x205;
- //const TYPE_UNKNOWN = 0xffff;
- public const TYPE_NINETEENFOUR = 0x22;
- public const TYPE_MERGEDCELLS = 0xE5;
- public const TYPE_WINDOW1 = 0x3D;
- //const DEF_NUM_FORMAT = "%.2f";
- public const DEF_NUM_FORMAT = '%s';
- // OLE
- public const NUM_BIG_BLOCK_DEPOT_BLOCKS_POS = 0x2c;
- public const SMALL_BLOCK_DEPOT_BLOCK_POS = 0x3c;
- public const ROOT_START_BLOCK_POS = 0x30;
- public const BIG_BLOCK_SIZE = 0x200;
- public const SMALL_BLOCK_SIZE = 0x40;
- public const EXTENSION_BLOCK_POS = 0x44;
- public const NUM_EXTENSION_BLOCK_POS = 0x48;
- public const PROPERTY_STORAGE_BLOCK_SIZE = 0x80;
- public const BIG_BLOCK_DEPOT_BLOCKS_POS = 0x4c;
- public const SMALL_BLOCK_THRESHOLD = 0x1000;
- // property storage offsets
- public const SIZE_OF_NAME_POS = 0x40;
- public const TYPE_POS = 0x42;
- public const START_BLOCK_POS = 0x74;
- public const SIZE_POS = 0x78;
- /**
- * Array of worksheets found
- *
- * @var array
- * @access public
- */
- public $boundsheets = array();
- public $activeSheet = 0;
- /**
- * 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'
- );
- protected $datetimeFormat = 'Y-m-d H:i:s';
- /**
- * Default encoding
- *
- * @var string
- * @access private
- */
- protected $defaultEncoding = 'UTF-8';
- /**
- * Default number format
- *
- * @var integer
- * @access private
- */
- protected $defaultFormat = self::DEF_NUM_FORMAT;
- /**
- * List of formats to use for each column
- *
- * @var array
- * @access private
- */
- protected $columnsFormat = array();
- protected $nineteenFour;
- protected $multiplier;
- protected $sn;
- protected $curFormat;
- // OLERead
- protected $data;
- protected $bigBlockChain;
- protected $smallBlockChain;
- protected $rootEntry;
- protected $entry;
- protected $props;
- // sergey.shuchkin@gmail.com
- protected $wrkbook; // false - to use excel format
- protected $error = false;
- protected $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(string $filename, bool $isData = false, bool $debug = false)
- {
- $this->debug = $debug;
- $this->_oleread($filename, $isData);
- $this->_parse();
- }
- public static function parseFile($filename, $debug = false)
- {
- return self::parse($filename, false, $debug);
- }
- public static function parseData($data, $debug = false)
- {
- return self::parse($data, true, $debug);
- }
- public static function parse($filename, $isData = false, $debug = false)
- {
- $xls = new self($filename, $isData, $debug);
- if ($xls->success()) {
- return $xls;
- }
- self::parseError($xls->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(): bool
- {
- return ! $this->error;
- }
- public function rows($sheetNum = 0, $limit = 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 ] = $s['cells'][$i][$j] ?? '';
- }
- $result[] = $r;
- $limit--;
- if ($limit === 0) {
- break;
- }
- }
- return $result;
- }
- return false;
- }
- public function rowsEx($sheetNum = 0, $limit = 0): array
- {
- 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 ++) {
- $v = $s['cellsInfo'][$i][$j] ?? array();
- // if ( $v['type'] === self::TYPE_RK || $v['type'] === self::TYPE_RK2 ||
- $v['value'] = $s['cells'][$i][$j] ?? '';
- $r[ $j ] = $v;
- }
- $result[] = $r;
- $limit--;
- if ($limit === 0) {
- break;
- }
- }
- return $result;
- }
- return [];
- }
- public function toHTML($worksheetIndex = 0): string
- {
- $s = '<table class=excel>';
- foreach ($this->rows($worksheetIndex) as $r) {
- $s .= '<tr>';
- foreach ($r as $c) {
- $s .= '<td nowrap>' . ( $c === '' ? ' ' : htmlspecialchars($c, ENT_QUOTES) ) . '</td>';
- }
- $s .= "</tr>\r\n";
- }
- $s .= '</table>';
- return $s;
- }
- public function setDateTimeFormat($value): SimpleXLS
- {
- $this->datetimeFormat = is_string($value) ? $value : false;
- return $this;
- }
- public function sheetNames(): array
- {
- $result = array();
- foreach ($this->boundsheets as $k => $v) {
- $result[ $k ] = $v['name'];
- }
- return $result;
- }
- public function sheetName($index)
- {
- return isset($this->boundsheets[ $index ]) ? $this->boundsheets[ $index ]['name'] : null;
- }
- // }}}
- protected function _oleread($sFileName, $isData = false): bool
- {
- 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 (strpos($this->data, pack('CCCCCCCC', 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1)) !== 0) {
- $this->error('File is not XLS');
- return false;
- }
- $numBigBlockDepotBlocks = $this->_getInt4d(self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
- $sbdStartBlock = $this->_getInt4d(self::SMALL_BLOCK_DEPOT_BLOCK_POS);
- $rootStartBlock = $this->_getInt4d(self::ROOT_START_BLOCK_POS);
- $extensionBlock = $this->_getInt4d(self::EXTENSION_BLOCK_POS);
- $numExtensionBlocks = $this->_getInt4d(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 = $numBigBlockDepotBlocks;
- if ($numExtensionBlocks !== 0) {
- $bbdBlocks = ( self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS ) / 4;
- }
- for ($i = 0; $i < $bbdBlocks; $i ++) {
- $bigBlockDepotBlocks[ $i ] = $this->_getInt4d($pos);
- $pos += 4;
- }
- for ($j = 0; $j < $numExtensionBlocks; $j ++) {
- $pos = ( $extensionBlock + 1 ) * self::BIG_BLOCK_SIZE;
- $blocksToRead = min($numBigBlockDepotBlocks - $bbdBlocks, self::BIG_BLOCK_SIZE / 4 - 1);
- for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; $i ++) {
- $bigBlockDepotBlocks[ $i ] = $this->_getInt4d($pos);
- $pos += 4;
- }
- $bbdBlocks += $blocksToRead;
- if ($bbdBlocks < $numBigBlockDepotBlocks) {
- $extensionBlock = $this->_getInt4d($pos);
- }
- }
- // var_dump($bigBlockDepotBlocks);
- // readBigBlockDepot
- $index = 0;
- $this->bigBlockChain = array();
- for ($i = 0; $i < $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($pos);
- $pos += 4;
- $index ++;
- }
- }
- //var_dump($this->bigBlockChain);
- //echo '=====2';
- // readSmallBlockDepot();
- $index = 0;
- $sbdBlock = $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($pos);
- $pos += 4;
- $index ++;
- }
- $sbdBlock = $this->bigBlockChain[ $sbdBlock ];
- }
- // readData(rootStartBlock)
- $block = $rootStartBlock;
- $this->entry = $this->_readData($block);
- /*
- while ($block != -2) {
- $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
- $this->entry = $this->entry.substr($this->_data, $pos, self::BIG_BLOCK_SIZE);
- $block = $this->bigBlockChain[$block];
- }
- */
- //echo '==='.$this->entry."===";
- $this->_readPropertySets();
- $this->data = $this->_readWorkBook();
- return true;
- }
- // {{{ setOutputEncoding()
- protected function _getInt2d($pos): int
- {
- return ord($this->data[ $pos ]) | ord($this->data[ $pos + 1 ]) << 8;
- // return ($value > 0x7FFFFFFF) ? $value - 0x100000000 : $value;
- }
- protected function _getInt4d($pos): int
- {
- $value = ord($this->data[ $pos ]) | ( ord($this->data[ $pos + 1 ]) << 8 ) | ( ord($this->data[ $pos + 2 ]) << 16 ) | ( ord($this->data[ $pos + 3 ]) << 24 );
- return ($value > 0x7FFFFFFF) ? $value - 0x100000000 : $value;
- }
- // }}}
- // {{{ setRowColOffset()
- protected function _readData($bl): string
- {
- $block = $bl;
- $data = '';
- while ($block !== - 2) {
- $pos = ( $block + 1 ) * self::BIG_BLOCK_SIZE;
- $data .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
- //echo "pos = $pos data=$data\n";
- $block = $this->bigBlockChain[ $block ];
- }
- return $data;
- }
- // }}}
- // {{{ setDefaultFormat()
- protected function _readPropertySets(): void
- {
- $offset = 0;
- //var_dump($this->entry);
- while ($offset < strlen($this->entry)) {
- $d = 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 = ord($d[ self::START_BLOCK_POS]) | ( ord($d[ self::START_BLOCK_POS + 1 ]) << 8 ) | ( ord($d[ self::START_BLOCK_POS + 2 ]) << 16 ) | ( ord($d[ self::START_BLOCK_POS + 3 ]) << 24 );
- $size = ord($d[ self::SIZE_POS]) | ( ord($d[ self::SIZE_POS + 1 ]) << 8 ) | ( ord($d[ self::SIZE_POS + 2 ]) << 16 ) | ( ord($d[ self::SIZE_POS + 3 ]) << 24 );
- $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()
- protected function _readWorkBook(): string
- {
- 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 .= 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 .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
- $block = $this->bigBlockChain[ $block ];
- }
- //echo 'stream'.$streamData;
- return $streamData;
- }
- // }}}
- protected function parseSubstreamHeader($pos): array
- {
- $length = $this->_getInt2d($pos + 2);
- $version = $this->_getInt2d($pos + 4);
- $substreamType = $this->_getInt2d($pos + 6);
- return array( $length, $version, $substreamType );
- }
- // {{{ _parse()
- /**
- * Parse a workbook
- *
- * @access private
- * @return bool
- */
- protected function _parse()
- {
- $pos = 0;
- // $code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8;
- [$length, $version, $substreamType] = $this->parseSubstreamHeader($pos);
- // 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($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 = $this->_getInt2d($spos);
- $spos += 2;
- }
- if ($extendedString) {
- // Read in cchExtRst
- $extendedRunLength = $this->_getInt4d($spos);
- $spos += 4;
- }
- $len = $asciiEncoding ? $numChars : $numChars * 2;
- if ($spos + $len < $limitpos) {
- $retstr = substr($this->data, $spos, $len);
- $spos += $len;
- } else {
- // found countinue
- $retstr = substr($this->data, $spos, $limitpos - $spos);
- $bytesRead = $limitpos - $spos;
- $charsLeft = $numChars - ( $asciiEncoding ? $bytesRead : ( $bytesRead / 2 ) );
- $spos = $limitpos;
- while ($charsLeft > 0) {
- $opcode = $this->_getInt2d($spos);
- $conlength = $this->_getInt2d($spos + 2);
- 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 .= substr($this->data, $spos, $len);
- $charsLeft -= $len;
- $asciiEncoding = true;
- } elseif (! $asciiEncoding && ( $option !== 0 )) {
- $len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength);
- $retstr .= 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 = strlen($retstr); $j < $len_retstr; $j ++) {
- $newstr = $retstr[ $j ] . chr(0);
- }
- $retstr = $newstr;
- $len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength);
- $retstr .= substr($this->data, $spos, $len);
- $charsLeft -= $len / 2;
- $asciiEncoding = false;
- //echo "Izavrat\n";
- }
- $spos += $len;
- }
- }
- $retstr = $asciiEncoding ? $this->_latin1toDef($retstr) : $this->_UTF16toDef($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;
- case self::TYPE_NAME:
- //echo "Type_NAME\n";
- break;
- case self::TYPE_FORMAT:
- $indexCode = $this->_getInt2d($pos + 4);
- if ($version === self::BIFF8) {
- $numchars = $this->_getInt2d($pos + 6);
- if (ord($this->data[ $pos + 8 ]) === 0) { // ascii
- $formatString = substr($this->data, $pos + 9, $numchars);
- $formatString = $this->_latin1toDef($formatString);
- } else {
- $formatString = substr($this->data, $pos + 9, $numchars * 2);
- $formatString = $this->_UTF16toDef($formatString);
- }
- } else {
- $numchars = ord($this->data[ $pos + 6 ]);
- $formatString = substr($this->data, $pos + 7, $numchars * 2);
- $formatString = $this->_latin1toDef($formatString);
- }
- $this->formatRecords[ $indexCode ] = $formatString;
- // echo "Type.FORMAT[$indexCode]=$formatString\n";
- break;
- case self::TYPE_XF:
- $formatstr = '';
- $indexCode = $this->_getInt2d($pos + 6);
- // echo "\nType.XF code=".$indexCode." dateFormat=".$this->dateFormats[ $indexCode ]." numberFormats=".$this->numberFormats[ $indexCode ].PHP_EOL;
- 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 ])) {
- // die( 'L:'.__LINE__ );
- $formatstr = $this->formatRecords[ $indexCode ];
- }
- //echo '.other.';
- // echo "\nfl=".strlen( $formatstr)." fs=$formatstr=\n";
- // echo "\ncode=".$indexCode." fl=".strlen( $formatstr)." fs=$formatstr=\n";
- $fs = str_replace('\\', '', $formatstr);
- if ($fs && preg_match('/^[hmsday\/\-:\., ]+$/i', $fs)) { // found day and time format
- $isdate = true;
- $formatstr = str_replace(array( 'yyyy',':mm','mm','dddd','dd', 'h','ss' ), array('Y',':i','m','l','d', 'H','s' ), $fs);
- }
- }
- if ($isdate) {
- $this->formatRecords['xfrecords'][] = array(
- 'type' => 'date',
- 'format' => $formatstr,
- 'code' => $indexCode
- );
- } else {
- // echo 'fs='.$formatstr.PHP_EOL;
- $this->formatRecords['xfrecords'][] = array(
- 'type' => 'other',
- 'format' => '',
- 'code' => $indexCode
- );
- }
- }
- // echo count( $this->formatRecords['xfrecords'] ).' fs='.$formatstr.' ' . PHP_EOL;
- //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($pos + 4);
- // $rec_typeFlag = ord($this->_data[$pos + 8]);
- $rec_length = ord($this->data[ $pos + 10 ]);
- $hidden = false;
- $rec_name = '';
- if ($version === self::BIFF8) {
- //ord($this->data[$pos + 9])
- $hidden = ord($this->data[$pos + 8]) === 1;
- $chartype = ord($this->data[ $pos + 11 ]);
- if ($chartype === 0) {
- $rec_name = substr($this->data, $pos + 12, $rec_length);
- $rec_name = $this->_latin1toDef($rec_name);
- } else {
- $rec_name = substr($this->data, $pos + 12, $rec_length * 2);
- $rec_name = $this->_UTF16toDef($rec_name);
- }
- } elseif ($version === self::BIFF7) {
- $rec_name = substr($this->data, $pos + 11, $rec_length);
- }
- $this->boundsheets[] = array(
- 'name' => $rec_name,
- 'offset' => $rec_offset,
- 'hidden' => $hidden,
- 'active' => false
- );
- break;
- case self::TYPE_WINDOW1:
- $this->activeSheet = $this->_getInt2d($pos + 14);
- break;
- }
- //echo "Code = ".base_convert($r['code'],10,16)."\n";
- $pos += $length + 4;
- $code = $this->_getInt2d($pos);
- $length = $this->_getInt2d($pos + 2);
- //$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']);
- if ($key === $this->activeSheet) {
- $this->boundsheets[ $key ]['active'] = true;
- }
- }
- return true;
- }
- protected function _latin1toDef($string)
- {
- $result = $string;
- if ($this->defaultEncoding) {
- $result = mb_convert_encoding($string, $this->defaultEncoding, 'ISO-8859-1');
- }
- return $result;
- }
- protected function _UTF16toDef($string)
- {
- $result = $string;
- if ($this->defaultEncoding && $this->defaultEncoding !== 'UTF-16LE') {
- $result = mb_convert_encoding($string, $this->defaultEncoding, 'UTF-16LE');
- }
- return $result;
- }
- protected function _parseSheet($spos): bool
- {
- $cont = true;
- // read BOF
- // $code = ord($this->_data[$spos]) | ord($this->_data[$spos + 1]) << 8;
- [$length, $version, $substreamType] = $this->parseSubstreamHeader($spos);
- if (( $version !== self::BIFF8 ) && ( $version !== self::BIFF7 )) {
- return false;
- }
- if ($substreamType !== self::WORKSHEET) {
- return false;
- }
- //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";
- $this->sheets[ $this->sn ]['maxrow'] = 0;
- $this->sheets[ $this->sn ]['maxcol'] = 0;
- $this->sheets[ $this->sn ]['numRows'] = 0;
- $this->sheets[ $this->sn ]['numCols'] = 0;
- while ($cont) {
- //echo "mem= ".memory_get_usage()."\n";
- // $r = &$this->file->nextRecord();
- $lowcode = ord($this->data[ $spos ]);
- if ($lowcode === self::TYPE_EOF) {
- break;
- }
- $t_code = $lowcode | ord($this->data[ $spos + 1 ]) << 8;
- $length = ord($this->data[ $spos + 2 ]) | ord($this->data[ $spos + 3 ]) << 8;
- $spos += 4;
- //echo "Code=".base_convert($code,10,16)." $code\n";
- $this->multiplier = 1; // need for format with %
- switch ($t_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($spos + 6);
- $numValue = $this->_getIEEE754($rknum);
- //echo $numValue." ";
- $t_alias = 'n';
- if ($this->isDate($spos)) {
- [$string, $raw] = $this->createDate($numValue);
- $t_alias = 'd';
- } 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, $t_code, $t_alias);
- //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($spos + 6);
- //var_dump($this->sst);
- $this->addCell($row, $column, $this->sst[ $index ], $index, $t_code, 's');
- //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;
- $t_alias = 'n';
- for ($i = 0; $i < $columns; $i ++) {
- $numValue = $this->_getIEEE754($this->_getInt4d($tmppos + 2));
- if ($this->isDate($tmppos - 4)) {
- [$string, $raw] = $this->createDate($numValue);
- $t_alias = 'd';
- } 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, $t_code, $t_alias);
- //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', substr($this->data, $spos + 6, 8)); // It machine machine dependent
- $t_alias = 'n';
- if ($this->isDate($spos)) {
- [$string, $raw] = $this->createDate($tmp['double']);
- $t_alias = 'd';
- // $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, $t_code, $t_alias);
- //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', substr($this->data, $spos + 6, 8)); // It machine machine dependent
- if ($this->isDate($spos)) {
- [$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, $t_code, 'f');
- //echo "Number $row $column $string\n";
- }
- break;
- case self::TYPE_BOOLERR:
- $row = $this->_getInt2d($spos);
- $column = $this->_getInt2d($spos + 2);
- $string = ord($this->data[ $spos + 6 ]);
- $this->addCell($row, $column, $string, $string, $t_code, 'b');
- //echo 'Type_BOOLERR '."\n";
- break;
- case self::TYPE_ROW:
- case self::TYPE_DBCELL:
- case self::TYPE_MULBLANK:
- break;
- case self::TYPE_LABEL:
- $row = $this->_getInt2d($spos);
- $column = $this->_getInt2d($spos);
- $string = substr($this->data, $spos + 8, ord($this->data[ $spos + 6 ]) | ord($this->data[ $spos + 7 ]) << 8);
- $this->addCell($row, $column, $string, '', $t_code, 'inlineStr');
- // $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 ($this->sheets[ $this->sn ]['numRows'] === 0) {
- $this->sheets[ $this->sn ]['numRows'] = $this->sheets[ $this->sn ]['maxrow'];
- }
- if ($this->sheets[ $this->sn ]['numCols'] === 0) {
- $this->sheets[ $this->sn ]['numCols'] = $this->sheets[ $this->sn ]['maxcol'];
- }
- return true;
- }
- //}}}
- //{{{ createDate()
- protected 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 / (2 ** (20 - ($exp - 1023)));
- if ($sign) {
- $value = - 1 * $value;
- }
- //end of changes by mmp
- }
- if (( $rknum & 0x01 ) !== 0) {
- $value /= 100;
- }
- return $value;
- }
- protected function isDate($spos): bool
- {
- //$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']." ".$this->formatRecords['xfrecords'][ $xfindex ]['format']."\n";
- if ($this->formatRecords['xfrecords'][ $xfindex ]['type'] === 'date') {
- $this->curFormat = $this->formatRecords['xfrecords'][ $xfindex ]['format'];
- return true;
- }
- if ($this->formatRecords['xfrecords'][ $xfindex ]['type'] === 'number') {
- $this->curFormat = $this->formatRecords['xfrecords'][ $xfindex ]['format'];
- if (strpos($this->curFormat, '%%') !== false) {
- $this->multiplier = 100;
- }
- } else {
- $this->curFormat = $this->defaultFormat;
- }
- 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(int $timevalue): array
- {
- // $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 );
- }
- protected function addCell($row, $col, $string, $raw = '', $type_code = 0, $type_alias = ''): void
- {
- //echo "ADD cel $row-$col $string\n";
- $this->sheets[ $this->sn ]['maxrow'] = max($this->sheets[ $this->sn ]['maxrow'], $row);
- $this->sheets[ $this->sn ]['maxcol'] = max($this->sheets[ $this->sn ]['maxcol'], $col);
- $this->sheets[ $this->sn ]['cells'][ $row ][ $col ] = $string;
- if ($raw) {
- $this->sheets[ $this->sn ]['cellsInfo'][ $row ][ $col ]['raw'] = $raw;
- }
- if ($type_code) {
- $this->sheets[$this->sn]['cellsInfo'][$row][$col]['type'] = $type_code;
- $this->sheets[$this->sn]['cellsInfo'][$row][$col]['t'] = $type_alias;
- }
- }
- protected function createNumber($spos)
- {
- $rknumhigh = $this->_getInt4d($spos + 10);
- $rknumlow = $this->_getInt4d($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 / (2 ** (20 - ($exp - 1023)));
- if ($mantissalow1 !== 0) {
- $value += 1 / (2 ** (21 - ($exp - 1023)));
- }
- $value += $mantissalow2 / (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(string $encoding): SimpleXLS
- {
- $this->defaultEncoding = $encoding;
- return $this;
- }
- /**
- * Set the default number format, ex. "%.2f"
- *
- * @access public
- *
- * @param string $sFormat Default format
- */
- public function setDefaultFormat(string $sFormat): SimpleXLS
- {
- $this->defaultFormat = $sFormat;
- return $this;
- }
- /**
- * Force a column to use a certain format
- *
- * @access public
- *
- * @param integer $column Column number
- * @param string $sFormat Format
- */
- public function setColumnFormat(int $column, string $sFormat): SimpleXLS
- {
- $this->columnsFormat[ $column ] = $sFormat;
- return $this;
- }
- }
|