PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.4.11
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.4.11
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
visualizer / vendor / phpoffice / phpspreadsheet / src / PhpSpreadsheet / Shared / OLERead.php

OLERead.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.4.11, at vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php

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