PageRenderTime 95ms CodeModel.GetById 40ms app.highlight 15ms RepoModel.GetById 35ms app.codeStats 0ms

/htdocs/includes/phpexcel/PHPExcel/Shared/OLERead.php

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