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', 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1));
  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->data, 0, 8) != 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->data, self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
  91.  
  92.         // SecID of the first sector of the directory stream
  93.         $this->rootStartBlock = self::_GetInt4d($this->data, self::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->data, self::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->data, self::EXTENSION_BLOCK_POS);
  100.  
  101.         // Total number of sectors used by MSAT
  102.         $this->numExtensionBlocks = self::_GetInt4d($this->data, self::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[$i] = self::_GetInt4d($this->data, $pos);
  115.               $pos += 4;
  116.         }
  117.  
  118.         for ($j = 0; $j < $this->numExtensionBlocks; ++$j) {
  119.             $pos = ($this->extensionBlock + 1) * self::BIG_BLOCK_SIZE;
  120.             $blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, self::BIG_BLOCK_SIZE / 4 - 1);
  121.  
  122.             for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; ++$i) {
  123.                 $bigBlockDepotBlocks[$i] = self::_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[$i] + 1) * self::BIG_BLOCK_SIZE;
  139.  
  140.             for ($j = 0 ; $j < $bbs; ++$j) {
  141.                 $this->bigBlockChain[$index] = self::_GetInt4d($this->data, $pos);
  142.                 $pos += 4 ;
  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 + 1) * self::BIG_BLOCK_SIZE;
  153.  
  154.             for ($j = 0; $j < $bbs; ++$j) {
  155.                 $this->smallBlockChain[$index] = self::_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, $pos, self::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 == 0) return '';
  203.  
  204.             $block = $this->props[$stream]['startBlock'];
  205.  
  206.             while ($block != -2) {
  207.                 $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
  208.                 $streamData .= substr($this->data, $pos, self::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 + 1) * self::BIG_BLOCK_SIZE;
  229.             $data .= substr($this->data, $pos, self::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, $offset, self::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($d, self::START_BLOCK_POS);
  256.  
  257.             $size = self::_GetInt4d($d, self::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->props) - 1;
  270.             }
  271.  
  272.             // Root entry
  273.             if ($name == 'Root Entry' || $name == 'ROOT ENTRY' || $name == 'R') {
  274.                 $this->rootentry = count($this->props) - 1;
  275.             }
  276.  
  277.             // Summary information
  278.             if ($name == chr(5) . 'SummaryInformation') {
  279. //                echo 'Summary Information<br />';
  280.                 $this->summaryInformation = count($this->props) - 1;
  281.             }
  282.  
  283.             // Additional Document Summary information
  284.             if ($name == chr(5) . 'DocumentSummaryInformation') {
  285. //                echo 'Document Summary Information<br />';
  286.                 $this->documentSummaryInformation = count($this->props) - 1;
  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