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 - 2010 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 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel)
  24.  * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
  25.  * @version    1.7.2, 2010-01-11
  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.      * Read the file
  63.      *
  64.      * @param $sFileName string Filename
  65.      * @throws Exception
  66.      */
  67.     public function read($sFileName)
  68.     {
  69.         // Check if file exists and is readable
  70.         if(!is_readable($sFileName)) {
  71.             throw new Exception("Could not open " $sFileName " for reading! File does not exist, or it is not readable.");
  72.         }
  73.  
  74.         // Get the file data
  75.         $this->data = file_get_contents($sFileName);
  76.  
  77.         // Check OLE identifier
  78.         if (substr($this->data08!= self::IDENTIFIER_OLE{
  79.             throw new Exception('The filename ' $sFileName ' is not recognised as an OLE file');
  80.         }
  81.  
  82.         // Total number of sectors used for the SAT
  83.         $this->numBigBlockDepotBlocks $this->_GetInt4d($this->dataself::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
  84.  
  85.         // SecID of the first sector of the directory stream
  86.         $this->rootStartBlock $this->_GetInt4d($this->dataself::ROOT_START_BLOCK_POS);
  87.  
  88.         // SecID of the first sector of the SSAT (or -2 if not extant)
  89.         $this->sbdStartBlock $this->_GetInt4d($this->dataself::SMALL_BLOCK_DEPOT_BLOCK_POS);
  90.  
  91.         // SecID of the first sector of the MSAT (or -2 if no additional sectors are used)
  92.         $this->extensionBlock $this->_GetInt4d($this->dataself::EXTENSION_BLOCK_POS);
  93.  
  94.         // Total number of sectors used by MSAT
  95.         $this->numExtensionBlocks $this->_GetInt4d($this->dataself::NUM_EXTENSION_BLOCK_POS);
  96.  
  97.         $bigBlockDepotBlocks array();
  98.         $pos self::BIG_BLOCK_DEPOT_BLOCKS_POS;
  99.  
  100.         $bbdBlocks $this->numBigBlockDepotBlocks;
  101.  
  102.         if ($this->numExtensionBlocks != 0{
  103.             $bbdBlocks (self::BIG_BLOCK_SIZE self::BIG_BLOCK_DEPOT_BLOCKS_POS)/4;
  104.         }
  105.  
  106.         for ($i 0$i $bbdBlocks++$i{
  107.               $bigBlockDepotBlocks[$i$this->_GetInt4d($this->data$pos);
  108.               $pos += 4;
  109.         }
  110.  
  111.         for ($j 0$j $this->numExtensionBlocks++$j{
  112.             $pos ($this->extensionBlock 1self::BIG_BLOCK_SIZE;
  113.             $blocksToRead min($this->numBigBlockDepotBlocks $bbdBlocksself::BIG_BLOCK_SIZE 1);
  114.  
  115.             for ($i $bbdBlocks$i $bbdBlocks $blocksToRead++$i{
  116.                 $bigBlockDepotBlocks[$i$this->_GetInt4d($this->data$pos);
  117.                 $pos += 4;
  118.             }
  119.  
  120.             $bbdBlocks += $blocksToRead;
  121.             if ($bbdBlocks $this->numBigBlockDepotBlocks{
  122.                 $this->extensionBlock $this->_GetInt4d($this->data$pos);
  123.             }
  124.         }
  125.  
  126.         $pos 0;
  127.         $index 0;
  128.         $this->bigBlockChain array();
  129.  
  130.         for ($i 0$i $this->numBigBlockDepotBlocks++$i{
  131.             $pos ($bigBlockDepotBlocks[$i1self::BIG_BLOCK_SIZE;
  132.  
  133.             for ($j $j self::BIG_BLOCK_SIZE 4++$j{
  134.                 $this->bigBlockChain[$index$this->_GetInt4d($this->data$pos);
  135.                 $pos += ;
  136.                 ++$index;
  137.             }
  138.         }
  139.  
  140.         $pos 0;
  141.         $index 0;
  142.         $sbdBlock $this->sbdStartBlock;
  143.         $this->smallBlockChain array();
  144.  
  145.         while ($sbdBlock != -2{
  146.             $pos ($sbdBlock 1self::BIG_BLOCK_SIZE;
  147.  
  148.             for ($j 0$j self::BIG_BLOCK_SIZE 4++$j{
  149.                 $this->smallBlockChain[$index$this->_GetInt4d($this->data$pos);
  150.                 $pos += 4;
  151.                 ++$index;
  152.             }
  153.  
  154.             $sbdBlock $this->bigBlockChain[$sbdBlock];
  155.         }
  156.  
  157.         $block $this->rootStartBlock;
  158.         $pos 0;
  159.  
  160.         // read the directory stream
  161.         $this->entry $this->_readData($block);
  162.  
  163.         $this->_readPropertySets();
  164.  
  165.     }
  166.  
  167.     /**
  168.      * Extract binary stream data, workbook stream + sheet streams
  169.      *
  170.      * @return string 
  171.      */
  172.     public function getWorkBook()
  173.     {
  174.         if ($this->props[$this->wrkbook]['size'self::SMALL_BLOCK_THRESHOLD){
  175.             $rootdata $this->_readData($this->props[$this->rootentry]['startBlock']);
  176.  
  177.             $streamData '';
  178.             $block $this->props[$this->wrkbook]['startBlock'];
  179.  
  180.             $pos 0;
  181.             while ($block != -2{
  182.                   $pos $block self::SMALL_BLOCK_SIZE;
  183.                 $streamData .= substr($rootdata$posself::SMALL_BLOCK_SIZE);
  184.  
  185.                 $block $this->smallBlockChain[$block];
  186.             }
  187.  
  188.             return $streamData;
  189.  
  190.  
  191.         else {
  192.             $numBlocks $this->props[$this->wrkbook]['size'self::BIG_BLOCK_SIZE;
  193.             if ($this->props[$this->wrkbook]['size'self::BIG_BLOCK_SIZE != 0{
  194.                 ++$numBlocks;
  195.             }
  196.  
  197.             if ($numBlocks == 0return '';
  198.  
  199.  
  200.             $streamData '';
  201.             $block $this->props[$this->wrkbook]['startBlock'];
  202.  
  203.             $pos 0;
  204.  
  205.             while ($block != -2{
  206.                 $pos ($block 1self::BIG_BLOCK_SIZE;
  207.                 $streamData .= substr($this->data$posself::BIG_BLOCK_SIZE);
  208.                 $block $this->bigBlockChain[$block];
  209.             }
  210.  
  211.             return $streamData;
  212.         }
  213.     }
  214.  
  215.     /**
  216.      * Read a standard stream (by joining sectors using information from SAT)
  217.      *
  218.      * @param int $bl Sector ID where the stream starts
  219.      * @return string Data for standard stream
  220.      */
  221.     private function _readData($bl)
  222.     {
  223.         $block $bl;
  224.         $pos 0;
  225.         $data '';
  226.  
  227.         while ($block != -2)  {
  228.             $pos ($block 1self::BIG_BLOCK_SIZE;
  229.             $data $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.     {
  240.         $offset 0;
  241.  
  242.         // loop through entires, each entry is 128 bytes
  243.         while ($offset strlen($this->entry)) {
  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 $this->_GetInt4d($dself::START_BLOCK_POS);
  256.  
  257.             $size $this->_GetInt4d($dself::SIZE_POS);
  258.  
  259.             $name '';
  260.             for ($i 0$i $nameSize ++$i{
  261.                 $name .= $d[$i];
  262.             }
  263.  
  264.             $name str_replace("\x00"""$name);
  265.  
  266.             $this->props[array (
  267.                 'name' => $name,
  268.                 'type' => $type,
  269.                 'startBlock' => $startBlock,
  270.                 'size' => $size);
  271.  
  272.             // Workbook directory entry (BIFF5 uses Book, BIFF8 uses Workbook)
  273.             if (($name == 'Workbook'|| ($name == 'Book'|| ($name == 'WORKBOOK')) {
  274.                 $this->wrkbook count($this->props1;
  275.             }
  276.  
  277.             // Root entry
  278.             if ($name == 'Root Entry' || $name == 'ROOT ENTRY' || $name == 'R'{
  279.                 $this->rootentry count($this->props1;
  280.             }
  281.  
  282.             $offset += self::PROPERTY_STORAGE_BLOCK_SIZE;
  283.         }
  284.  
  285.     }
  286.  
  287.     /**
  288.      * Read 4 bytes of data at specified position
  289.      *
  290.      * @param string $data 
  291.      * @param int $pos 
  292.      * @return int 
  293.      */
  294.     private function _GetInt4d($data$pos)
  295.     {
  296.         // Hacked by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
  297.         $_or_24 ord($data[$pos+3]);
  298.         if ($_or_24>=128$_ord_24 = -abs((256-$_or_24<< 24);
  299.         else $_ord_24 ($_or_24&127<< 24;
  300.  
  301.         return ord($data[$pos](ord($data[$pos+1]<< 8(ord($data[$pos+2]<< 16$_ord_24;
  302.     }
  303.  
  304. }

Documentation generated on Mon, 11 Jan 2010 08:12:23 +0100 by phpDocumentor 1.4.1