1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 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:
35: const IDENTIFIER_OLE = IDENTIFIER_OLE;
36:
37:
38: const BIG_BLOCK_SIZE = 0x200;
39:
40:
41: const SMALL_BLOCK_SIZE = 0x40;
42:
43:
44: const PROPERTY_STORAGE_BLOCK_SIZE = 0x80;
45:
46:
47: const SMALL_BLOCK_THRESHOLD = 0x1000;
48:
49:
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:
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: 72: 73: 74: 75:
76: public function read($sFileName)
77: {
78:
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:
84:
85: $this->data = file_get_contents($sFileName, FALSE, NULL, 0, 8);
86:
87:
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:
93: $this->data = file_get_contents($sFileName);
94:
95:
96: $this->numBigBlockDepotBlocks = self::_GetInt4d($this->data, self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
97:
98:
99: $this->rootStartBlock = self::_GetInt4d($this->data, self::ROOT_START_BLOCK_POS);
100:
101:
102: $this->sbdStartBlock = self::_GetInt4d($this->data, self::SMALL_BLOCK_DEPOT_BLOCK_POS);
103:
104:
105: $this->extensionBlock = self::_GetInt4d($this->data, self::EXTENSION_BLOCK_POS);
106:
107:
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:
162: $block = $this->rootStartBlock;
163: $this->entry = $this->_readData($block);
164:
165: $this->_readPropertySets();
166: }
167:
168: 169: 170: 171: 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: 216: 217: 218: 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: 235:
236: private function _readPropertySets() {
237: $offset = 0;
238:
239:
240: $entryLen = strlen($this->entry);
241: while ($offset < $entryLen) {
242:
243: $d = substr($this->entry, $offset, self::PROPERTY_STORAGE_BLOCK_SIZE);
244:
245:
246: $nameSize = ord($d[self::SIZE_OF_NAME_POS]) | (ord($d[self::SIZE_OF_NAME_POS+1]) << 8);
247:
248:
249: $type = ord($d[self::TYPE_POS]);
250:
251:
252:
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:
267: $upName = strtoupper($name);
268:
269:
270: if (($upName === 'WORKBOOK') || ($upName === 'BOOK')) {
271: $this->wrkbook = count($this->props) - 1;
272: }
273: else if ( $upName === 'ROOT ENTRY' || $upName === 'R') {
274:
275: $this->rootentry = count($this->props) - 1;
276: }
277:
278:
279: if ($name == chr(5) . 'SummaryInformation') {
280:
281: $this->summaryInformation = count($this->props) - 1;
282: }
283:
284:
285: if ($name == chr(5) . 'DocumentSummaryInformation') {
286:
287: $this->documentSummaryInformation = count($this->props) - 1;
288: }
289:
290: $offset += self::PROPERTY_STORAGE_BLOCK_SIZE;
291: }
292:
293: }
294:
295: 296: 297: 298: 299: 300: 301:
302: private static function _GetInt4d($data, $pos)
303: {
304:
305:
306:
307: $_or_24 = ord($data[$pos + 3]);
308: if ($_or_24 >= 128) {
309:
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: }
318: