PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / libraries / vendor / PhpSpreadsheet / Reader / Xls / Escher.php

Escher.php in TablePress – Tables in WordPress made easy 3.4, at libraries/vendor/PhpSpreadsheet/Reader/Xls/Escher.php

620 lines 16.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace TablePress\PhpOffice\PhpSpreadsheet\Reader\Xls;
4
5 use TablePress\PhpOffice\PhpSpreadsheet\Cell\Coordinate;
6 use TablePress\PhpOffice\PhpSpreadsheet\Reader\Xls;
7 use TablePress\PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer;
8 use TablePress\PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
9 use TablePress\PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer\SpContainer;
10 use TablePress\PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer;
11 use TablePress\PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer;
12 use TablePress\PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE;
13 use TablePress\PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE\Blip;
14
15 /**
16 * @template T of BSE|BstoreContainer|DgContainer|DggContainer|\TablePress\PhpOffice\PhpSpreadsheet\Shared\Escher|SpContainer|SpgrContainer
17 */
18 class Escher
19 {
20 const DGGCONTAINER = 0xF000;
21 const BSTORECONTAINER = 0xF001;
22 const DGCONTAINER = 0xF002;
23 const SPGRCONTAINER = 0xF003;
24 const SPCONTAINER = 0xF004;
25 const DGG = 0xF006;
26 const BSE = 0xF007;
27 const DG = 0xF008;
28 const SPGR = 0xF009;
29 const SP = 0xF00A;
30 const OPT = 0xF00B;
31 const CLIENTTEXTBOX = 0xF00D;
32 const CLIENTANCHOR = 0xF010;
33 const CLIENTDATA = 0xF011;
34 const BLIPJPEG = 0xF01D;
35 const BLIPPNG = 0xF01E;
36 const SPLITMENUCOLORS = 0xF11E;
37 const TERTIARYOPT = 0xF122;
38
39 /**
40 * Escher stream data (binary).
41 */
42 private string $data;
43
44 /**
45 * Size in bytes of the Escher stream data.
46 */
47 private int $dataSize;
48
49 /**
50 * Current position of stream pointer in Escher stream data.
51 */
52 private int $pos;
53
54 /**
55 * The object to be returned by the reader. Modified during load.
56 *
57 * @var T
58 */
59 private $object;
60
61 /**
62 * Create a new Escher instance.
63 *
64 * @param T $object
65 */
66 public function __construct($object)
67 {
68 $this->object = $object;
69 }
70
71 private const WHICH_ROUTINE = [
72 self::DGGCONTAINER => 'readDggContainer',
73 self::DGG => 'readDgg',
74 self::BSTORECONTAINER => 'readBstoreContainer',
75 self::BSE => 'readBSE',
76 self::BLIPJPEG => 'readBlipJPEG',
77 self::BLIPPNG => 'readBlipPNG',
78 self::OPT => 'readOPT',
79 self::TERTIARYOPT => 'readTertiaryOPT',
80 self::SPLITMENUCOLORS => 'readSplitMenuColors',
81 self::DGCONTAINER => 'readDgContainer',
82 self::DG => 'readDg',
83 self::SPGRCONTAINER => 'readSpgrContainer',
84 self::SPCONTAINER => 'readSpContainer',
85 self::SPGR => 'readSpgr',
86 self::SP => 'readSp',
87 self::CLIENTTEXTBOX => 'readClientTextbox',
88 self::CLIENTANCHOR => 'readClientAnchor',
89 self::CLIENTDATA => 'readClientData',
90 ];
91
92 /**
93 * Load Escher stream data. May be a partial Escher stream.
94 *
95 * @return T
96 */
97 public function load(string $data)
98 {
99 $this->data = $data;
100
101 // total byte size of Excel data (workbook global substream + sheet substreams)
102 $this->dataSize = strlen($this->data);
103
104 $this->pos = 0;
105
106 // Parse Escher stream
107 while ($this->pos < $this->dataSize) {
108 // offset: 2; size: 2: Record Type
109 $fbt = Xls::getUInt2d($this->data, $this->pos + 2);
110 $routine = self::WHICH_ROUTINE[$fbt] ?? 'readDefault';
111 if (method_exists($this, $routine)) {
112 $this->$routine();
113 }
114 }
115
116 return $this->object;
117 }
118
119 /**
120 * Read a generic record.
121 */
122 private function readDefault(): void
123 {
124 // offset 0; size: 2; recVer and recInstance
125 //$verInstance = Xls::getUInt2d($this->data, $this->pos);
126
127 // offset: 2; size: 2: Record Type
128 //$fbt = Xls::getUInt2d($this->data, $this->pos + 2);
129
130 // bit: 0-3; mask: 0x000F; recVer
131 //$recVer = (0x000F & $verInstance) >> 0;
132
133 $length = Xls::getInt4d($this->data, $this->pos + 4);
134 //$recordData = substr($this->data, $this->pos + 8, $length);
135
136 // move stream pointer to next record
137 $this->pos += 8 + $length;
138 }
139
140 /**
141 * Read DggContainer record (Drawing Group Container).
142 */
143 private function readDggContainer(): void
144 {
145 $length = Xls::getInt4d($this->data, $this->pos + 4);
146 $recordData = (string) substr($this->data, $this->pos + 8, $length);
147
148 // move stream pointer to next record
149 $this->pos += 8 + $length;
150
151 // record is a container, read contents
152 $dggContainer = new DggContainer();
153 $this->applyAttribute('setDggContainer', $dggContainer);
154 $reader = new self($dggContainer);
155 $reader->load($recordData);
156 }
157
158 /**
159 * Read Dgg record (Drawing Group).
160 */
161 private function readDgg(): void
162 {
163 $length = Xls::getInt4d($this->data, $this->pos + 4);
164 //$recordData = substr($this->data, $this->pos + 8, $length);
165
166 // move stream pointer to next record
167 $this->pos += 8 + $length;
168 }
169
170 /**
171 * Read BstoreContainer record (Blip Store Container).
172 */
173 private function readBstoreContainer(): void
174 {
175 $length = Xls::getInt4d($this->data, $this->pos + 4);
176 $recordData = (string) substr($this->data, $this->pos + 8, $length);
177
178 // move stream pointer to next record
179 $this->pos += 8 + $length;
180
181 // record is a container, read contents
182 $bstoreContainer = new BstoreContainer();
183 $this->applyAttribute('setBstoreContainer', $bstoreContainer);
184 $reader = new self($bstoreContainer);
185 $reader->load($recordData);
186 }
187
188 /**
189 * Read BSE record.
190 */
191 private function readBSE(): void
192 {
193 // offset: 0; size: 2; recVer and recInstance
194
195 // bit: 4-15; mask: 0xFFF0; recInstance
196 $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
197
198 $length = Xls::getInt4d($this->data, $this->pos + 4);
199 $recordData = (string) substr($this->data, $this->pos + 8, $length);
200
201 // move stream pointer to next record
202 $this->pos += 8 + $length;
203
204 // add BSE to BstoreContainer
205 $BSE = new BSE();
206 $this->applyAttribute('addBSE', $BSE);
207
208 $BSE->setBLIPType($recInstance);
209
210 // offset: 0; size: 1; btWin32 (MSOBLIPTYPE)
211 //$btWin32 = ord($recordData[0]);
212
213 // offset: 1; size: 1; btWin32 (MSOBLIPTYPE)
214 //$btMacOS = ord($recordData[1]);
215
216 // offset: 2; size: 16; MD4 digest
217 //$rgbUid = substr($recordData, 2, 16);
218
219 // offset: 18; size: 2; tag
220 //$tag = Xls::getUInt2d($recordData, 18);
221
222 // offset: 20; size: 4; size of BLIP in bytes
223 //$size = Xls::getInt4d($recordData, 20);
224
225 // offset: 24; size: 4; number of references to this BLIP
226 //$cRef = Xls::getInt4d($recordData, 24);
227
228 // offset: 28; size: 4; MSOFO file offset
229 //$foDelay = Xls::getInt4d($recordData, 28);
230
231 // offset: 32; size: 1; unused1
232 //$unused1 = ord($recordData[32]);
233
234 // offset: 33; size: 1; size of nameData in bytes (including null terminator)
235 $cbName = ord($recordData[33]);
236
237 // offset: 34; size: 1; unused2
238 //$unused2 = ord($recordData[34]);
239
240 // offset: 35; size: 1; unused3
241 //$unused3 = ord($recordData[35]);
242
243 // offset: 36; size: $cbName; nameData
244 //$nameData = substr($recordData, 36, $cbName);
245
246 // offset: 36 + $cbName, size: var; the BLIP data
247 $blipData = (string) substr($recordData, 36 + $cbName);
248
249 // record is a container, read contents
250 $reader = new self($BSE);
251 $reader->load($blipData);
252 }
253
254 /**
255 * Read BlipJPEG record. Holds raw JPEG image data.
256 */
257 private function readBlipJPEG(): void
258 {
259 // offset: 0; size: 2; recVer and recInstance
260
261 // bit: 4-15; mask: 0xFFF0; recInstance
262 $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
263
264 $length = Xls::getInt4d($this->data, $this->pos + 4);
265 $recordData = (string) substr($this->data, $this->pos + 8, $length);
266
267 // move stream pointer to next record
268 $this->pos += 8 + $length;
269
270 $pos = 0;
271
272 // offset: 0; size: 16; rgbUid1 (MD4 digest of)
273 //$rgbUid1 = substr($recordData, 0, 16);
274 $pos += 16;
275
276 // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
277 if (in_array($recInstance, [0x046B, 0x06E3])) {
278 //$rgbUid2 = substr($recordData, 16, 16);
279 $pos += 16;
280 }
281
282 // offset: var; size: 1; tag
283 //$tag = ord($recordData[$pos]);
284 ++$pos;
285
286 // offset: var; size: var; the raw image data
287 $data = (string) substr($recordData, $pos);
288
289 $blip = new Blip();
290 $blip->setData($data);
291
292 $this->applyAttribute('setBlip', $blip);
293 }
294
295 /**
296 * Read BlipPNG record. Holds raw PNG image data.
297 */
298 private function readBlipPNG(): void
299 {
300 // offset: 0; size: 2; recVer and recInstance
301
302 // bit: 4-15; mask: 0xFFF0; recInstance
303 $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
304
305 $length = Xls::getInt4d($this->data, $this->pos + 4);
306 $recordData = (string) substr($this->data, $this->pos + 8, $length);
307
308 // move stream pointer to next record
309 $this->pos += 8 + $length;
310
311 $pos = 0;
312
313 // offset: 0; size: 16; rgbUid1 (MD4 digest of)
314 //$rgbUid1 = substr($recordData, 0, 16);
315 $pos += 16;
316
317 // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
318 if ($recInstance == 0x06E1) {
319 //$rgbUid2 = substr($recordData, 16, 16);
320 $pos += 16;
321 }
322
323 // offset: var; size: 1; tag
324 //$tag = ord($recordData[$pos]);
325 ++$pos;
326
327 // offset: var; size: var; the raw image data
328 $data = (string) substr($recordData, $pos);
329
330 $blip = new Blip();
331 $blip->setData($data);
332
333 $this->applyAttribute('setBlip', $blip);
334 }
335
336 /**
337 * Read OPT record. This record may occur within DggContainer record or SpContainer.
338 */
339 private function readOPT(): void
340 {
341 // offset: 0; size: 2; recVer and recInstance
342
343 // bit: 4-15; mask: 0xFFF0; recInstance
344 $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
345
346 $length = Xls::getInt4d($this->data, $this->pos + 4);
347 $recordData = (string) substr($this->data, $this->pos + 8, $length);
348
349 // move stream pointer to next record
350 $this->pos += 8 + $length;
351
352 $this->readOfficeArtRGFOPTE($recordData, $recInstance);
353 }
354
355 /**
356 * Read TertiaryOPT record.
357 */
358 private function readTertiaryOPT(): void
359 {
360 // offset: 0; size: 2; recVer and recInstance
361
362 // bit: 4-15; mask: 0xFFF0; recInstance
363 //$recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
364
365 $length = Xls::getInt4d($this->data, $this->pos + 4);
366 //$recordData = substr($this->data, $this->pos + 8, $length);
367
368 // move stream pointer to next record
369 $this->pos += 8 + $length;
370 }
371
372 /**
373 * Read SplitMenuColors record.
374 */
375 private function readSplitMenuColors(): void
376 {
377 $length = Xls::getInt4d($this->data, $this->pos + 4);
378 //$recordData = substr($this->data, $this->pos + 8, $length);
379
380 // move stream pointer to next record
381 $this->pos += 8 + $length;
382 }
383
384 /**
385 * Read DgContainer record (Drawing Container).
386 */
387 private function readDgContainer(): void
388 {
389 $length = Xls::getInt4d($this->data, $this->pos + 4);
390 $recordData = (string) substr($this->data, $this->pos + 8, $length);
391
392 // move stream pointer to next record
393 $this->pos += 8 + $length;
394
395 // record is a container, read contents
396 $dgContainer = new DgContainer();
397 $this->applyAttribute('setDgContainer', $dgContainer);
398 $reader = new self($dgContainer);
399 $reader->load($recordData);
400 }
401
402 /**
403 * Read Dg record (Drawing).
404 */
405 private function readDg(): void
406 {
407 $length = Xls::getInt4d($this->data, $this->pos + 4);
408 //$recordData = substr($this->data, $this->pos + 8, $length);
409
410 // move stream pointer to next record
411 $this->pos += 8 + $length;
412 }
413
414 /**
415 * Read SpgrContainer record (Shape Group Container).
416 */
417 private function readSpgrContainer(): void
418 {
419 // context is either context DgContainer or SpgrContainer
420
421 $length = Xls::getInt4d($this->data, $this->pos + 4);
422 $recordData = (string) substr($this->data, $this->pos + 8, $length);
423
424 // move stream pointer to next record
425 $this->pos += 8 + $length;
426
427 // record is a container, read contents
428 $spgrContainer = new SpgrContainer();
429
430 if ($this->object instanceof DgContainer) {
431 // DgContainer
432 $this->object->setSpgrContainer($spgrContainer);
433 } elseif ($this->object instanceof SpgrContainer) {
434 // SpgrContainer
435 $this->object->addChild($spgrContainer);
436 }
437
438 $reader = new self($spgrContainer);
439 $reader->load($recordData);
440 }
441
442 /**
443 * Read SpContainer record (Shape Container).
444 */
445 private function readSpContainer(): void
446 {
447 $length = Xls::getInt4d($this->data, $this->pos + 4);
448 $recordData = (string) substr($this->data, $this->pos + 8, $length);
449
450 // add spContainer to spgrContainer
451 $spContainer = new SpContainer();
452 $this->applyAttribute('addChild', $spContainer);
453
454 // move stream pointer to next record
455 $this->pos += 8 + $length;
456
457 // record is a container, read contents
458 $reader = new self($spContainer);
459 $reader->load($recordData);
460 }
461
462 /**
463 * Read Spgr record (Shape Group).
464 */
465 private function readSpgr(): void
466 {
467 $length = Xls::getInt4d($this->data, $this->pos + 4);
468 //$recordData = substr($this->data, $this->pos + 8, $length);
469
470 // move stream pointer to next record
471 $this->pos += 8 + $length;
472 }
473
474 /**
475 * Read Sp record (Shape).
476 */
477 private function readSp(): void
478 {
479 // offset: 0; size: 2; recVer and recInstance
480
481 // bit: 4-15; mask: 0xFFF0; recInstance
482 //$recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
483
484 $length = Xls::getInt4d($this->data, $this->pos + 4);
485 //$recordData = substr($this->data, $this->pos + 8, $length);
486
487 // move stream pointer to next record
488 $this->pos += 8 + $length;
489 }
490
491 /**
492 * Read ClientTextbox record.
493 */
494 private function readClientTextbox(): void
495 {
496 // offset: 0; size: 2; recVer and recInstance
497
498 // bit: 4-15; mask: 0xFFF0; recInstance
499 //$recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
500
501 $length = Xls::getInt4d($this->data, $this->pos + 4);
502 //$recordData = substr($this->data, $this->pos + 8, $length);
503
504 // move stream pointer to next record
505 $this->pos += 8 + $length;
506 }
507
508 /**
509 * Read ClientAnchor record. This record holds information about where the shape is anchored in worksheet.
510 */
511 private function readClientAnchor(): void
512 {
513 $length = Xls::getInt4d($this->data, $this->pos + 4);
514 $recordData = (string) substr($this->data, $this->pos + 8, $length);
515
516 // move stream pointer to next record
517 $this->pos += 8 + $length;
518
519 // offset: 2; size: 2; upper-left corner column index (0-based)
520 $c1 = Xls::getUInt2d($recordData, 2);
521
522 // offset: 4; size: 2; upper-left corner horizontal offset in 1/1024 of column width
523 $startOffsetX = Xls::getUInt2d($recordData, 4);
524
525 // offset: 6; size: 2; upper-left corner row index (0-based)
526 $r1 = Xls::getUInt2d($recordData, 6);
527
528 // offset: 8; size: 2; upper-left corner vertical offset in 1/256 of row height
529 $startOffsetY = Xls::getUInt2d($recordData, 8);
530
531 // offset: 10; size: 2; bottom-right corner column index (0-based)
532 $c2 = Xls::getUInt2d($recordData, 10);
533
534 // offset: 12; size: 2; bottom-right corner horizontal offset in 1/1024 of column width
535 $endOffsetX = Xls::getUInt2d($recordData, 12);
536
537 // offset: 14; size: 2; bottom-right corner row index (0-based)
538 $r2 = Xls::getUInt2d($recordData, 14);
539
540 // offset: 16; size: 2; bottom-right corner vertical offset in 1/256 of row height
541 $endOffsetY = Xls::getUInt2d($recordData, 16);
542
543 $this->applyAttribute('setStartCoordinates', Coordinate::stringFromColumnIndex($c1 + 1) . ($r1 + 1));
544 $this->applyAttribute('setStartOffsetX', $startOffsetX);
545 $this->applyAttribute('setStartOffsetY', $startOffsetY);
546 $this->applyAttribute('setEndCoordinates', Coordinate::stringFromColumnIndex($c2 + 1) . ($r2 + 1));
547 $this->applyAttribute('setEndOffsetX', $endOffsetX);
548 $this->applyAttribute('setEndOffsetY', $endOffsetY);
549 }
550
551 /**
552 * @param mixed $value
553 */
554 private function applyAttribute(string $name, $value): void
555 {
556 if (method_exists($this->object, $name)) {
557 $this->object->$name($value);
558 }
559 }
560
561 /**
562 * Read ClientData record.
563 */
564 private function readClientData(): void
565 {
566 $length = Xls::getInt4d($this->data, $this->pos + 4);
567 //$recordData = substr($this->data, $this->pos + 8, $length);
568
569 // move stream pointer to next record
570 $this->pos += 8 + $length;
571 }
572
573 /**
574 * Read OfficeArtRGFOPTE table of property-value pairs.
575 *
576 * @param string $data Binary data
577 * @param int $n Number of properties
578 */
579 private function readOfficeArtRGFOPTE(string $data, int $n): void
580 {
581 $splicedComplexData = (string) substr($data, 6 * $n);
582
583 // loop through property-value pairs
584 for ($i = 0; $i < $n; ++$i) {
585 // read 6 bytes at a time
586 $fopte = (string) substr($data, 6 * $i, 6);
587
588 // offset: 0; size: 2; opid
589 $opid = Xls::getUInt2d($fopte, 0);
590
591 // bit: 0-13; mask: 0x3FFF; opid.opid
592 $opidOpid = (0x3FFF & $opid) >> 0;
593
594 // bit: 14; mask 0x4000; 1 = value in op field is BLIP identifier
595 //$opidFBid = (0x4000 & $opid) >> 14;
596
597 // bit: 15; mask 0x8000; 1 = this is a complex property, op field specifies size of complex data
598 $opidFComplex = (0x8000 & $opid) >> 15;
599
600 // offset: 2; size: 4; the value for this property
601 $op = Xls::getInt4d($fopte, 2);
602
603 if ($opidFComplex) {
604 $complexData = (string) substr($splicedComplexData, 0, $op);
605 $splicedComplexData = (string) substr($splicedComplexData, $op);
606
607 // we store string value with complex data
608 $value = $complexData;
609 } else {
610 // we store integer value
611 $value = $op;
612 }
613
614 if (method_exists($this->object, 'setOPT')) {
615 $this->object->setOPT($opidOpid, $value);
616 }
617 }
618 }
619 }
620