Advertisement
krot

ABC Excel Parser Pro

Nov 3rd, 2017
279
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 45.96 KB | None | 0 0
  1. <?php
  2.  
  3. //------------------------------------------------------------------------
  4. // ABC Excel Parser Pro (Debug class)
  5. //
  6. // Version: 4.5
  7. // PHP compatibility: 4.3.x
  8. // Copyright (c) 2002 - 2004 Zakkis Technology, Inc.
  9. // All rights reserved.
  10. //
  11. // This script parses a binary Excel file and store all data in an array.
  12. // For more information see README.TXT file included in this distribution.
  13. //
  14. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  15. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  16. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  17. // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  18. // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  19. // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  20. // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  22. // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  23. // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  24. // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  25. // OF THE POSSIBILITY OF SUCH DAMAGE.
  26. //
  27. //------------------------------------------------------------------------
  28.  
  29. error_reporting (0);
  30.  
  31. define('ABC_CRITICAL',      0);
  32. define('ABC_ERROR',         1);
  33. define('ABC_ALERT',         2);
  34. define('ABC_WARNING',       3);
  35. define('ABC_NOTICE',        4);
  36. define('ABC_INFO',          5);
  37. define('ABC_DEBUG',         6);
  38. define('ABC_TRACE',         7);
  39. define('ABC_VAR_DUMP',      8);
  40. define('ABC_NO_LOG',      -1);
  41.  
  42. $php_version = split( "\.", phpversion() );
  43.  
  44. if( $php_version[0] == 4 && $php_version[1] <= 1 ) {
  45.     if( !function_exists('var_export') ) {
  46.         function var_export( $exp, $ret ) {
  47.                 ob_start();
  48.                 var_dump( $exp );
  49.                 $result = ob_get_contents();
  50.                 ob_end_clean();
  51.                 return $result;
  52.         }
  53.     }
  54. }
  55.  
  56. function print_bt()
  57. {
  58.     print "<code>\n";
  59.     $cs = debug_backtrace();
  60.     for( $i = 1; $i < count($cs) ; $i++ )
  61.     {
  62.         $item = $cs[ $i ];
  63.        
  64.         for( $j = 0; $j < count($item['args']); $j++ )
  65.             if( is_string($item['args'][$j]) )
  66.                 $item['args'][$j] = "\"" . $item['args'][$j] . "\"";
  67.         $args = join(",", $item['args'] );
  68.            
  69.         if( isset( $item['class'] ) )
  70.             $str = sprintf("%s(%d): %s%s%s(%s)",
  71.                 $item['file'],
  72.                 $item['line'],
  73.                 $item['class'],
  74.                 $item['type'],
  75.                 $item['function'],
  76.                 $args );
  77.         else
  78.             $str = sprintf("%s(%d): %s(%s)",
  79.                 $item['file'],
  80.                 $item['line'],
  81.                 $item['function'],
  82.                 $args );
  83.         echo $str . "<br>\n";
  84.     }
  85.     print "</code>\n";
  86. }
  87.  
  88. function _die( $str )
  89. {
  90.     print "Выполнение скрипта остановлено по причине: $str<br>\n";
  91.     print_bt();
  92.     exit();
  93. }
  94.  
  95. class DebugOut
  96. {
  97.  
  98. var $priorities = array(ABC_CRITICAL    => 'Критический',
  99.                         ABC_ERROR       => 'Ошибка',
  100.                         ABC_ALERT       => 'Предупреждение',
  101.                         ABC_WARNING     => 'Внимание',
  102.                         ABC_NOTICE      => 'Уведомление',
  103.                         ABC_INFO        => 'Информация',
  104.                         ABC_DEBUG       => 'Отладка',
  105.                         ABC_TRACE       => 'Трассировка',
  106.                         ABC_VAR_DUMP        => 'Дамп'
  107.                         );
  108. var $_ready = false;
  109.  
  110. var $_currentPriority = ABC_DEBUG;
  111.  
  112. var $_consumers = array();
  113.  
  114. var  $_filename;
  115. var  $_fp;
  116. var  $_logger_name;
  117.  
  118.  
  119.  function DebugOut($name, $logger_name, $level ){
  120.      $this->_filename = $name;
  121.      $this->_currentPriority = $level;
  122.      $this->_logger_name = $logger_name;
  123.      if ($level > ABC_NO_LOG){
  124.         $this->_openfile();
  125.      }
  126.  
  127.      /*Регистрация деструктора*/
  128.      register_shutdown_function(array($this,"close"));
  129.  }
  130.  
  131.  
  132.  
  133.  function log($message, $priority = ABC_INFO) {
  134.         // Прерывает обработку если $priority выше максимального уровня.
  135.         if ($priority > $this->_currentPriority) {
  136.             return false;
  137.         }
  138.         // Добавьте к массиву loglines
  139.         return $this->_writeLine($message, $priority, strftime('%b %d %H:%M:%S'));
  140.  }
  141.  
  142.  function dump($variable,$name) {
  143.        $priority = ABC_VAR_DUMP;
  144.        if ($priority > $this->_currentPriority ) {
  145.             return false;
  146.        }
  147.        $time = strftime('%b %d %H:%M:%S');
  148.        $message = var_export($variable,true);
  149.        return fwrite($this->_fp,
  150.                      sprintf("%s %s [%s] variable %s = %s \r\n",
  151.                              $time,
  152.                              $this->_logger_name,
  153.                              $this->priorities[$priority],
  154.                              $name,
  155.                              $message)
  156.                              );
  157.  }
  158.  
  159.  function info($message) {
  160.         return $this->log($message, ABC_INFO);
  161.  }
  162.  
  163.  function debug($message) {
  164.         return $this->log($message, ABC_DEBUG);
  165.  }
  166.  
  167.  function notice($message) {
  168.         return $this->log($message, ABC_NOTICE);
  169.  }
  170.  
  171.  function warning($message) {
  172.         return $this->log($message, ABC_WARNING);
  173.  }
  174.  
  175.  function trace($message) {
  176.         return $this->log($message, ABC_TRACE);
  177.  }
  178.  
  179.  function error($message) {
  180.         return $this->log($message, ABC_ERROR);
  181.  }
  182.  
  183.  
  184.  
  185.  /**
  186.   * Пишет линию в  logfile
  187.   *
  188.   * @param  string $line      Линия, чтобы писать
  189.   * @param  integer $priority Приоритет этой линии / сообщения
  190.   * @return integer           НЧисло записанных байтов или -1 если ошибка
  191.   * @access private
  192.   */
  193.  function _writeLine($message, $priority, $time) {
  194.     if( fwrite($this->_fp, sprintf("%s %s [%s] %s\r\n", $time, $this->_logger_name, $this->priorities[$priority], $message)) ) {
  195.         return fflush($this->_fp);
  196.     } else {
  197.         return false;
  198.     }
  199.  }
  200.  
  201.  function _openfile() {
  202.     if (($this->_fp = @fopen($this->_filename, 'a')) == false) {
  203.         return false;
  204.     }
  205.         return true;
  206.  }
  207.  
  208.  function close(){
  209.     if($this->_currentPriority != ABC_NO_LOG){
  210.         $this->info("Logger остановлен");
  211.         return fclose($this->_fp);
  212.     }
  213.  }
  214.  
  215.  /*
  216.   * Организаторские Функции.
  217.   *
  218.   */
  219.  
  220.  function Factory($name, $logger_name, $level) {
  221.     $instance = new DebugOut($name, $logger_name, $level);
  222.     return $instance;
  223.  }
  224.  
  225.  
  226.  function &getWriterSingleton($name, $logger_name, $level = ABC_DEBUG){
  227.  
  228.       static $instances;
  229.       if (!isset($instances)){
  230.         $instances = array();
  231.       }
  232.       $signature = serialize(array($name, $level));
  233.  
  234.       if (!isset($instances[$signature])) {
  235.             $instances[$signature] = DebugOut::Factory($name, $logger_name, $level);
  236.       }
  237.      
  238.       return $instances[$signature];
  239.  }
  240.  
  241.  
  242.  function attach(&$logObserver) {
  243.     if (!is_object($logObserver)) {
  244.         return false;
  245.     }
  246.  
  247.     $logObserver->_listenerID = uniqid(rand());
  248.  
  249.     $this->_listeners[$logObserver->_listenerID] = &$logObserver;
  250.  }
  251.  
  252. }
  253.  
  254. define ('ABC_BAD_DATE', -1);
  255.  
  256. class ExcelDateUtil{
  257.  
  258.  
  259. /*
  260.  * Возвращение 1900 года как целое число TIMESTAMP.
  261.  * используется для UNIX
  262.  *
  263.  */
  264.  
  265. function xls2tstamp($date) {
  266.     $date=$date>25568?$date:25569;
  267.     /*Существовала ошибка при Преобразовании даты меньшей чем 1-1-1970 (tstamp 0) */
  268.         $ofs=(70 * 365 + 17+2) * 86400;
  269.          return ($date * 86400) - $ofs;
  270. }
  271.  
  272. function getDateArray($xls_date){
  273.     $ret = array();
  274.  
  275.     // Ошибка высокосного года
  276.     if ($xls_date == 60) {
  277.  
  278.         $ret['day']   = 29;
  279.         $ret['month'] = 2;
  280.         $ret['year']  = 1900;
  281.         return $ret;
  282.  
  283.     } else if ($xls_date < 60) {
  284.         // 29-02-1900 ошибка
  285.         $xls_date++;
  286.     }
  287.  
  288.     // Изменения к Юлианскому  DMY вычислению с дополнением 2415019
  289.     $l = $xls_date + 68569 + 2415019;
  290.     $n = (int)(( 4 * $l ) / 146097);
  291.     $l = $l - (int)(( 146097 * $n + 3 ) / 4);
  292.     $i = (int)(( 4000 * ( $l + 1 ) ) / 1461001);
  293.     $l = $l - (int)(( 1461 * $i ) / 4) + 31;
  294.     $j = (int)(( 80 * $l ) / 2447);
  295.     $ret['day'] = $l - (int)(( 2447 * $j ) / 80);
  296.     $l = (int)($j / 11);
  297.     $ret['month'] = $j + 2 - ( 12 * $l );
  298.     $ret['year'] = 100 * ( $n - 49 ) + $i + $l;
  299.  
  300.     return $ret;
  301. }
  302.  
  303.  
  304.  
  305. function isInternalDateFormat($format) {
  306.     $retval =false;
  307.  
  308.     switch(format) {
  309.     // Внутренние Форматы Даты как описано на странице 427 в
  310.     // Microsoft Excel Dev's Kit...
  311.         case 0x0e:
  312.         case 0x0f:
  313.         case 0x10:
  314.         case 0x11:
  315.         case 0x12:
  316.         case 0x13:
  317.         case 0x14:
  318.         case 0x15:
  319.         case 0x16:
  320.         case 0x2d:
  321.         case 0x2e:
  322.         case 0x2f:
  323.         // Дополнительные внутренние форматы даты, найденные при
  324.         // использовании Excel v. X 10.1.0 (Mac)
  325.         case 0xa4:
  326.         case 0xa5:
  327.         case 0xa6:
  328.         case 0xa7:
  329.         case 0xa8:
  330.         case 0xa9:
  331.         case 0xaa:
  332.         case 0xab:
  333.         case 0xac:
  334.         case 0xad:
  335.         $retval = true; break;
  336.         default: $retval = false; break;
  337.     }
  338.          return $retval;
  339. }
  340.  
  341. }
  342. define('EXCEL_FONT_RID',0x31);
  343.  
  344. define('XF_SCRIPT_NONE',0);
  345. define('XF_SCRIPT_SUPERSCRIPT',1);
  346. define('XF_SCRIPT_SUBSCRIPT',2);
  347.  
  348. define('XF_UNDERLINE_NONE',0x0);
  349. define('XF_UNDERLINE_SINGLE',0x1);
  350. define('XF_UNDERLINE_DOUBLE',0x2);
  351. define('XF_UNDERLINE_SINGLE_ACCOUNTING',0x3);
  352. define('XF_UNDERLINE_DOUBLE_ACCOUNTING',0x4);
  353.  
  354. define('XF_STYLE_ITALIC', 0x2);
  355. define('XF_STYLE_STRIKEOUT', 0x8);
  356.  
  357. define('XF_BOLDNESS_REGULAR',0x190);
  358. define('XF_BOLDNESS_BOLD',0x2BC);
  359.  
  360.  
  361. class ExcelFont {
  362.  
  363.  
  364.  function basicFontRecord() {
  365.     return  array('size'     => 10,
  366.                     'script'   => XF_SCRIPT_NONE,
  367.                     'undeline' => XF_UNDERLINE_NONE,
  368.                     'italic'   => false,
  369.                     'strikeout'=> false,
  370.                     'bold'     => false,
  371.                     'boldness' => XF_BOLDNESS_REGULAR,
  372.                     'palete'   => 0,
  373.                     'name'     => 'Arial');
  374.  }
  375.  
  376.  function getFontRecord(&$wb,$ptr) {
  377.  
  378.     $retval = array('size'     => 0,
  379.                     'script'   => XF_SCRIPT_NONE,
  380.                     'undeline' => XF_UNDERLINE_NONE,
  381.                     'italic'   => false,
  382.                     'strikeout'=> false,
  383.                     'bold'     => false,
  384.                     'boldness' => XF_BOLDNESS_REGULAR,
  385.                     'palete'   => 0,
  386.                     'name'     => '');
  387.     $retval['size'] = (ord($wb[$ptr])+ 256*ord($wb[$ptr+1]))/20;
  388.     $style=ord($wb[$ptr+2]);
  389.     if (($style & XF_STYLE_ITALIC) != 0) {
  390.         $retval['italic'] = true;
  391.     }
  392.     if (($style & XF_STYLE_STRIKEOUT) != 0) {
  393.         $retval['strikeout'] = true;
  394.     }
  395.     $retval['palete'] = ord($wb[$ptr+4])+256*ord($wb[$ptr+5]);
  396.  
  397.     $retval['boldness'] = ord($wb[$ptr+6])+256*ord($wb[$ptr+7]);
  398.     $retval['bold'] = $retval['boldness'] == XF_BOLDNESS_REGULAR ? false:true;
  399.     $retval['script'] =  ord($wb[$ptr+8])+256*ord($wb[$ptr+9]);
  400.     $retval['underline'] = ord($wb[$ptr+10]);
  401.  
  402.     $length = ord($wb[$ptr+14]);
  403.     if($length >0) {
  404.         if(ord($wb[$ptr+15]) == 0) { // Сжатие Unicode
  405.             $retval['name'] = substr($wb,$ptr+16,$length);
  406.         } else { // Без сжатия Unicode
  407.             $retval['name'] = ExcelFont::getUnicodeString($wb,$ptr+15,$length);
  408.         }
  409.  
  410.     }
  411.  
  412.  
  413.     return $retval;
  414.  }
  415.  
  416.  function toString(&$record,$index) {
  417.     $retval = sprintf("Индекс Шрифта = %d \nРазмер шрифта =%d\nКурсив = %s\nЗачеркнутый=%s\nPalete=%s\nЖирность = %s Полужирный=%s\n Script = %d\n Подчеркивание = %d\n Имя шрифта=%s<hr>",
  418.                 $index,
  419.                 $record['size'],
  420.                 $record['italic']    == true?"true":"false",
  421.                 $record['strikeout'] == true?"true":"false",
  422.                 $record['palete'],
  423.                 $record['boldness'],
  424.                 $record['bold'] == true?"true":"false",
  425.                 $record['script'],
  426.                 $record['underline'],
  427.                 $record['name']
  428.                 );
  429.  
  430.     return $retval;
  431.  
  432.  }
  433.  
  434.  
  435.  function getUnicodeString(&$string,$offset,$length) {
  436.         $bstring = "";
  437.         $index   = $offset + 1;   // Начало с младших битов.
  438.  
  439.         for ($k = 0; $k < $length; $k++)
  440.         {
  441.             $bstring = $bstring.$string[$index];
  442.             $index        += 2;
  443.         }
  444.         return substr($bstring,0,$length);
  445.  }
  446.  
  447.  function ExcelToCSS($rec, $app_font=true, $app_size=true, $app_italic=true, $app_bold=true){
  448.     $ret = "";
  449.     if($app_font==true){
  450.         $ret = $ret."font-family:".$rec['name']."; ";
  451.     }
  452.     if($app_size==true){
  453.         $ret = $ret."font-size:".$rec['size']."pt; ";
  454.     }
  455.     if($app_bold==true){
  456.         if($rec['bold']==true){
  457.             $ret = $ret."font-weight:bold; ";
  458.         } else {
  459.             $ret = $ret."font-weight:normal; ";
  460.         }
  461.     }
  462.     if($app_italic==true){
  463.         if($rec['italic']==true){
  464.             $ret = $ret."font-style:italic; ";
  465.         } else {
  466.             $ret = $ret."font-style:normal; ";
  467.         }
  468.     }
  469.  
  470.     return $ret;
  471.  }
  472.  
  473. }
  474. define ( DP_EMPTY,          0 );
  475. define ( DP_STRING_SOURCE,  1 );
  476. define ( DP_FILE_SOURCE,    2 );
  477.  
  478. class ExcelParserUtil
  479. {
  480.     function str2long($str) {
  481.         return ord($str[0]) + 256*(ord($str[1]) +
  482.             256*(ord($str[2]) + 256*(ord($str[3])) ));
  483.     }
  484. }
  485.  
  486. class DataProvider
  487. {
  488.     function DataProvider( $data, $dataType )
  489.     {
  490.         switch( $dataType )
  491.         {
  492.         case DP_FILE_SOURCE:
  493.             if( !( $this->_data = @fopen( $data, "rb" )) )
  494.                 return;
  495.             $this->_size = @filesize( $data );
  496.             if( !$this->_size )
  497.                 _die("Невозможно определить размер файла.");
  498.             break;
  499.         case DP_STRING_SOURCE:
  500.             $this->_data = $data;
  501.             $this->_size = strlen( $data );
  502.             break;
  503.         default:
  504.             _die("Обнаружен недопустимый тип данных.");
  505.         }
  506.        
  507.         $this->_type = $dataType;      
  508.         register_shutdown_function( array( $this, "close") );
  509.     }
  510.    
  511.     function get( $offset, $length )
  512.     {
  513.         if( !$this->isValid() )
  514.             _die("В источнике нет данных - пусто.");
  515.         /*if( $this->_baseOfs + $offset + $length > $this->_size )
  516.             _die("Недопустимое смещение/длина.");
  517.             */
  518.         switch( $this->_type )
  519.         {
  520.         case DP_FILE_SOURCE:
  521.         {
  522.             if( @fseek( $this->_data, $this->_baseOfs + $offset, SEEK_SET ) == -1 )
  523.                 _die("Неудалось найти позицию в файле указанную в смещении.");
  524.             return @fread( $this->_data, $length );
  525.         }
  526.         case DP_STRING_SOURCE:
  527.         {
  528.             $rc = substr( $this->_data, $this->_baseOfs + $offset, $length );
  529.             return $rc;
  530.         }
  531.         default:
  532.             _die("Недопустимый тип данных или класс не был инициализирован.");
  533.         }
  534.     }
  535.    
  536.     function getByte( $offset )
  537.     {
  538.         return $this->get( $offset, 1 );
  539.     }
  540.    
  541.     function getOrd( $offset )
  542.     {
  543.         return ord( $this->getByte( $offset ) );
  544.     }
  545.    
  546.     function getLong( $offset )
  547.     {
  548.         $str = $this->get( $offset, 4 );
  549.         return ExcelParserUtil::str2long( $str );
  550.     }
  551.    
  552.     function getSize()
  553.     {
  554.         if( !$this->isValid() )
  555.             _die("Источник данных пуст.");
  556.         return $this->_size;
  557.     }
  558.    
  559.     function getBlocks()
  560.     {
  561.         if( !$this->isValid() )
  562.             _die("Источник данных пуст.");
  563.         return (int)(($this->_size - 1) / 0x200) - 1;
  564.     }
  565.    
  566.     function ReadFromFat( $chain, $gran = 0x200 )
  567.     {
  568.         $rc = '';
  569.         for( $i = 0; $i < count($chain); $i++ )
  570.             $rc .= $this->get( $chain[$i] * $gran, $gran );
  571.         return $rc;
  572.     }
  573.    
  574.     function close()
  575.     {
  576.         switch($this->_type )
  577.         {
  578.         case DP_FILE_SOURCE:
  579.             @fclose( $this->_data );
  580.         case DP_STRING_SOURCE:
  581.             $this->_data = null;
  582.         default:
  583.             $_type = DP_EMPTY;
  584.             break;
  585.         }
  586.     }
  587.    
  588.     function isValid()
  589.     {
  590.         return $this->_type != DP_EMPTY;
  591.     }
  592.    
  593.     var $_type = DP_EMPTY;
  594.     var $_data = null;
  595.     var $_size = -1;
  596.     var $_baseOfs = 0;
  597. }
  598.  
  599. class ExcelFileParser {
  600.  
  601.     var $dp = null;
  602.    
  603.     var $max_blocks;
  604.     var $max_sblocks;
  605.    
  606.     // Внутренние переменные
  607.     var $fat;
  608.     var $sfat;
  609.    
  610.     // Удаленный: var $sbd;
  611.     //Удаленный: var $syear;
  612.    
  613.     var $formats;
  614.     var $xf;
  615.     var $fonts;
  616.  
  617.     var $dbglog;
  618.  
  619.  
  620.     function ExcelFileParser($logfile="",$level=ABC_NO_LOG) {
  621.         $this->dbglog = &DebugOut::getWriterSingleton($logfile,"",$level);
  622.         $this->dbglog->info("Logger запущен");
  623.     }
  624.  
  625.     function populateFormat() {
  626.     $this->dbglog->trace(" populateFormat() function call");
  627.  
  628.     $ret = array (
  629.             0=> "General",
  630.             1=> "0",
  631.             2=> "0.00",
  632.             3=> "#,##0",
  633.             4=> "#,##0.00",
  634.             5=> "($#,##0_);($#,##0)",
  635.             6=> "($#,##0_);[Red]($#,##0)",
  636.             7=> "($#,##0.00);($#,##0.00)",
  637.             8=> "($#,##0.00_);[Red]($#,##0.00)",
  638.             9=> "0%",
  639.             0xa=> "0.00%",
  640.             0xb=> "0.00E+00",
  641.             0xc=> "# ?/?",
  642.             0xd=> "# ??/??",
  643.             0xe=> "m/d/yy",
  644.             0xf=> "d-mmm-yy",
  645.             0x10=> "d-mmm",
  646.             0x11=> "mmm-yy",
  647.             0x12=> "h:mm AM/PM",
  648.             0x13=> "h:mm:ss AM/PM",
  649.             0x14=> "h:mm",
  650.             0x15=> "h:mm:ss",
  651.             0x16=> "m/d/yy h:mm",
  652.  
  653.             // 0x17 - 0x24 Зарезервировано
  654.             0x17=> "0x17",
  655.             0x18=> "0x18",
  656.             0x19=> "0x19",
  657.             0x1a=> "0x1a",
  658.             0x1b=> "0x1b",
  659.             0x1c=> "0x1c",
  660.             0x1d=> "0x1d",
  661.             0x1e=> "0x1e",
  662.             0x1f=> "0x1f",
  663.             0x20=> "0x20",
  664.             0x21=> "0x21",
  665.             0x22=> "0x22",
  666.             0x23=> "0x23",
  667.             0x24=> "0x24",
  668.  
  669.             // 0x17 - 0x24 Зарезервировано
  670.             0x25=> "(#,##0_);(#,##0)",
  671.             0x26=> "(#,##0_);[Red](#,##0)",
  672.             0x27=> "(#,##0.00_);(#,##0.00)",
  673.             0x28=> "(#,##0.00_);[Red](#,##0.00)",
  674.             0x29=> "_(*#,##0_);_(*(#,##0);_(* \"-\"_);_(@_)",
  675.             0x2a=> "_($*#,##0_);_($*(#,##0);_($* \"-\"_);_(@_)",
  676.             0x2b=> "_(*#,##0.00_);_(*(#,##0.00);_(*\"-\"??_);_(@_)",
  677.             0x2c=> "_($*#,##0.00_);_($*(#,##0.00);_($*\"-\"??_);_(@_)",
  678.             0x2d=> "mm:ss",
  679.             0x2e=> "[h]:mm:ss",
  680.             0x2f=> "mm:ss.0",
  681.             0x30=> "##0.0E+0",
  682.             0x31=> "@");
  683.  
  684.             $this->dbglog->dump($ret,"\$ret");
  685.             $this->dbglog->trace("populateFormat() function return");
  686.  
  687.             return $ret;
  688.  
  689.     }
  690.  
  691.     function xls2tstamp($date) {
  692.     $date=$date>25568?$date:25569;
  693.     /*Раньше существовала ошибка в преобразовании даты 1-1-1970 (tstamp 0)*/
  694.         $ofs=(70 * 365 + 17+2) * 86400;
  695.          return ($date * 86400) - $ofs;
  696.     }
  697.  
  698.  
  699.     function getDateArray($date) {
  700.        return ExcelDateUtil::getDateArray($date);
  701.     }
  702.  
  703.     function isDateFormat($val){
  704.         $f_i=$this->xf['format'][$val];
  705.         if(preg_match("/[m|d|y]/i",$this->format[$f_i])!=0){
  706.             if(strrpos($this->format[$f_i],'[')!=FALSE) {
  707.                 $tmp = preg_replace("/(\[\/?)(\w+)([^\]]*\])/","'\\1'.''.'\\3'",$this->format[$f_i]);
  708.                 if(preg_match("/[m|d|y]/i",$tmp)!=0)
  709.                    return TRUE;
  710.                  else
  711.                    return FALSE;
  712.             } else {
  713.                 return TRUE;
  714.             }
  715.         } else
  716.           return FALSE;
  717.     }
  718.  
  719.  
  720.     function getUnicodeString($str,$ofs){
  721.        $size=0;
  722.        $i_ofs=0;
  723. /*     if (ord($str[$ofs])==255) {
  724.          $size=ord($str[$ofs])+ 256*(ord($str[$ofs+1]));
  725.          $i_ofs=2;
  726.        } else {*/
  727.          $size=ord($str[$ofs]);
  728.          $i_ofs=1;
  729. /*     }*/
  730.        return substr($str,$ofs+$i_ofs+1,$size);
  731.  
  732.     }
  733.  
  734.  
  735.     function getByteString($str,$ofs){
  736.        $size=0;
  737.        $i_ofs=0;
  738. //     if (ord($str[$ofs])==255) {
  739. //       $size=ord($str[$ofs])+ 256*(ord($str[$ofs+1]));
  740. //       $i_ofs=2;
  741. //     } else {
  742.          $size=ord($str[$ofs]);
  743.          $i_ofs=1;
  744. //     }
  745.        return substr($str,$ofs+$i_ofs+1,$size);
  746.     }
  747.  
  748.  
  749.  
  750.     /*
  751.      * Получение цепочек данных
  752.      */
  753.     function get_blocks_chain($start,$small_fat=false) {
  754.  
  755.         $this->dbglog->trace("get_blocks_chain(".var_export($start,true).",".var_export($small_fat,true).") function call ");
  756.         $chain = array();
  757.  
  758.         $next_block = $start;
  759.         if( !$small_fat ) {
  760.             while(  ($next_block!=0xfffffffe) &&
  761.                 ($next_block <= $this->max_blocks) &&
  762.                 ($next_block < count($this->fat)) )
  763.             {
  764.                 $chain[] = $next_block;
  765.                 $next_block = $this->fat[$next_block];
  766.             }
  767.         } else {
  768.             while(  ($next_block!=0xfffffffe) &&
  769.                 ($next_block <= $this->max_sblocks) &&
  770.                 ($next_block < count($this->sfat)) )
  771.             {
  772.                 $chain[] = $next_block;
  773.                 $next_block = $this->sfat[$next_block];
  774.             }
  775.         }
  776.        
  777.         if( $next_block != 0xfffffffe )
  778.             return false;
  779.  
  780.         $this->dbglog->dump($chain,"\$chain");
  781.         $this->dbglog->trace("get_blocks_chain() function return");
  782.         return $chain;
  783.     }
  784.  
  785.     /* Поиcк потока по имени
  786.      *
  787.      */
  788.  
  789.     function find_stream( $dir, $item_name,$item_num=0) {
  790.  
  791.         $this->dbglog->trace("find_stream(".var_export($dir,true).",".var_export($item_name,true).",".var_export($item_num,true).") function call ");
  792.  
  793.         $dt = $dir->getOrd( $item_num * 0x80 + 0x42 );
  794.         $prev = $dir->getLong( $item_num * 0x80 + 0x44 );
  795.         $next = $dir->getLong( $item_num * 0x80 + 0x48 );
  796.         $dir_ = $dir->getLong( $item_num * 0x80 + 0x4c );
  797.  
  798.         $curr_name = '';
  799.         if( ($dt==2) || ($dt==5) )
  800.             for( $i=0;
  801.              $i < ( $dir->getOrd( $item_num * 0x80 + 0x40 ) +
  802.               256 * $dir->getOrd( $item_num * 0x80 + 0x41 ) )/2-1;
  803.              $i++ )
  804.                 $curr_name .= $dir->getByte( $item_num * 0x80 + $i * 2 );
  805.  
  806.         if( (($dt==2) || ($dt==5)) && (strcmp($curr_name,$item_name)==0) ){
  807.             $this->dbglog->trace("find_stream() function return with ".var_export($item_num,true));
  808.             return $item_num;
  809.         }
  810.  
  811.         if( $prev != 0xffffffff ) {
  812.             $i = $this->find_stream( $dir, $item_name, $prev);
  813.             if( $i>=0 ){
  814.                 $this->dbglog->trace("find_stream() function return with ".var_export($i,true));
  815.                 return $i;
  816.              }
  817.         }
  818.         if( $next != 0xffffffff ) {
  819.             $i = $this->find_stream( $dir, $item_name, $next);
  820.             if( $i>=0 ){
  821.                 $this->dbglog->trace("find_stream() function return with ".var_export($i,true));
  822.                 return $i;
  823.             }
  824.         }
  825.         if( $dir_ != 0xffffffff ) {
  826.             $i = $this->find_stream( $dir, $item_name, $dir_ );
  827.             if( $i>=0 ) {
  828.                 $this->dbglog->trace("find_stream() function return with ".var_export($i,true));
  829.                 return $i;
  830.             }
  831.         }
  832.         $this->dbglog->trace("find_stream() function return with -1");
  833.         return -2;
  834.     }
  835.  
  836.     function rk_decode($rk) {
  837.  
  838. //      $this->dbglog->trace("rk_decode(".var_export($rk,true).") function call");
  839.         $res = array();
  840.         if( $rk & 2 ) {
  841.             //целое
  842.             $val = ($rk & 0xfffffffc) >> 2;
  843.             if( $rk & 1 ) $val = $val / 100;
  844.             if (((float)$val) == floor((float)$val)){
  845.                $res['val'] = (int)$val;
  846.                $res['type'] = 1;
  847.             } else {
  848.                $res['val'] = (float)$val;
  849.                $res['type'] = 2;
  850.             }
  851.  
  852.         } else {
  853.             //вещественное
  854.             $res['type'] = 2;
  855.             $frk = $rk;
  856.  
  857.             $fexp =  (($frk & 0x7ff00000) >> 20) - 1023;
  858.             $val = 1+(($frk & 0x000fffff) >> 2)/262144;
  859.  
  860.             if( $fexp > 0 ) {
  861.                 for( $i=0; $i<$fexp; $i++ )
  862.                     $val *= 2;
  863.             } else {
  864.                 if( $fexp==-1023 ) {
  865.                     $val=0;
  866.                 } else {
  867.                  for( $i=0; $i<abs($fexp); $i++ )
  868.                     $val /= 2;
  869.                 }
  870.             }
  871.  
  872.             if( $rk & 1 ) $val = $val / 100;
  873.             if( $rk & 0x80000000 ) $val = -$val;
  874.  
  875.             $res['val'] = (float)$val;
  876.         }
  877. //      $this->dbglog->trace("rk_decode() function returns");
  878.         return $res;
  879.     }
  880.  
  881.     // Анализ рабочих листов
  882.     //-----------------
  883.  
  884.     function parse_worksheet($ws) {
  885.  
  886.         $this->dbglog->debug("parse_worksheet(DATA) function");
  887.         if( strlen($ws) <= 0 ){
  888.             $this->dbglog->trace("parse_worksheet() function returns 7 (Data not Found)");
  889.             return 7;
  890.         }
  891.         if( strlen($ws) <  4 ){
  892.             $this->dbglog->trace("parse_worksheet() function returns 6 (File Corrupted)");
  893.             return 6;
  894.         }
  895.  
  896.         //анализ заголовка рабочей книги
  897.         if( strlen($ws) < 256*ord($ws[3])+ord($ws[2]) ) return 6;
  898.  
  899.         if( ord($ws[0]) != 0x09 ) return 6;
  900.         $vers = ord($ws[1]);
  901.         if( ($vers!=0) && ($vers!=2) && ($vers!=4) && ($vers!=8) )
  902.             return 8;
  903.  
  904.         if( $vers!=8 ) {
  905.          $biff_ver = ($ver+4)/2;
  906.         } else {
  907.          if( strlen($ws) < 12 ) return 6;
  908.          switch( ord($ws[4])+256*ord($ws[5]) ) {
  909.             case 0x0500:
  910.                 if( ord($ws[0x0a])+256*ord($ws[0x0b]) < 1994 ) {
  911.                     $biff_ver = 5;
  912.                 } else {
  913.                     switch(ord( $ws[8])+256*ord($ws[9]) ) {
  914.                      case 2412:
  915.                      case 3218:
  916.                      case 3321:
  917. /*dbg*/                 $this->dbglog->debug("Анализатор BIFF версия - 5");
  918.                         $biff_ver = 5;
  919.                          break;
  920.                      default:
  921.                         $this->dbglog->debug("Анализатор BIFF версия 7");
  922.                         $biff_ver = 7;
  923.                         break;
  924.                     }
  925.                 }
  926.                 break;
  927.             case 0x0600:
  928. /*DBG*/         $this->dbglog->debug("Анализатор BIFF версия 8");
  929.                 $biff_ver = 8;
  930.                 break;
  931.             default:
  932.                 return 8;
  933.          }
  934.         }
  935.  
  936.         if( $biff_ver < 5 ) {
  937. /*DBG*/  $this->dbglog->debug("parse_worksheet() function found ($biff_ver < 5) return 8");
  938.           return 8;
  939.         }
  940.         $ptr = 0;
  941.         $data = array('biff_version' => $biff_ver );
  942.  
  943.         while( (ord($ws[$ptr])!=0x0a) && ($ptr<strlen($ws)) ) {
  944.                      
  945.          switch (ord($ws[$ptr])+256*ord($ws[$ptr+1])) {
  946.            
  947.          
  948.           // Формула
  949.           //Номер
  950.          
  951.           case 0x0203:
  952.           case 0x0006:
  953.           case 0x0206:
  954.           case 0x0406:
  955.          
  956. /*DBG*/     $this->dbglog->trace("found NUMBER");
  957.  
  958.             if( ($biff_ver < 3) ){
  959. /*DBG*/         $this->dbglog->trace("$biff_ver < 3 break;");
  960.                 break;
  961.             }
  962.             if( (ord($ws[$ptr+2])+256*ord($ws[$ptr+3])) < 14 ){
  963. /*DBG*/         $this->dbglog->debug("parse_worksheet() return 6");
  964.                 return 6;
  965.             }
  966.  
  967.             $row = ord($ws[$ptr+4])+256*ord($ws[$ptr+5]);
  968.             $col = ord($ws[$ptr+6])+256*ord($ws[$ptr+7]);
  969.             $num_lo = ExcelParserUtil::str2long(substr($ws,$ptr+10,4));
  970.             $num_hi = ExcelParserUtil::str2long(substr($ws,$ptr+14,4));
  971.             $xf_i = ord($ws[$ptr+8])+256*ord($ws[$ptr+9]);
  972.  
  973.             if($this->isDateFormat($xf_i)){
  974.                 $data['cell'][$row][$col]['type'] = 3;
  975.             } else {
  976.                 $data['cell'][$row][$col]['type'] = 2;
  977.             }
  978.  
  979.             $fonti = $this->xf['font'][$xf_i];
  980.                 $data['cell'][$row][$fc+$i]['font'] = $fonti;
  981.  
  982.             $fexp = (($num_hi & 0x7ff00000) >> 20) - 1023;
  983.             $val = 1+(($num_hi & 0x000fffff)+$num_lo/4294967296)/1048576;
  984.  
  985.             if( $fexp > 0 ) {
  986.                 for( $i=0; $i<$fexp; $i++ )
  987.                     $val *= 2;
  988.             } else {
  989.                 for( $i=0; $i<abs($fexp); $i++ )
  990.                     $val /= 2;
  991.             }
  992.             if( $num_hi & 0x80000000 ) $val = -$val;
  993.  
  994.             $data['cell'][$row][$col]['data'] = (float)$val;
  995.  
  996.             if( !isset($data['max_row']) ||
  997.                 ($data['max_row'] < $row) )
  998.                 $data['max_row'] = $row;
  999.  
  1000.             if( !isset($data['max_col']) ||
  1001.                 ($data['max_col'] < $col) )
  1002.                 $data['max_col'] = $col;
  1003.  
  1004.             break;
  1005.  
  1006.           // RK
  1007.           case 0x027e:
  1008. /*DBG*/  $this->dbglog->trace("found RK");
  1009.             if( ($biff_ver < 3) ) break;
  1010.             if( (ord($ws[$ptr+2])+256*ord($ws[$ptr+3])) < 0x0a )
  1011.                 return 6;
  1012.             $row  = ord($ws[$ptr+4])+256*ord($ws[$ptr+5]);
  1013.             $col  = ord($ws[$ptr+6])+256*ord($ws[$ptr+7]);
  1014.             $xf_i = ord($ws[$ptr+8])+256*ord($ws[$ptr+9]);
  1015.             $val  = $this->rk_decode(
  1016.                     ExcelParserUtil::str2long(substr($ws,$ptr+10,4))
  1017.                 );
  1018.  
  1019.             if($this->isDateFormat($xf_i)==TRUE){
  1020.                 $data['cell'][$row][$col]['type'] = 3;
  1021.             } else {
  1022.                 $data['cell'][$row][$col]['type'] = $val['type'];
  1023.             }
  1024.             $fonti = $this->xf['font'][$xf_i];
  1025.  
  1026.             $data['cell'][$row][$col]['font'] = $fonti;
  1027.             $data['cell'][$row][$col]['data'] = $val['val'];
  1028.  
  1029.  
  1030.             if( !isset($data['max_row']) ||
  1031.                 ($data['max_row'] < $row) )
  1032.                 $data['max_row'] = $row;
  1033.  
  1034.             if( !isset($data['max_col']) ||
  1035.                 ($data['max_col'] < $col) )
  1036.                 $data['max_col'] = $col;
  1037.  
  1038.             break;
  1039.  
  1040.           // MULRK
  1041.           case 0x00bd:
  1042. /*DBG*/  $this->dbglog->trace("found  MULL RK");
  1043.             if( ($biff_ver < 5) ) break;
  1044.             $sz = ord($ws[$ptr+2])+256*ord($ws[$ptr+3]);
  1045.             if( $sz < 6 ) return 6;
  1046.  
  1047.             $row = ord($ws[$ptr+4])+256*ord($ws[$ptr+5]);
  1048.             $fc = ord($ws[$ptr+6])+256*ord($ws[$ptr+7]);
  1049.             $lc = ord($ws[$ptr+$sz+2])+256*ord($ws[$ptr+$sz+3]);
  1050.  
  1051.             for( $i=0; $i<=$lc-$fc; $i++) {
  1052.              $val = $this->rk_decode(
  1053.                 ExcelParserUtil::str2long(substr($ws,$ptr+10+$i*6,4))
  1054.                 );
  1055.  
  1056.                $xf_i=ord($ws[$ptr+8+$i*6])+256*ord($ws[($ptr+9+$i*6)]);
  1057.                if($this->isDateFormat($xf_i)==TRUE) {
  1058.                 $data['cell'][$row][$fc+$i]['type'] = 3;
  1059.                } else {
  1060.                 $data['cell'][$row][$fc+$i]['type'] = $val['type'];
  1061.                }
  1062.                $fonti = $this->xf['font'][$xf_i];
  1063.                $data['cell'][$row][$fc+$i]['font'] = $fonti;
  1064.                $data['cell'][$row][$fc+$i]['data'] = $val['val'];
  1065.             }
  1066.  
  1067.             if( !isset($data['max_row']) ||
  1068.                 ($data['max_row'] < $row) )
  1069.                 $data['max_row'] = $row;
  1070.  
  1071.             if( !isset($data['max_col']) ||
  1072.                 ($data['max_col'] < $lc) )
  1073.                 $data['max_col'] = $lc;
  1074.  
  1075.             break;
  1076.  
  1077.           // МЕТКИ
  1078.           case 0x0204:
  1079. /*DBG*/  $this->dbglog->trace("found LABEL");
  1080.             if( ($biff_ver < 3) ){
  1081.                 break;
  1082.             }
  1083.             if( (ord($ws[$ptr+2])+256*ord($ws[$ptr+3])) < 8 ){
  1084.                 return 6;
  1085.             }
  1086.             $row = ord($ws[$ptr+4])+256*ord($ws[$ptr+5]);
  1087.             $col = ord($ws[$ptr+6])+256*ord($ws[$ptr+7]);
  1088.             $xf = ord($ws[$ptr+8])+256*ord($ws[$ptr+9]);
  1089.             $fonti = $this->xf['font'][$xf];
  1090.             $font =  $this->fonts[$fonti];
  1091.  
  1092.  
  1093.             $str_len = ord($ws[$ptr+10])+256*ord($ws[$ptr+11]);
  1094.  
  1095.             if( $ptr+12+$str_len > strlen($ws) )
  1096.                 return 6;
  1097.             $this->sst['unicode'][] = false;
  1098.             $this->sst['data'][] = substr($ws,$ptr+12,$str_len);
  1099.             $data['cell'][$row][$col]['type'] = 0;
  1100.             $sst_ind = count($this->sst['data'])-1;
  1101.             $data['cell'][$row][$col]['data'] = $sst_ind;
  1102.             $data['cell'][$row][$col]['font'] = $fonti;
  1103.  
  1104. /*          echo str_replace("\n","<br>\n", ExcelFont::toString($font,$fonti));
  1105.             echo "Шрифт строки записи ".$this->sst['data'][$sst_ind]."<br>";*/
  1106.  
  1107.             if( !isset($data['max_row']) ||
  1108.                 ($data['max_row'] < $row) )
  1109.                 $data['max_row'] = $row;
  1110.  
  1111.             if( !isset($data['max_col']) ||
  1112.                 ($data['max_col'] < $col) )
  1113.                 $data['max_col'] = $col;
  1114.  
  1115.  
  1116.  
  1117.             break;
  1118.  
  1119.           // Метки ОСТ-SST
  1120.           case 0x00fd:
  1121.             if( $biff_ver < 8 ) break;
  1122.             if( (ord($ws[$ptr+2])+256*ord($ws[$ptr+3])) < 0x0a )
  1123.                 return 6;
  1124.             $row = ord($ws[$ptr+4])+256*ord($ws[$ptr+5]);
  1125.             $col = ord($ws[$ptr+6])+256*ord($ws[$ptr+7]);
  1126.             $xf = ord($ws[$ptr+8])+256*ord($ws[$ptr+9]);
  1127.             $fonti = $this->xf['font'][$xf];
  1128.             $font = &$this->fonts[$fonti];
  1129.  
  1130.             $data['cell'][$row][$col]['type'] = 0;
  1131.             $sst_ind = ExcelParserUtil::str2long(substr($ws,$ptr+10,4));
  1132.             $data['cell'][$row][$col]['data'] = $sst_ind;
  1133.             $data['cell'][$row][$col]['font'] = $fonti;
  1134.  
  1135. /*            echo "Шрифт записи для строки  $row,$col<br>";
  1136.             echo str_replace("\n","<br>\n", ExcelFont::toString($font,$fonti));*/
  1137.  
  1138.             if( !isset($data['max_row']) ||
  1139.                 ($data['max_row'] < $row) )
  1140.                 $data['max_row'] = $row;
  1141.  
  1142.             if( !isset($data['max_col']) ||
  1143.                 ($data['max_col'] < $col) )
  1144.                 $data['max_col'] = $col;
  1145.  
  1146.             break;
  1147.  
  1148.           // Неизвестный, неподдерживаемый или неиспользованный код
  1149.           default:
  1150.             break;
  1151.          }
  1152.  
  1153.         $ptr += 4+256*ord($ws[$ptr+3])+ord($ws[$ptr+2]);
  1154.         }
  1155.         //$this->dbglog->debug("parse_worksheet() function returns ".var_export($data,true));
  1156.  
  1157.         return $data;
  1158.  
  1159.     }
  1160.  
  1161.     // Анализатор рабочей книги
  1162.     //----------------
  1163.  
  1164.     function parse_workbook( $f_header, $dp ) {
  1165.  
  1166. /*DBG*/ $this->dbglog->debug("parse_workbook() function");
  1167.  
  1168.         $root_entry_block = $f_header->getLong(0x30);
  1169.         $num_fat_blocks = $f_header->getLong(0x2c);
  1170.  
  1171. /*TRC*/ $this->dbglog->trace("Анализ заголовка");
  1172.  
  1173.         $this->fat = array();
  1174.         for( $i = 0; $i < $num_fat_blocks; $i++ ){
  1175. /*TRC*/     $this->dbglog->trace("FOR LOOP iteration i =".$i);
  1176.  
  1177.             $fat_block = $f_header->getLong( 0x4c + 4 * $i );          
  1178.             $fatbuf = $dp->get( $fat_block * 0x200, 0x200 );
  1179.             $fat = new DataProvider( $fatbuf, DP_STRING_SOURCE );
  1180.  
  1181.             if( $fat->getSize() < 0x200 ){
  1182. /*DBG*/         $this->dbglog->debug("parse_workbook() function found (strlen($fat) < 0x200) returns 6");
  1183.                 return 6;
  1184.             }
  1185.            
  1186.             for( $j=0; $j<0x80; $j++ )
  1187.                 $this->fat[] = $fat->getLong( $j * 4 );
  1188.                    
  1189.             $fat->close();
  1190.             unset( $fat_block, $fatbuf, $fat );        
  1191.         }
  1192.        
  1193. /*DBG*/ $this->dbglog->dump( $this->fat, "\$fat" );
  1194.        
  1195.         if( count($this->fat) < $num_fat_blocks ) {
  1196. /*DBG*/     $this->dbglog->debug("parse_workbook() function found (count($this->fat) < $num_fat_blocks) returns 6");
  1197.             return 6;
  1198.         }
  1199.        
  1200.         $chain = $this->get_blocks_chain($root_entry_block);
  1201.         $dir = new DataProvider( $dp->ReadFromFat( $chain ), DP_STRING_SOURCE );
  1202.         unset( $chain );
  1203.  
  1204.         $this->sfat = array();
  1205.         $small_block = $f_header->getLong( 0x3c );
  1206.         if( $small_block != 0xfeffffff ) {
  1207.            
  1208.             $root_entry_index = $this->find_stream( $dir, 'Root Entry');
  1209.            
  1210.             // Удалено для совместимости МАС
  1211.            
  1212.             //if( $root_entry_index < 0 ) {
  1213. /*DBG*/         //$this->dbglog->debug("parse_workbook() function dont found Root Entry returns 6");
  1214.                 //return 6;
  1215.             //}
  1216.            
  1217.             $sdc_start_block = $dir->getLong( $root_entry_index * 0x80 + 0x74 );
  1218.             $small_data_chain = $this->get_blocks_chain($sdc_start_block);
  1219.             $this->max_sblocks = count($small_data_chain) * 8;
  1220.            
  1221.             $schain = $this->get_blocks_chain($small_block);           
  1222.             for( $i = 0; $i < count( $schain ); $i++ ) {
  1223.                
  1224.                 $sfatbuf = $dp->get( $schain[$i] * 0x200, 0x200 );
  1225.                 $sfat = new DataProvider( $sfatbuf, DP_STRING_SOURCE );
  1226.                
  1227.                 //$this->dbglog->dump( strlen($sfatbuf), "strlen(\$sftabuf)");
  1228.                 //$this->dbglog->dump( $sfat, "\$sfat");
  1229.                
  1230.                 if( $sfat->getSize() < 0x200 ) {
  1231. /*DBG*/             $this->dbglog->debug("parse_workbook() function found (strlen($sfat) < 0x200)  returns 6");
  1232.                     return 6;
  1233.                 }
  1234.                
  1235.                 for( $j=0; $j<0x80; $j++ )
  1236.                     $this->sfat[] = $sfat->getLong( $j * 4 );
  1237.                
  1238.                 $sfat->close();
  1239.                 unset( $sfatbuf, $sfat );
  1240.             }
  1241.             unset( $schain );
  1242.  
  1243.             $sfcbuf = $dp->ReadFromFat( $small_data_chain );
  1244.             $sdp = new DataProvider( $sfcbuf, DP_STRING_SOURCE );
  1245.             unset( $sfcbuf, $small_data_chain );
  1246.         }
  1247.  
  1248.         $workbook_index = $this->find_stream( $dir, 'Workbook' );
  1249.         if( $workbook_index<0 ) {
  1250.             $workbook_index = $this->find_stream( $dir, 'Book' );
  1251.             if( $workbook_index<0 ){
  1252. /*DBG*/         $this->dbglog->debug("parse_workbook() function workbook index not found returns 7");
  1253.                 return 7;
  1254.             }
  1255.         }
  1256.  
  1257.         $workbook_start_block = $dir->getLong( $workbook_index * 0x80 + 0x74 );
  1258.         $workbook_length = $dir->getLong( $workbook_index * 0x80 + 0x78 );
  1259.         $wb = '';
  1260.  
  1261.         if( $workbook_length > 0 ) {
  1262.             if( $workbook_length >= 0x1000 ) {
  1263.                 $chain = $this->get_blocks_chain($workbook_start_block);
  1264.                 $wb = $dp->ReadFromFat( $chain );
  1265.             } else {
  1266.                 $chain = $this->get_blocks_chain($workbook_start_block,true);
  1267.                 $wb = $sdp->ReadFromFat( $chain, 0x40 );
  1268.                 unset( $sdp );
  1269.             }
  1270.             $wb = substr($wb,0,$workbook_length);
  1271.             if( strlen($wb) != $workbook_length )
  1272.                 return 6;
  1273.             unset( $chain );
  1274.         }
  1275.        
  1276.         // Unset fat arrays
  1277.         unset( $this->fat, $this->sfat );
  1278.  
  1279.         if( strlen($wb) <= 0 ) {
  1280. /*DBG*/    $this->dbglog->debug("parse_workbook() function workbook found (strlen($wb) <= 0) returns 7");
  1281.            return 7;
  1282.         }
  1283.         if( strlen($wb) <  4 ) {
  1284. /*DBG*/    $this->dbglog->debug("parse_workbook() function workbook found (strlen($wb) < 4) returns 6");
  1285.             return 6;
  1286.         }
  1287.            
  1288.         //анализатор заголовка книги
  1289.         if( strlen($wb) < 256*ord($wb[3])+ord($wb[2]) ){
  1290. /*DBG*/     $this->dbglog->debug("parse_workbook() function workbook found (strlen($wb) < 256*ord($wb[3])+ord($wb[2])) < 4) returns 6");
  1291.             return 6;
  1292.         }
  1293.  
  1294.         if( ord($wb[0]) != 0x09 ){
  1295. /*DBG*/     $this->dbglog->debug("parse_workbook() function workbook found (ord($wb[0]) != 0x09) returns 6");
  1296.             return 6;
  1297.         }
  1298.        
  1299.         $vers = ord($wb[1]);
  1300.         if( ($vers!=0) && ($vers!=2) && ($vers!=4) && ($vers!=8) ){
  1301.             return 8;
  1302.         }
  1303.         if( $vers!=8 )
  1304.             $biff_ver = ($ver+4)/2;
  1305.         else {
  1306.                    
  1307.             if( strlen($wb) < 12 ) return 6;
  1308.             switch( ord($wb[4])+256*ord($wb[5]) )
  1309.             {
  1310.             case 0x0500:
  1311.                 if( ord($wb[0x0a])+256*ord($wb[0x0b]) < 1994 )
  1312.                     $biff_ver = 5;
  1313.                 else {
  1314.                     switch(ord( $wb[8])+256*ord($wb[9]) ) {
  1315.                      case 2412:
  1316.                      case 3218:
  1317.                      case 3321:
  1318.                         $biff_ver = 5;
  1319.                         break;
  1320.                      default:
  1321.                         $biff_ver = 7;
  1322.                         break;
  1323.                     }
  1324.                 }
  1325.                 break;
  1326.             case 0x0600:
  1327.                 $biff_ver = 8;
  1328.                 break;
  1329.             default:
  1330.                 return 8;
  1331.             }
  1332.         }
  1333.  
  1334.         if( $biff_ver < 5 ) return 8;
  1335.  
  1336.         $ptr = 0;
  1337.         $this->worksheet['offset'] = array();
  1338.         $this->worksheet['options'] = array();
  1339.         $this->worksheet['unicode'] = array();
  1340.         $this->worksheet['name'] = array();
  1341.         $this->worksheet['data'] = array();
  1342.         $this->format = $this->populateFormat();
  1343.         $this->fonts = array();
  1344.         $this->fonts[0] = ExcelFont::basicFontRecord();
  1345.  
  1346.         $this->xf = array();
  1347.         $this->xf['format'] = array();
  1348.         $this->xf['font'] = array();
  1349.         $this->xf['type_prot'] = array();
  1350.         $this->xf['alignment'] = array();
  1351.         $this->xf['decoration'] = array();
  1352.  
  1353.         $xf_cnt=0;
  1354.  
  1355.         $this->sst['unicode'] = array();
  1356.         $this->sst['data'] = array();
  1357.  
  1358.         $opcode = 0;
  1359.         $sst_defined = false;
  1360.         $wblen = strlen($wb);
  1361.  
  1362.         while( (ord($wb[$ptr])!=0x0a) && ($ptr<$wblen) )
  1363.         {
  1364.             $oc = ord($wb[$ptr])+256*ord($wb[$ptr+1]);
  1365.             if( $oc != 0x3c )
  1366.                 $opcode = $oc;
  1367.            
  1368.             switch ($opcode)
  1369.             {
  1370.            
  1371.             case 0x0085:
  1372.                 $ofs = ExcelParserUtil::str2long(substr($wb,$ptr+4,4));
  1373.                 $this->worksheet['offset'][] = $ofs;
  1374.                 $this->worksheet['options'][] = ord($wb[$ptr+8])+256*ord($wb[$ptr+9]);
  1375.                 if( $biff_ver==8 ) {
  1376.                     $len = ord($wb[$ptr+10]);
  1377.                     if( (ord($wb[$ptr+11]) & 1) > 0 ) {
  1378.                         $this->worksheet['unicode'][] = true;
  1379.                         $len = $len*2;
  1380.                     } else {
  1381.                         $this->worksheet['unicode'][] = false;
  1382.                     }
  1383.                     $this->worksheet['name'][] = substr($wb,$ptr+12,$len);
  1384.                 } else {
  1385.                     $this->worksheet['unicode'][] = false;
  1386.                     $len = ord($wb[$ptr+10]);
  1387.                     $this->worksheet['name'][] = substr($wb,$ptr+11,$len);
  1388.                 }
  1389.    
  1390.                 $pws = $this->parse_worksheet(substr($wb,$ofs));
  1391.                 if( is_array($pws) )
  1392.                     $this->worksheet['data'][] = $pws;
  1393.                 else
  1394.                     return $pws;
  1395.                 break;
  1396.  
  1397.             // Формат
  1398.             case 0x041e:
  1399.                 $fidx = ord($wb[$ptr+4])+256*ord($wb[$ptr+5]);
  1400.                 if($fidx<0x31 ||$fidx==0x31 )
  1401.                     break;
  1402.                 elseif($biff_ver>7)
  1403.                     $this->format[$fidx] = $this->getUnicodeString($wb,$ptr+6);
  1404.                 break;
  1405.  
  1406.             // Шрифт 0x31
  1407.             case EXCEL_FONT_RID:
  1408.                 $rec = ExcelFont::getFontRecord($wb,$ptr+4);
  1409.                 $this->fonts[count($this->fonts)] = $rec;
  1410. /*echo str_replace("\n","<br>\n",ExcelFont::toString($rec,count($this->fonts)-1));
  1411. echo "Шрифт записи<br>" */;
  1412.                 break;
  1413.  
  1414.             // XF
  1415.             case 0x00e0:
  1416.                 $this->xf['font'][$xf_cnt] = ord($wb[$ptr+4])+256*ord($wb[$ptr+5]);
  1417.                 $this->xf['format'][$xf_cnt] = ord($wb[$ptr+6])+256*ord($wb[$ptr+7]);
  1418.                 $this->xf['type'][$xf_cnt]  = "1";
  1419.                 $this->xf['bitmask'][$xf_cnt] = "1";
  1420.                 $xf_cnt++;
  1421.                 break;
  1422.  
  1423.             // SST
  1424.             case 0x00fc:
  1425.                 if( $biff_ver < 8 ) break;
  1426.  
  1427.                 $sbuflen = ord($wb[$ptr+2]) + 256*ord($wb[$ptr+3]);
  1428.  
  1429.                 if( $oc != 0x3c ) {
  1430.                     if( $sst_defined ) return 6;
  1431.                     $snum = ExcelParserUtil::str2long(substr($wb,$ptr+8,4));
  1432.                     $sptr = $ptr+12;
  1433.                     $sst_defined = true;
  1434.                 } else {
  1435.                     if( $rslen > $slen ) {
  1436.                         $sptr = $ptr+4;
  1437.                         $rslen -= $slen;
  1438.                         $slen = $rslen;
  1439.  
  1440.                         if( (ord($wb[$sptr]) & 1) > 0 ) {
  1441.                             if( $char_bytes == 1 ) {
  1442.                                 $sstr = '';
  1443.                                 for( $i=0; $i<strlen($str); $i++ )
  1444.                                     $sstr .= $str[$i].chr(0);
  1445.                                 $str = $sstr;
  1446.                                 $char_bytes=2;
  1447.                             }
  1448.                             $schar_bytes = 2;
  1449.                         } else {
  1450.                             $schar_bytes = 1;
  1451.                         }
  1452.  
  1453.                         if( $sptr+$slen*$schar_bytes > $ptr+4+$sbuflen )
  1454.                             $slen = ($ptr+$sbuflen-$sptr+3)/$schar_bytes;
  1455.  
  1456.                         $sstr = substr($wb,$sptr+1,$slen*$schar_bytes);
  1457.  
  1458.                         if( ($char_bytes == 2) && ($schar_bytes == 1) )
  1459.                         {
  1460.                             $sstr2 = '';
  1461.                             for( $i=0; $i<strlen($sstr); $i++ )
  1462.                                 $sstr2 .= $sstr[$i].chr(0);
  1463.                             $sstr = $sstr2;
  1464.                         }
  1465.                         $str .= $sstr;
  1466.  
  1467.                         $sptr += $slen*$schar_bytes+1+4*$rt+$fesz;
  1468.                         if( $slen < $rslen ) {
  1469.                             if( ($sptr >= strlen($wb)) ||
  1470.                                 ($sptr < $ptr+4+$sbuflen) ||
  1471.                                 (ord($wb[$sptr]) != 0x3c) )
  1472.                             {
  1473.                                 return 6;
  1474.                             }
  1475.                             break;
  1476.                         } else {
  1477.                             if( $char_bytes == 2 ) {
  1478.                                 $this->sst['unicode'][] = true;
  1479.                             } else {
  1480.                                 $this->sst['unicode'][] = false;
  1481.                             }
  1482.                             $this->sst['data'][] = $str;
  1483.                             $snum--;
  1484.                         }
  1485.                     } else {
  1486.                         $sptr = $ptr+4;
  1487.                         if( $sptr > $ptr ) $sptr += 4*$rt+$fesz;
  1488.                     }
  1489.                 }
  1490.  
  1491.                 while(  ($sptr < $ptr+4+$sbuflen) &&
  1492.                     ($sptr < strlen($wb)) &&
  1493.                     ($snum > 0) )
  1494.                 {
  1495.                      $rslen = ord($wb[$sptr])+256*ord($wb[$sptr+1]);
  1496.                      $slen = $rslen;
  1497.  
  1498.                      if( (ord($wb[$sptr+2]) & 1) > 0 ) {
  1499.                         $char_bytes = 2;
  1500.                      } else {
  1501.                         $char_bytes = 1;
  1502.                      }
  1503.  
  1504.                      $rt = 0;
  1505.                      $fesz = 0;
  1506.                      switch (ord($wb[$sptr+2]) & 0x0c) {
  1507.                       // Rich-Text with Far-East
  1508.                       case 0x0c:
  1509.                         $rt = ord($wb[$sptr+3])+256*(ord($wb[$sptr+4]));
  1510.                         $fesz = ExcelParserUtil::str2long(substr($wb,$sptr+5,4));
  1511.                         if( $sptr+9+$slen*$char_bytes > $ptr+4+$sbuflen )
  1512.                             $slen = ($ptr+$sbuflen-$sptr-5)/$char_bytes;
  1513.                         $str = substr($wb,$sptr+9,$slen*$char_bytes);
  1514.                         $sptr += $slen*$char_bytes+9;
  1515.                         break;
  1516.        
  1517.                       // Rich-Text
  1518.                       case 8:
  1519.                         $rt = ord($wb[$sptr+3])+256*(ord($wb[$sptr+4]));
  1520.                         if( $sptr+5+$slen*$char_bytes > $ptr+4+$sbuflen )
  1521.                             $slen = ($ptr+$sbuflen-$sptr-1)/$char_bytes;
  1522.                         $str = substr($wb,$sptr+5,$slen*$char_bytes);
  1523.                         $sptr += $slen*$char_bytes+5;
  1524.                         break;
  1525.        
  1526.                       // Far-East
  1527.                       case 4:
  1528.                         $fesz = ExcelParserUtil::str2long(substr($wb,$sptr+3,4));
  1529.                         if( $sptr+7+$slen*$char_bytes > $ptr+4+$sbuflen )
  1530.                             $slen = ($ptr+$sbuflen-$sptr-3)/$char_bytes;
  1531.                         $str = substr($wb,$sptr+7,$slen*$char_bytes);
  1532.                         $sptr += $slen*$char_bytes+7;
  1533.                         break;
  1534.        
  1535.                       // Сжатый или несжатый unicode
  1536.                       case 0:
  1537.                         if( $sptr+3+$slen*$char_bytes > $ptr+4+$sbuflen )
  1538.                             $slen = ($ptr+$sbuflen-$sptr+1)/$char_bytes;
  1539.                         $str = substr($wb,$sptr+3,$slen*$char_bytes);
  1540.                         $sptr += $slen*$char_bytes+3;
  1541.                         break;
  1542.                      }
  1543.  
  1544.                      if( $slen < $rslen ) {
  1545.                         if( ($sptr >= strlen($wb)) ||
  1546.                             ($sptr < $ptr+4+$sbuflen) ||
  1547.                             (ord($wb[$sptr]) != 0x3c) ) return 6;
  1548.                      } else {
  1549.                         if( $char_bytes == 2 ) {
  1550.                             $this->sst['unicode'][] = true;
  1551.                         } else {
  1552.                             $this->sst['unicode'][] = false;
  1553.                         }
  1554.                         $sptr += 4*$rt+$fesz;
  1555.                         $this->sst['data'][] = $str;
  1556.                         $snum--;
  1557.                      }
  1558.                 } // switch
  1559.                 break;
  1560.             } // switch
  1561.            
  1562.             // !!! Оптимизация:
  1563.             //  $this->wsb[] = substr($wb,$ptr,4+256*ord($wb[$ptr+3])+ord($wb[$ptr+2]));
  1564.            
  1565.             $ptr += 4+256*ord($wb[$ptr+3])+ord($wb[$ptr+2]);
  1566.         } // while
  1567.  
  1568.         // !!! Оптимизация:
  1569.         //  $this->workbook = $wb;
  1570.         $this->biff_version = $biff_ver;
  1571. /*DBG*/ $this->dbglog->debug("parse_workbook() function returns 0");
  1572.         return 0;
  1573.     }
  1574.  
  1575.     // ParseFromString & ParseFromFile
  1576.     //---------------------------------
  1577.     //
  1578.     // В:
  1579.     //  Содержание строки - содержание Файла
  1580.     //  Имя файла строки - Имя файла существующего файла Excel.
  1581.     //
  1582.     // ИЗ:
  1583.     //  0 - Успешно
  1584.     //  1 - невозможно открыть файл
  1585.     //  2 - Файл, слишком маленький чтобы быть файлом Excel
  1586.     //  3 - Ошибка при чтении заголовка
  1587.     //  4 - ошибка при чтении файла
  1588.     //  5 - Это - не файл Excel или файл, сохраненный в < Excel 5.0
  1589.     //  6 - битый файл
  1590.     //  7 - данные не найдены
  1591.     //  8 - Неподдерживаемая версия файла
  1592.  
  1593.     function ParseFromString( $contents )
  1594.     {
  1595.         $this->dbglog->info("ParseFromString() enter.");
  1596.         $this->dp = new DataProvider( $contents, DP_STRING_SOURCE );
  1597.         return $this->InitParser();
  1598.     }
  1599.  
  1600.     function ParseFromFile( $filename )
  1601.     {
  1602.         $this->dbglog->info("ParseFromFile() enter.");
  1603.         $this->dp = new DataProvider( $filename, DP_FILE_SOURCE );
  1604.         return $this->InitParser();
  1605.     }
  1606.  
  1607.     function InitParser()
  1608.     {
  1609.         $this->dbglog->info("InitParser() enter.");
  1610.         if( !$this->dp->isValid() )
  1611.         {
  1612.             $this->dbglog->error("InitParser() Failed to open file.");
  1613.             $this->dbglog->error("InitParser() function returns 1");
  1614.             return 1;
  1615.         }
  1616.         if( $this->dp->getSize() <= 0x200 )
  1617.         {
  1618.             $this->dbglog->error("InitParser() File too small to be an Excel file.");
  1619.             $this->dbglog->error("InitParser() function returns 2");
  1620.             return 2;
  1621.         }
  1622.  
  1623.         $this->max_blocks = $this->dp->getBlocks();
  1624.        
  1625.         // чтение заголовка файла
  1626.         $hdrbuf = $this->dp->get( 0, 0x200 );
  1627.         if( strlen( $hdrbuf ) < 0x200 )
  1628.         {
  1629.             $this->dbglog->error("InitParser() Error reading header.");
  1630.             $this->dbglog->error("InitParser() function returns 3");
  1631.             return 3;
  1632.         }
  1633.    
  1634.         // проверка заголовка файла
  1635.         $header_sig = array(0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1);
  1636.         for( $i = 0; $i < count($header_sig); $i++ )
  1637.             if( $header_sig[$i] != ord( $hdrbuf[$i] ) ){
  1638. /*DBG*/         $this->dbglog->error("InitParser() function founds invalid header");
  1639. /*DBG*/         $this->dbglog->error("InitParser() function returns 5");
  1640.                 return 5;
  1641.             }
  1642.            
  1643.         $f_header = new DataProvider( $hdrbuf, DP_STRING_SOURCE );
  1644.         unset( $hdrbuf, $header_sig, $i );
  1645.  
  1646.         $this->dp->_baseOfs = 0x200;
  1647.         $rc = $this->parse_workbook( $f_header, $this->dp );
  1648.         unset( $f_header );
  1649.         unset( $this->dp, $this->max_blocks, $this->max_sblocks );
  1650.        
  1651.         return $rc;
  1652.     }
  1653. }
  1654.  
  1655. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement