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.3c, 2010-06-01
  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.      * Extract binary stream data, workbook stream + sheet streams
  168.      *
  169.      * @return string 
  170.      */
  171.     public function getWorkBook()
  172.     {
  173.         if ($this->props[$this->wrkbook]['size'self::SMALL_BLOCK_THRESHOLD){
  174.             $rootdata $this->_readData($this->props[$this->rootentry]['startBlock']);
  175.  
  176.             $streamData '';
  177.             $block $this->props[$this->wrkbook]['startBlock'];
  178.  
  179.             $pos 0;
  180.             while ($block != -2{
  181.                   $pos $block self::SMALL_BLOCK_SIZE;
  182.                 $streamData .= substr($rootdata$posself::SMALL_BLOCK_SIZE);
  183.  
  184.                 $block $this->smallBlockChain[$block];
  185.             }
  186.  
  187.             return $streamData;
  188.  
  189.  
  190.         else {
  191.             $numBlocks $this->props[$this->wrkbook]['size'self::BIG_BLOCK_SIZE;
  192.             if ($this->props[$this->wrkbook]['size'self::BIG_BLOCK_SIZE != 0{
  193.                 ++$numBlocks;
  194.             }
  195.  
  196.             if ($numBlocks == 0return '';
  197.  
  198.  
  199.             $streamData '';
  200.             $block $this->props[$this->wrkbook]['startBlock'];
  201.  
  202.             $pos 0;
  203.  
  204.             while ($block != -2{
  205.                 $pos ($block 1self::BIG_BLOCK_SIZE;
  206.                 $streamData .= substr($this->data$posself::BIG_BLOCK_SIZE);
  207.                 $block $this->bigBlockChain[$block];
  208.             }
  209.  
  210.             return $streamData;
  211.         }
  212.     }
  213.  
  214.     /**
  215.      * Extract binary stream data, summary information
  216.      *
  217.      * @return string|null
  218.      */
  219.     public function getSummaryInformation()
  220.     {
  221.         if (!isset($this->summaryInformation)) {
  222.             return null;
  223.         }
  224.  
  225.         if ($this->props[$this->summaryInformation]['size'self::SMALL_BLOCK_THRESHOLD){
  226.             $rootdata $this->_readData($this->props[$this->rootentry]['startBlock']);
  227.  
  228.             $streamData '';
  229.             $block $this->props[$this->summaryInformation]['startBlock'];
  230.  
  231.             $pos 0;
  232.             while ($block != -2{
  233.                   $pos $block self::SMALL_BLOCK_SIZE;
  234.                 $streamData .= substr($rootdata$posself::SMALL_BLOCK_SIZE);
  235.  
  236.                 $block $this->smallBlockChain[$block];
  237.             }
  238.  
  239.             return $streamData;
  240.  
  241.  
  242.         else {
  243.             $numBlocks $this->props[$this->summaryInformation]['size'self::BIG_BLOCK_SIZE;
  244.             if ($this->props[$this->summaryInformation]['size'self::BIG_BLOCK_SIZE != 0{
  245.                 ++$numBlocks;
  246.             }
  247.  
  248.             if ($numBlocks == 0return '';
  249.  
  250.  
  251.             $streamData '';
  252.             $block $this->props[$this->summaryInformation]['startBlock'];
  253.  
  254.             $pos 0;
  255.  
  256.             while ($block != -2{
  257.                 $pos ($block 1self::BIG_BLOCK_SIZE;
  258.                 $streamData .= substr($this->data$posself::BIG_BLOCK_SIZE);
  259.                 $block $this->bigBlockChain[$block];
  260.             }
  261.  
  262.             return $streamData;
  263.         }
  264.     }
  265.  
  266.     /**
  267.      * Read a standard stream (by joining sectors using information from SAT)
  268.      *
  269.      * @param int $bl Sector ID where the stream starts
  270.      * @return string Data for standard stream
  271.      */
  272.     private function _readData($bl)
  273.     {
  274.         $block $bl;
  275.         $pos 0;
  276.         $data '';
  277.  
  278.         while ($block != -2)  {
  279.             $pos ($block 1self::BIG_BLOCK_SIZE;
  280.             $data $data substr($this->data$posself::BIG_BLOCK_SIZE);
  281.             $block $this->bigBlockChain[$block];
  282.         }
  283.         return $data;
  284.      }
  285.  
  286.     /**
  287.      * Read entries in the directory stream.
  288.      */
  289.     private function _readPropertySets()
  290.     {
  291.         $offset 0;
  292.  
  293.         // loop through entires, each entry is 128 bytes
  294.         while ($offset strlen($this->entry)) {
  295.             // entry data (128 bytes)
  296.             $d substr($this->entry$offsetself::PROPERTY_STORAGE_BLOCK_SIZE);
  297.  
  298.             // size in bytes of name
  299.             $nameSize ord($d[self::SIZE_OF_NAME_POS](ord($d[self::SIZE_OF_NAME_POS+1]<< 8);
  300.  
  301.             // type of entry
  302.             $type ord($d[self::TYPE_POS]);
  303.  
  304.             // sectorID of first sector or short sector, if this entry refers to a stream (the case with workbook)
  305.             // sectorID of first sector of the short-stream container stream, if this entry is root entry
  306.             $startBlock $this->_GetInt4d($dself::START_BLOCK_POS);
  307.  
  308.             $size $this->_GetInt4d($dself::SIZE_POS);
  309.  
  310.             $name '';
  311.             for ($i 0$i $nameSize ++$i{
  312.                 $name .= $d[$i];
  313.             }
  314.  
  315.             $name str_replace("\x00"""$name);
  316.  
  317.             $this->props[array (
  318.                 'name' => $name,
  319.                 'type' => $type,
  320.                 'startBlock' => $startBlock,
  321.                 'size' => $size);
  322.  
  323.             // Workbook directory entry (BIFF5 uses Book, BIFF8 uses Workbook)
  324.             if (($name == 'Workbook'|| ($name == 'Book'|| ($name == 'WORKBOOK'|| ($name == 'BOOK')) {
  325.                 $this->wrkbook count($this->props1;
  326.             }
  327.  
  328.             // Root entry
  329.             if ($name == 'Root Entry' || $name == 'ROOT ENTRY' || $name == 'R'{
  330.                 $this->rootentry count($this->props1;
  331.             }
  332.  
  333.             // Summary information
  334.             if ($name == chr(5'SummaryInformation'{
  335.                 $this->summaryInformation count($this->props1;
  336.             }
  337.  
  338.             $offset += self::PROPERTY_STORAGE_BLOCK_SIZE;
  339.         }
  340.  
  341.     }
  342.  
  343.     /**
  344.      * Read 4 bytes of data at specified position
  345.      *
  346.      * @param string $data 
  347.      * @param int $pos 
  348.      * @return int 
  349.      */
  350.     private function _GetInt4d($data$pos)
  351.     {
  352.         // Hacked by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
  353.         $_or_24 ord($data[$pos+3]);
  354.         if ($_or_24>=128$_ord_24 = -abs((256-$_or_24<< 24);
  355.         else $_ord_24 ($_or_24&127<< 24;
  356.  
  357.         return ord($data[$pos](ord($data[$pos+1]<< 8(ord($data[$pos+2]<< 16$_ord_24;
  358.     }
  359.  
  360. }

Documentation generated on Tue, 01 Jun 2010 17:05:25 +0200 by phpDocumentor 1.4.3