ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Escher.php
Go to the documentation of this file.
1<?php
2
4
14
15class Escher
16{
17 const DGGCONTAINER = 0xF000;
18 const BSTORECONTAINER = 0xF001;
19 const DGCONTAINER = 0xF002;
20 const SPGRCONTAINER = 0xF003;
21 const SPCONTAINER = 0xF004;
22 const DGG = 0xF006;
23 const BSE = 0xF007;
24 const DG = 0xF008;
25 const SPGR = 0xF009;
26 const SP = 0xF00A;
27 const OPT = 0xF00B;
28 const CLIENTTEXTBOX = 0xF00D;
29 const CLIENTANCHOR = 0xF010;
30 const CLIENTDATA = 0xF011;
31 const BLIPJPEG = 0xF01D;
32 const BLIPPNG = 0xF01E;
33 const SPLITMENUCOLORS = 0xF11E;
34 const TERTIARYOPT = 0xF122;
35
41 private $data;
42
48 private $dataSize;
49
55 private $pos;
56
62 private $object;
63
69 public function __construct($object)
70 {
71 $this->object = $object;
72 }
73
81 public function load($data)
82 {
83 $this->data = $data;
84
85 // total byte size of Excel data (workbook global substream + sheet substreams)
86 $this->dataSize = strlen($this->data);
87
88 $this->pos = 0;
89
90 // Parse Escher stream
91 while ($this->pos < $this->dataSize) {
92 // offset: 2; size: 2: Record Type
93 $fbt = Xls::getUInt2d($this->data, $this->pos + 2);
94
95 switch ($fbt) {
97 $this->readDggContainer();
98
99 break;
100 case self::DGG:
101 $this->readDgg();
102
103 break;
105 $this->readBstoreContainer();
106
107 break;
108 case self::BSE:
109 $this->readBSE();
110
111 break;
112 case self::BLIPJPEG:
113 $this->readBlipJPEG();
114
115 break;
116 case self::BLIPPNG:
117 $this->readBlipPNG();
118
119 break;
120 case self::OPT:
121 $this->readOPT();
122
123 break;
125 $this->readTertiaryOPT();
126
127 break;
129 $this->readSplitMenuColors();
130
131 break;
133 $this->readDgContainer();
134
135 break;
136 case self::DG:
137 $this->readDg();
138
139 break;
141 $this->readSpgrContainer();
142
143 break;
145 $this->readSpContainer();
146
147 break;
148 case self::SPGR:
149 $this->readSpgr();
150
151 break;
152 case self::SP:
153 $this->readSp();
154
155 break;
157 $this->readClientTextbox();
158
159 break;
161 $this->readClientAnchor();
162
163 break;
164 case self::CLIENTDATA:
165 $this->readClientData();
166
167 break;
168 default:
169 $this->readDefault();
170
171 break;
172 }
173 }
174
175 return $this->object;
176 }
177
181 private function readDefault(): void
182 {
183 // offset 0; size: 2; recVer and recInstance
184 $verInstance = Xls::getUInt2d($this->data, $this->pos);
185
186 // offset: 2; size: 2: Record Type
187 $fbt = Xls::getUInt2d($this->data, $this->pos + 2);
188
189 // bit: 0-3; mask: 0x000F; recVer
190 $recVer = (0x000F & $verInstance) >> 0;
191
192 $length = Xls::getInt4d($this->data, $this->pos + 4);
193 $recordData = substr($this->data, $this->pos + 8, $length);
194
195 // move stream pointer to next record
196 $this->pos += 8 + $length;
197 }
198
202 private function readDggContainer(): void
203 {
204 $length = Xls::getInt4d($this->data, $this->pos + 4);
205 $recordData = substr($this->data, $this->pos + 8, $length);
206
207 // move stream pointer to next record
208 $this->pos += 8 + $length;
209
210 // record is a container, read contents
211 $dggContainer = new DggContainer();
212 $this->object->setDggContainer($dggContainer);
213 $reader = new self($dggContainer);
214 $reader->load($recordData);
215 }
216
220 private function readDgg(): void
221 {
222 $length = Xls::getInt4d($this->data, $this->pos + 4);
223 $recordData = substr($this->data, $this->pos + 8, $length);
224
225 // move stream pointer to next record
226 $this->pos += 8 + $length;
227 }
228
232 private function readBstoreContainer(): void
233 {
234 $length = Xls::getInt4d($this->data, $this->pos + 4);
235 $recordData = substr($this->data, $this->pos + 8, $length);
236
237 // move stream pointer to next record
238 $this->pos += 8 + $length;
239
240 // record is a container, read contents
241 $bstoreContainer = new BstoreContainer();
242 $this->object->setBstoreContainer($bstoreContainer);
243 $reader = new self($bstoreContainer);
244 $reader->load($recordData);
245 }
246
250 private function readBSE(): void
251 {
252 // offset: 0; size: 2; recVer and recInstance
253
254 // bit: 4-15; mask: 0xFFF0; recInstance
255 $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
256
257 $length = Xls::getInt4d($this->data, $this->pos + 4);
258 $recordData = substr($this->data, $this->pos + 8, $length);
259
260 // move stream pointer to next record
261 $this->pos += 8 + $length;
262
263 // add BSE to BstoreContainer
264 $BSE = new BSE();
265 $this->object->addBSE($BSE);
266
267 $BSE->setBLIPType($recInstance);
268
269 // offset: 0; size: 1; btWin32 (MSOBLIPTYPE)
270 $btWin32 = ord($recordData[0]);
271
272 // offset: 1; size: 1; btWin32 (MSOBLIPTYPE)
273 $btMacOS = ord($recordData[1]);
274
275 // offset: 2; size: 16; MD4 digest
276 $rgbUid = substr($recordData, 2, 16);
277
278 // offset: 18; size: 2; tag
279 $tag = Xls::getUInt2d($recordData, 18);
280
281 // offset: 20; size: 4; size of BLIP in bytes
282 $size = Xls::getInt4d($recordData, 20);
283
284 // offset: 24; size: 4; number of references to this BLIP
285 $cRef = Xls::getInt4d($recordData, 24);
286
287 // offset: 28; size: 4; MSOFO file offset
288 $foDelay = Xls::getInt4d($recordData, 28);
289
290 // offset: 32; size: 1; unused1
291 $unused1 = ord($recordData[32]);
292
293 // offset: 33; size: 1; size of nameData in bytes (including null terminator)
294 $cbName = ord($recordData[33]);
295
296 // offset: 34; size: 1; unused2
297 $unused2 = ord($recordData[34]);
298
299 // offset: 35; size: 1; unused3
300 $unused3 = ord($recordData[35]);
301
302 // offset: 36; size: $cbName; nameData
303 $nameData = substr($recordData, 36, $cbName);
304
305 // offset: 36 + $cbName, size: var; the BLIP data
306 $blipData = substr($recordData, 36 + $cbName);
307
308 // record is a container, read contents
309 $reader = new self($BSE);
310 $reader->load($blipData);
311 }
312
316 private function readBlipJPEG(): void
317 {
318 // offset: 0; size: 2; recVer and recInstance
319
320 // bit: 4-15; mask: 0xFFF0; recInstance
321 $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
322
323 $length = Xls::getInt4d($this->data, $this->pos + 4);
324 $recordData = substr($this->data, $this->pos + 8, $length);
325
326 // move stream pointer to next record
327 $this->pos += 8 + $length;
328
329 $pos = 0;
330
331 // offset: 0; size: 16; rgbUid1 (MD4 digest of)
332 $rgbUid1 = substr($recordData, 0, 16);
333 $pos += 16;
334
335 // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
336 if (in_array($recInstance, [0x046B, 0x06E3])) {
337 $rgbUid2 = substr($recordData, 16, 16);
338 $pos += 16;
339 }
340
341 // offset: var; size: 1; tag
342 $tag = ord($recordData[$pos]);
343 ++$pos;
344
345 // offset: var; size: var; the raw image data
346 $data = substr($recordData, $pos);
347
348 $blip = new Blip();
349 $blip->setData($data);
350
351 $this->object->setBlip($blip);
352 }
353
357 private function readBlipPNG(): void
358 {
359 // offset: 0; size: 2; recVer and recInstance
360
361 // bit: 4-15; mask: 0xFFF0; recInstance
362 $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
363
364 $length = Xls::getInt4d($this->data, $this->pos + 4);
365 $recordData = substr($this->data, $this->pos + 8, $length);
366
367 // move stream pointer to next record
368 $this->pos += 8 + $length;
369
370 $pos = 0;
371
372 // offset: 0; size: 16; rgbUid1 (MD4 digest of)
373 $rgbUid1 = substr($recordData, 0, 16);
374 $pos += 16;
375
376 // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
377 if ($recInstance == 0x06E1) {
378 $rgbUid2 = substr($recordData, 16, 16);
379 $pos += 16;
380 }
381
382 // offset: var; size: 1; tag
383 $tag = ord($recordData[$pos]);
384 ++$pos;
385
386 // offset: var; size: var; the raw image data
387 $data = substr($recordData, $pos);
388
389 $blip = new Blip();
390 $blip->setData($data);
391
392 $this->object->setBlip($blip);
393 }
394
398 private function readOPT(): void
399 {
400 // offset: 0; size: 2; recVer and recInstance
401
402 // bit: 4-15; mask: 0xFFF0; recInstance
403 $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
404
405 $length = Xls::getInt4d($this->data, $this->pos + 4);
406 $recordData = substr($this->data, $this->pos + 8, $length);
407
408 // move stream pointer to next record
409 $this->pos += 8 + $length;
410
411 $this->readOfficeArtRGFOPTE($recordData, $recInstance);
412 }
413
417 private function readTertiaryOPT(): void
418 {
419 // offset: 0; size: 2; recVer and recInstance
420
421 // bit: 4-15; mask: 0xFFF0; recInstance
422 $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
423
424 $length = Xls::getInt4d($this->data, $this->pos + 4);
425 $recordData = substr($this->data, $this->pos + 8, $length);
426
427 // move stream pointer to next record
428 $this->pos += 8 + $length;
429 }
430
434 private function readSplitMenuColors(): void
435 {
436 $length = Xls::getInt4d($this->data, $this->pos + 4);
437 $recordData = substr($this->data, $this->pos + 8, $length);
438
439 // move stream pointer to next record
440 $this->pos += 8 + $length;
441 }
442
446 private function readDgContainer(): void
447 {
448 $length = Xls::getInt4d($this->data, $this->pos + 4);
449 $recordData = substr($this->data, $this->pos + 8, $length);
450
451 // move stream pointer to next record
452 $this->pos += 8 + $length;
453
454 // record is a container, read contents
455 $dgContainer = new DgContainer();
456 $this->object->setDgContainer($dgContainer);
457 $reader = new self($dgContainer);
458 $escher = $reader->load($recordData);
459 }
460
464 private function readDg(): void
465 {
466 $length = Xls::getInt4d($this->data, $this->pos + 4);
467 $recordData = substr($this->data, $this->pos + 8, $length);
468
469 // move stream pointer to next record
470 $this->pos += 8 + $length;
471 }
472
476 private function readSpgrContainer(): void
477 {
478 // context is either context DgContainer or SpgrContainer
479
480 $length = Xls::getInt4d($this->data, $this->pos + 4);
481 $recordData = substr($this->data, $this->pos + 8, $length);
482
483 // move stream pointer to next record
484 $this->pos += 8 + $length;
485
486 // record is a container, read contents
487 $spgrContainer = new SpgrContainer();
488
489 if ($this->object instanceof DgContainer) {
490 // DgContainer
491 $this->object->setSpgrContainer($spgrContainer);
492 } else {
493 // SpgrContainer
494 $this->object->addChild($spgrContainer);
495 }
496
497 $reader = new self($spgrContainer);
498 $escher = $reader->load($recordData);
499 }
500
504 private function readSpContainer(): void
505 {
506 $length = Xls::getInt4d($this->data, $this->pos + 4);
507 $recordData = substr($this->data, $this->pos + 8, $length);
508
509 // add spContainer to spgrContainer
510 $spContainer = new SpContainer();
511 $this->object->addChild($spContainer);
512
513 // move stream pointer to next record
514 $this->pos += 8 + $length;
515
516 // record is a container, read contents
517 $reader = new self($spContainer);
518 $escher = $reader->load($recordData);
519 }
520
524 private function readSpgr(): void
525 {
526 $length = Xls::getInt4d($this->data, $this->pos + 4);
527 $recordData = substr($this->data, $this->pos + 8, $length);
528
529 // move stream pointer to next record
530 $this->pos += 8 + $length;
531 }
532
536 private function readSp(): void
537 {
538 // offset: 0; size: 2; recVer and recInstance
539
540 // bit: 4-15; mask: 0xFFF0; recInstance
541 $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
542
543 $length = Xls::getInt4d($this->data, $this->pos + 4);
544 $recordData = substr($this->data, $this->pos + 8, $length);
545
546 // move stream pointer to next record
547 $this->pos += 8 + $length;
548 }
549
553 private function readClientTextbox(): void
554 {
555 // offset: 0; size: 2; recVer and recInstance
556
557 // bit: 4-15; mask: 0xFFF0; recInstance
558 $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
559
560 $length = Xls::getInt4d($this->data, $this->pos + 4);
561 $recordData = substr($this->data, $this->pos + 8, $length);
562
563 // move stream pointer to next record
564 $this->pos += 8 + $length;
565 }
566
570 private function readClientAnchor(): void
571 {
572 $length = Xls::getInt4d($this->data, $this->pos + 4);
573 $recordData = substr($this->data, $this->pos + 8, $length);
574
575 // move stream pointer to next record
576 $this->pos += 8 + $length;
577
578 // offset: 2; size: 2; upper-left corner column index (0-based)
579 $c1 = Xls::getUInt2d($recordData, 2);
580
581 // offset: 4; size: 2; upper-left corner horizontal offset in 1/1024 of column width
582 $startOffsetX = Xls::getUInt2d($recordData, 4);
583
584 // offset: 6; size: 2; upper-left corner row index (0-based)
585 $r1 = Xls::getUInt2d($recordData, 6);
586
587 // offset: 8; size: 2; upper-left corner vertical offset in 1/256 of row height
588 $startOffsetY = Xls::getUInt2d($recordData, 8);
589
590 // offset: 10; size: 2; bottom-right corner column index (0-based)
591 $c2 = Xls::getUInt2d($recordData, 10);
592
593 // offset: 12; size: 2; bottom-right corner horizontal offset in 1/1024 of column width
594 $endOffsetX = Xls::getUInt2d($recordData, 12);
595
596 // offset: 14; size: 2; bottom-right corner row index (0-based)
597 $r2 = Xls::getUInt2d($recordData, 14);
598
599 // offset: 16; size: 2; bottom-right corner vertical offset in 1/256 of row height
600 $endOffsetY = Xls::getUInt2d($recordData, 16);
601
602 // set the start coordinates
603 $this->object->setStartCoordinates(Coordinate::stringFromColumnIndex($c1 + 1) . ($r1 + 1));
604
605 // set the start offsetX
606 $this->object->setStartOffsetX($startOffsetX);
607
608 // set the start offsetY
609 $this->object->setStartOffsetY($startOffsetY);
610
611 // set the end coordinates
612 $this->object->setEndCoordinates(Coordinate::stringFromColumnIndex($c2 + 1) . ($r2 + 1));
613
614 // set the end offsetX
615 $this->object->setEndOffsetX($endOffsetX);
616
617 // set the end offsetY
618 $this->object->setEndOffsetY($endOffsetY);
619 }
620
624 private function readClientData(): void
625 {
626 $length = Xls::getInt4d($this->data, $this->pos + 4);
627 $recordData = substr($this->data, $this->pos + 8, $length);
628
629 // move stream pointer to next record
630 $this->pos += 8 + $length;
631 }
632
639 private function readOfficeArtRGFOPTE($data, $n): void
640 {
641 $splicedComplexData = substr($data, 6 * $n);
642
643 // loop through property-value pairs
644 for ($i = 0; $i < $n; ++$i) {
645 // read 6 bytes at a time
646 $fopte = substr($data, 6 * $i, 6);
647
648 // offset: 0; size: 2; opid
649 $opid = Xls::getUInt2d($fopte, 0);
650
651 // bit: 0-13; mask: 0x3FFF; opid.opid
652 $opidOpid = (0x3FFF & $opid) >> 0;
653
654 // bit: 14; mask 0x4000; 1 = value in op field is BLIP identifier
655 $opidFBid = (0x4000 & $opid) >> 14;
656
657 // bit: 15; mask 0x8000; 1 = this is a complex property, op field specifies size of complex data
658 $opidFComplex = (0x8000 & $opid) >> 15;
659
660 // offset: 2; size: 4; the value for this property
661 $op = Xls::getInt4d($fopte, 2);
662
663 if ($opidFComplex) {
664 $complexData = substr($splicedComplexData, 0, $op);
665 $splicedComplexData = substr($splicedComplexData, $op);
666
667 // we store string value with complex data
668 $value = $complexData;
669 } else {
670 // we store integer value
671 $value = $op;
672 }
673
674 $this->object->setOPT($opidOpid, $value);
675 }
676 }
677}
$n
Definition: RandomTest.php:85
$size
Definition: RandomTest.php:84
An exception for terminatinating execution or to throw for unit testing.
Helper class to manipulate cell coordinates.
Definition: Coordinate.php:15
static stringFromColumnIndex($columnIndex)
String from column index.
Definition: Coordinate.php:313
readSpContainer()
Read SpContainer record (Shape Container).
Definition: Escher.php:504
readDg()
Read Dg record (Drawing).
Definition: Escher.php:464
readSp()
Read Sp record (Shape).
Definition: Escher.php:536
readSplitMenuColors()
Read SplitMenuColors record.
Definition: Escher.php:434
readDefault()
Read a generic record.
Definition: Escher.php:181
load($data)
Load Escher stream data.
Definition: Escher.php:81
readSpgrContainer()
Read SpgrContainer record (Shape Group Container).
Definition: Escher.php:476
readBstoreContainer()
Read BstoreContainer record (Blip Store Container).
Definition: Escher.php:232
readBlipJPEG()
Read BlipJPEG record.
Definition: Escher.php:316
readDgContainer()
Read DgContainer record (Drawing Container).
Definition: Escher.php:446
__construct($object)
Create a new Escher instance.
Definition: Escher.php:69
readDggContainer()
Read DggContainer record (Drawing Group Container).
Definition: Escher.php:202
readTertiaryOPT()
Read TertiaryOPT record.
Definition: Escher.php:417
readDgg()
Read Dgg record (Drawing Group).
Definition: Escher.php:220
readClientTextbox()
Read ClientTextbox record.
Definition: Escher.php:553
readOfficeArtRGFOPTE($data, $n)
Read OfficeArtRGFOPTE table of property-value pairs.
Definition: Escher.php:639
readClientAnchor()
Read ClientAnchor record.
Definition: Escher.php:570
readSpgr()
Read Spgr record (Shape Group).
Definition: Escher.php:524
readClientData()
Read ClientData record.
Definition: Escher.php:624
static getInt4d($data, $pos)
Read 32-bit signed integer.
Definition: Xls.php:7887
static getUInt2d($data, $pos)
Read 16-bit unsigned integer.
Definition: Xls.php:7861
$i
Definition: disco.tpl.php:19
if(function_exists( 'posix_getuid') &&posix_getuid()===0) if(!array_key_exists('t', $options)) $tag
Definition: cron.php:35
$this data['403_header']