Subversion Repositories eFlore/Applications.cel

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2388 jpm 1
<?php
2
/**
3
 * PHPExcel
4
 *
5
 * Copyright (c) 2006 - 2013 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 - 2013 PHPExcel (http://www.codeplex.com/PHPExcel)
24
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt	LGPL
25
 * @version    ##VERSION##, ##DATE##
26
 */
27
 
28
defined('IDENTIFIER_OLE') ||
29
    define('IDENTIFIER_OLE', pack('CCCCCCCC', 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1));
30
 
31
class PHPExcel_Shared_OLERead {
32
	private $data = '';
33
 
34
	// OLE identifier
35
	const IDENTIFIER_OLE = IDENTIFIER_OLE;
36
 
37
	// Size of a sector = 512 bytes
38
	const BIG_BLOCK_SIZE					= 0x200;
39
 
40
	// Size of a short sector = 64 bytes
41
	const SMALL_BLOCK_SIZE					= 0x40;
42
 
43
	// Size of a directory entry always = 128 bytes
44
	const PROPERTY_STORAGE_BLOCK_SIZE		= 0x80;
45
 
46
	// Minimum size of a standard stream = 4096 bytes, streams smaller than this are stored as short streams
47
	const SMALL_BLOCK_THRESHOLD				= 0x1000;
48
 
49
	// header offsets
50
	const NUM_BIG_BLOCK_DEPOT_BLOCKS_POS	= 0x2c;
51
	const ROOT_START_BLOCK_POS				= 0x30;
52
	const SMALL_BLOCK_DEPOT_BLOCK_POS		= 0x3c;
53
	const EXTENSION_BLOCK_POS				= 0x44;
54
	const NUM_EXTENSION_BLOCK_POS			= 0x48;
55
	const BIG_BLOCK_DEPOT_BLOCKS_POS		= 0x4c;
56
 
57
	// property storage offsets (directory offsets)
58
	const SIZE_OF_NAME_POS					= 0x40;
59
	const TYPE_POS							= 0x42;
60
	const START_BLOCK_POS					= 0x74;
61
	const SIZE_POS							= 0x78;
62
 
63
 
64
 
65
	public $wrkbook						= null;
66
	public $summaryInformation			= null;
67
	public $documentSummaryInformation	= null;
68
 
69
 
70
	/**
71
	 * Read the file
72
	 *
73
	 * @param $sFileName string Filename
74
	 * @throws PHPExcel_Reader_Exception
75
	 */
76
	public function read($sFileName)
77
	{
78
		// Check if file exists and is readable
79
		if(!is_readable($sFileName)) {
80
			throw new PHPExcel_Reader_Exception("Could not open " . $sFileName . " for reading! File does not exist, or it is not readable.");
81
		}
82
 
83
		// Get the file identifier
84
		// Don't bother reading the whole file until we know it's a valid OLE file
85
		$this->data = file_get_contents($sFileName, FALSE, NULL, 0, 8);
86
 
87
		// Check OLE identifier
88
		if ($this->data != self::IDENTIFIER_OLE) {
89
			throw new PHPExcel_Reader_Exception('The filename ' . $sFileName . ' is not recognised as an OLE file');
90
		}
91
 
92
		// Get the file data
93
		$this->data = file_get_contents($sFileName);
94
 
95
		// Total number of sectors used for the SAT
96
		$this->numBigBlockDepotBlocks = self::_GetInt4d($this->data, self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
97
 
98
		// SecID of the first sector of the directory stream
99
		$this->rootStartBlock = self::_GetInt4d($this->data, self::ROOT_START_BLOCK_POS);
100
 
101
		// SecID of the first sector of the SSAT (or -2 if not extant)
102
		$this->sbdStartBlock = self::_GetInt4d($this->data, self::SMALL_BLOCK_DEPOT_BLOCK_POS);
103
 
104
		// SecID of the first sector of the MSAT (or -2 if no additional sectors are used)
105
		$this->extensionBlock = self::_GetInt4d($this->data, self::EXTENSION_BLOCK_POS);
106
 
107
		// Total number of sectors used by MSAT
108
		$this->numExtensionBlocks = self::_GetInt4d($this->data, self::NUM_EXTENSION_BLOCK_POS);
109
 
110
		$bigBlockDepotBlocks = array();
111
		$pos = self::BIG_BLOCK_DEPOT_BLOCKS_POS;
112
 
113
		$bbdBlocks = $this->numBigBlockDepotBlocks;
114
 
115
		if ($this->numExtensionBlocks != 0) {
116
			$bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS)/4;
117
		}
118
 
119
		for ($i = 0; $i < $bbdBlocks; ++$i) {
120
			  $bigBlockDepotBlocks[$i] = self::_GetInt4d($this->data, $pos);
121
			  $pos += 4;
122
		}
123
 
124
		for ($j = 0; $j < $this->numExtensionBlocks; ++$j) {
125
			$pos = ($this->extensionBlock + 1) * self::BIG_BLOCK_SIZE;
126
			$blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, self::BIG_BLOCK_SIZE / 4 - 1);
127
 
128
			for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; ++$i) {
129
				$bigBlockDepotBlocks[$i] = self::_GetInt4d($this->data, $pos);
130
				$pos += 4;
131
			}
132
 
133
			$bbdBlocks += $blocksToRead;
134
			if ($bbdBlocks < $this->numBigBlockDepotBlocks) {
135
				$this->extensionBlock = self::_GetInt4d($this->data, $pos);
136
			}
137
		}
138
 
139
		$pos = 0;
140
		$this->bigBlockChain = '';
141
		$bbs = self::BIG_BLOCK_SIZE / 4;
142
		for ($i = 0; $i < $this->numBigBlockDepotBlocks; ++$i) {
143
			$pos = ($bigBlockDepotBlocks[$i] + 1) * self::BIG_BLOCK_SIZE;
144
 
145
			$this->bigBlockChain .= substr($this->data, $pos, 4*$bbs);
146
			$pos += 4*$bbs;
147
		}
148
 
149
		$pos = 0;
150
		$sbdBlock = $this->sbdStartBlock;
151
		$this->smallBlockChain = '';
152
		while ($sbdBlock != -2) {
153
			$pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE;
154
 
155
			$this->smallBlockChain .= substr($this->data, $pos, 4*$bbs);
156
			$pos += 4*$bbs;
157
 
158
			$sbdBlock = self::_GetInt4d($this->bigBlockChain, $sbdBlock*4);
159
		}
160
 
161
		// read the directory stream
162
		$block = $this->rootStartBlock;
163
		$this->entry = $this->_readData($block);
164
 
165
		$this->_readPropertySets();
166
	}
167
 
168
	/**
169
	 * Extract binary stream data
170
	 *
171
	 * @return string
172
	 */
173
	public function getStream($stream)
174
	{
175
		if ($stream === NULL) {
176
			return null;
177
		}
178
 
179
		$streamData = '';
180
 
181
		if ($this->props[$stream]['size'] < self::SMALL_BLOCK_THRESHOLD) {
182
			$rootdata = $this->_readData($this->props[$this->rootentry]['startBlock']);
183
 
184
			$block = $this->props[$stream]['startBlock'];
185
 
186
			while ($block != -2) {
187
	  			$pos = $block * self::SMALL_BLOCK_SIZE;
188
				$streamData .= substr($rootdata, $pos, self::SMALL_BLOCK_SIZE);
189
 
190
				$block = self::_GetInt4d($this->smallBlockChain, $block*4);
191
			}
192
 
193
			return $streamData;
194
		} else {
195
			$numBlocks = $this->props[$stream]['size'] / self::BIG_BLOCK_SIZE;
196
			if ($this->props[$stream]['size'] % self::BIG_BLOCK_SIZE != 0) {
197
				++$numBlocks;
198
			}
199
 
200
			if ($numBlocks == 0) return '';
201
 
202
			$block = $this->props[$stream]['startBlock'];
203
 
204
			while ($block != -2) {
205
				$pos = ($block + 1) * self::BIG_BLOCK_SIZE;
206
				$streamData .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
207
				$block = self::_GetInt4d($this->bigBlockChain, $block*4);
208
			}
209
 
210
			return $streamData;
211
		}
212
	}
213
 
214
	/**
215
	 * Read a standard stream (by joining sectors using information from SAT)
216
	 *
217
	 * @param int $bl Sector ID where the stream starts
218
	 * @return string Data for standard stream
219
	 */
220
	private function _readData($bl)
221
	{
222
		$block = $bl;
223
		$data = '';
224
 
225
		while ($block != -2)  {
226
			$pos = ($block + 1) * self::BIG_BLOCK_SIZE;
227
			$data .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
228
			$block = self::_GetInt4d($this->bigBlockChain, $block*4);
229
		}
230
		return $data;
231
	 }
232
 
233
	/**
234
	 * Read entries in the directory stream.
235
	 */
236
	private function _readPropertySets() {
237
		$offset = 0;
238
 
239
		// loop through entires, each entry is 128 bytes
240
		$entryLen = strlen($this->entry);
241
		while ($offset < $entryLen) {
242
			// entry data (128 bytes)
243
			$d = substr($this->entry, $offset, self::PROPERTY_STORAGE_BLOCK_SIZE);
244
 
245
			// size in bytes of name
246
			$nameSize = ord($d[self::SIZE_OF_NAME_POS]) | (ord($d[self::SIZE_OF_NAME_POS+1]) << 8);
247
 
248
			// type of entry
249
			$type = ord($d[self::TYPE_POS]);
250
 
251
			// sectorID of first sector or short sector, if this entry refers to a stream (the case with workbook)
252
			// sectorID of first sector of the short-stream container stream, if this entry is root entry
253
			$startBlock = self::_GetInt4d($d, self::START_BLOCK_POS);
254
 
255
			$size = self::_GetInt4d($d, self::SIZE_POS);
256
 
257
			$name = str_replace("\x00", "", substr($d,0,$nameSize));
258
 
259
 
260
			$this->props[] = array (
261
				'name' => $name,
262
				'type' => $type,
263
				'startBlock' => $startBlock,
264
				'size' => $size);
265
 
266
			// tmp helper to simplify checks
267
			$upName = strtoupper($name);
268
 
269
			// Workbook directory entry (BIFF5 uses Book, BIFF8 uses Workbook)
270
			if (($upName === 'WORKBOOK') || ($upName === 'BOOK')) {
271
				$this->wrkbook = count($this->props) - 1;
272
			}
273
			else if ( $upName === 'ROOT ENTRY' || $upName === 'R') {
274
				// Root entry
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
}