PHPExcel_Shared
[ class tree: PHPExcel_Shared ] [ index: PHPExcel_Shared ] [ all elements ]

Source for file OLERead.php

Documentation is available at OLERead.php

  1. <?php
  2. /**
  3.  * PHPExcel
  4.  *
  5.  * Copyright (c) 2006 - 2011 PHPExcel
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  *
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  20.  *
  21.  * @category   PHPExcel
  22.  * @package    PHPExcel_Shared
  23.  * @copyright  Copyright (c) 2006 - 2011 PHPExcel (http://www.codeplex.com/PHPExcel)
  24.  * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
  25.  * @version    1.7.6, 2011-02-27
  26.  */
  27.  
  28. define('IDENTIFIER_OLE'pack('CCCCCCCC'0xd00xcf0x110xe00xa10xb10x1a0xe1));
  29.  
  30.     private $data '';
  31.  
  32.     // OLE identifier
  33.     const IDENTIFIER_OLE IDENTIFIER_OLE;
  34.  
  35.     // Size of a sector = 512 bytes
  36.     const BIG_BLOCK_SIZE                    0x200;
  37.  
  38.     // Size of a short sector = 64 bytes
  39.     const SMALL_BLOCK_SIZE                    0x40;
  40.  
  41.     // Size of a directory entry always = 128 bytes
  42.     const PROPERTY_STORAGE_BLOCK_SIZE        0x80;
  43.  
  44.     // Minimum size of a standard stream = 4096 bytes, streams smaller than this are stored as short streams
  45.     const SMALL_BLOCK_THRESHOLD                0x1000;
  46.  
  47.     // header offsets
  48.     const NUM_BIG_BLOCK_DEPOT_BLOCKS_POS    0x2c;
  49.     const ROOT_START_BLOCK_POS                0x30;
  50.     const SMALL_BLOCK_DEPOT_BLOCK_POS        0x3c;
  51.     const EXTENSION_BLOCK_POS                0x44;
  52.     const NUM_EXTENSION_BLOCK_POS            0x48;
  53.     const BIG_BLOCK_DEPOT_BLOCKS_POS        0x4c;
  54.  
  55.     // property storage offsets (directory offsets)
  56.     const SIZE_OF_NAME_POS                    0x40;
  57.     const TYPE_POS                            0x42;
  58.     const START_BLOCK_POS                    0x74;
  59.     const SIZE_POS                            0x78;
  60.  
  61.  
  62.  
  63.     public $wrkbook                        = null;
  64.     public $summaryInformation            = null;
  65.     public $documentSummaryInformation    = null;
  66.  
  67.  
  68.     /**
  69.      * Read the file
  70.      *
  71.      * @param $sFileName string Filename
  72.      * @throws Exception
  73.      */
  74.     public function read($sFileName)
  75.     {
  76.         // Check if file exists and is readable
  77.         if(!is_readable($sFileName)) {
  78.             throw new Exception("Could not open " $sFileName " for reading! File does not exist, or it is not readable.");
  79.         }
  80.  
  81.         // Get the file data
  82.         $this->data file_get_contents($sFileName);
  83.  
  84.         // Check OLE identifier
  85.         if (substr($this->data08!= self::IDENTIFIER_OLE{
  86.             throw new Exception('The filename ' $sFileName ' is not recognised as an OLE file');
  87.         }
  88.  
  89.         // Total number of sectors used for the SAT
  90.         $this->numBigBlockDepotBlocks self::_GetInt4d($this->dataself::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
  91.  
  92.         // SecID of the first sector of the directory stream
  93.         $this->rootStartBlock self::_GetInt4d($this->dataself::ROOT_START_BLOCK_POS);
  94.  
  95.         // SecID of the first sector of the SSAT (or -2 if not extant)
  96.         $this->sbdStartBlock self::_GetInt4d($this->dataself::SMALL_BLOCK_DEPOT_BLOCK_POS);
  97.  
  98.         // SecID of the first sector of the MSAT (or -2 if no additional sectors are used)
  99.         $this->extensionBlock self::_GetInt4d($this->dataself::EXTENSION_BLOCK_POS);
  100.  
  101.         // Total number of sectors used by MSAT
  102.         $this->numExtensionBlocks self::_GetInt4d($this->dataself::NUM_EXTENSION_BLOCK_POS);
  103.  
  104.         $bigBlockDepotBlocks array();
  105.         $pos self::BIG_BLOCK_DEPOT_BLOCKS_POS;
  106.  
  107.         $bbdBlocks $this->numBigBlockDepotBlocks;
  108.  
  109.         if ($this->numExtensionBlocks != 0{
  110.             $bbdBlocks (self::BIG_BLOCK_SIZE self::BIG_BLOCK_DEPOT_BLOCKS_POS)/4;
  111.         }
  112.  
  113.         for ($i 0$i $bbdBlocks++$i{
  114.               $bigBlockDepotBlocks[$iself::_GetInt4d($this->data$pos);
  115.               $pos += 4;
  116.         }
  117.  
  118.         for ($j 0$j $this->numExtensionBlocks++$j{
  119.             $pos ($this->extensionBlock 1self::BIG_BLOCK_SIZE;
  120.             $blocksToRead min($this->numBigBlockDepotBlocks $bbdBlocksself::BIG_BLOCK_SIZE 1);
  121.  
  122.             for ($i $bbdBlocks$i $bbdBlocks $blocksToRead++$i{
  123.                 $bigBlockDepotBlocks[$iself::_GetInt4d($this->data$pos);
  124.                 $pos += 4;
  125.             }
  126.  
  127.             $bbdBlocks += $blocksToRead;
  128.             if ($bbdBlocks $this->numBigBlockDepotBlocks{
  129.                 $this->extensionBlock self::_GetInt4d($this->data$pos);
  130.             }
  131.         }
  132.  
  133.         $pos $index 0;
  134.         $this->bigBlockChain array();
  135.  
  136.         $bbs self::BIG_BLOCK_SIZE 4;
  137.         for ($i 0$i $this->numBigBlockDepotBlocks++$i{
  138.             $pos ($bigBlockDepotBlocks[$i1self::BIG_BLOCK_SIZE;
  139.  
  140.             for ($j $j $bbs++$j{
  141.                 $this->bigBlockChain[$indexself::_GetInt4d($this->data$pos);
  142.                 $pos += ;
  143.                 ++$index;
  144.             }
  145.         }
  146.  
  147.         $pos $index 0;
  148.         $sbdBlock $this->sbdStartBlock;
  149.         $this->smallBlockChain array();
  150.  
  151.         while ($sbdBlock != -2{
  152.             $pos ($sbdBlock 1self::BIG_BLOCK_SIZE;
  153.  
  154.             for ($j 0$j $bbs++$j{
  155.                 $this->smallBlockChain[$indexself::_GetInt4d($this->data$pos);
  156.                 $pos += 4;
  157.                 ++$index;
  158.             }
  159.  
  160.             $sbdBlock $this->bigBlockChain[$sbdBlock];
  161.         }
  162.  
  163.         // read the directory stream
  164.         $block $this->rootStartBlock;
  165.         $this->entry $this->_readData($block);
  166.  
  167.         $this->_readPropertySets();
  168.     }
  169.  
  170.     /**
  171.      * Extract binary stream data
  172.      *
  173.      * @return string 
  174.      */
  175.     public function getStream($stream)
  176.     {
  177.         if (is_null($stream)) {
  178.             return null;
  179.         }
  180.  
  181.         $streamData '';
  182.  
  183.         if ($this->props[$stream]['size'self::SMALL_BLOCK_THRESHOLD{
  184.             $rootdata $this->_readData($this->props[$this->rootentry]['startBlock']);
  185.  
  186.             $block $this->props[$stream]['startBlock'];
  187.  
  188.             while ($block != -2{
  189.                   $pos $block self::SMALL_BLOCK_SIZE;
  190.                 $streamData .= substr($rootdata$posself::SMALL_BLOCK_SIZE);
  191.  
  192.                 $block $this->smallBlockChain[$block];
  193.             }
  194.  
  195.             return $streamData;
  196.         else {
  197.             $numBlocks $this->props[$stream]['size'self::BIG_BLOCK_SIZE;
  198.             if ($this->props[$stream]['size'self::BIG_BLOCK_SIZE != 0{
  199.                 ++$numBlocks;
  200.             }
  201.  
  202.             if ($numBlocks == 0return '';
  203.  
  204.             $block $this->props[$stream]['startBlock'];
  205.  
  206.             while ($block != -2{
  207.                 $pos ($block 1self::BIG_BLOCK_SIZE;
  208.                 $streamData .= substr($this->data$posself::BIG_BLOCK_SIZE);
  209.                 $block $this->bigBlockChain[$block];
  210.             }
  211.  
  212.             return $streamData;
  213.         }
  214.     }
  215.  
  216.     /**
  217.      * Read a standard stream (by joining sectors using information from SAT)
  218.      *
  219.      * @param int $bl Sector ID where the stream starts
  220.      * @return string Data for standard stream
  221.      */
  222.     private function _readData($bl)
  223.     {
  224.         $block $bl;
  225.         $data '';
  226.  
  227.         while ($block != -2)  {
  228.             $pos ($block 1self::BIG_BLOCK_SIZE;
  229.             $data .= substr($this->data$posself::BIG_BLOCK_SIZE);
  230.             $block $this->bigBlockChain[$block];
  231.         }
  232.         return $data;
  233.      }
  234.  
  235.     /**
  236.      * Read entries in the directory stream.
  237.      */
  238.     private function _readPropertySets({
  239.         $offset 0;
  240.  
  241.         // loop through entires, each entry is 128 bytes
  242.         $entryLen strlen($this->entry);
  243.         while ($offset $entryLen{
  244.             // entry data (128 bytes)
  245.             $d substr($this->entry$offsetself::PROPERTY_STORAGE_BLOCK_SIZE);
  246.  
  247.             // size in bytes of name
  248.             $nameSize ord($d[self::SIZE_OF_NAME_POS](ord($d[self::SIZE_OF_NAME_POS+1]<< 8);
  249.  
  250.             // type of entry
  251.             $type ord($d[self::TYPE_POS]);
  252.  
  253.             // sectorID of first sector or short sector, if this entry refers to a stream (the case with workbook)
  254.             // sectorID of first sector of the short-stream container stream, if this entry is root entry
  255.             $startBlock self::_GetInt4d($dself::START_BLOCK_POS);
  256.  
  257.             $size self::_GetInt4d($dself::SIZE_POS);
  258.  
  259.             $name str_replace("\x00"""substr($d,0,$nameSize));
  260.  
  261.             $this->props[array (
  262.                 'name' => $name,
  263.                 'type' => $type,
  264.                 'startBlock' => $startBlock,
  265.                 'size' => $size);
  266.  
  267.             // Workbook directory entry (BIFF5 uses Book, BIFF8 uses Workbook)
  268.             if (($name == 'Workbook'|| ($name == 'Book'|| ($name == 'WORKBOOK'|| ($name == 'BOOK')) {
  269.                 $this->wrkbook = count($this->props1;
  270.             }
  271.  
  272.             // Root entry
  273.             if ($name == 'Root Entry' || $name == 'ROOT ENTRY' || $name == 'R'{
  274.                 $this->rootentry count($this->props1;
  275.             }
  276.  
  277.             // Summary information
  278.             if ($name == chr(5'SummaryInformation'{
  279. //                echo 'Summary Information<br />';
  280.                 $this->summaryInformation = count($this->props1;
  281.             }
  282.  
  283.             // Additional Document Summary information
  284.             if ($name == chr(5'DocumentSummaryInformation'{
  285. //                echo 'Document Summary Information<br />';
  286.                 $this->documentSummaryInformation = count($this->props1;
  287.             }
  288.  
  289.             $offset += self::PROPERTY_STORAGE_BLOCK_SIZE;
  290.         }
  291.  
  292.     }
  293.  
  294.     /**
  295.      * Read 4 bytes of data at specified position
  296.      *
  297.      * @param string $data 
  298.      * @param int $pos 
  299.      * @return int 
  300.      */
  301.     private static function _GetInt4d($data$pos)
  302.     {
  303.         // FIX: represent numbers correctly on 64-bit system
  304.         // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
  305.         // Hacked by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
  306.         $_or_24 ord($data[$pos 3]);
  307.         if ($_or_24 >= 128{
  308.             // negative number
  309.             $_ord_24 = -abs((256 $_or_24<< 24);
  310.         else {
  311.             $_ord_24 ($_or_24 127<< 24;
  312.         }
  313.         return ord($data[$pos](ord($data[$pos 1]<< 8(ord($data[$pos 2]<< 16$_ord_24;
  314.     }
  315.  
  316. }

Documentation generated on Sun, 27 Feb 2011 16:32:57 -0800 by phpDocumentor 1.4.3