Failed Conditions
Push — develop ( 0ef1b5...a1e8c8 )
by Mark
34:44
created

Escher::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
4
5
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
6
use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer;
7
use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
8
use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer\SpContainer;
9
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer;
10
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer;
11
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE;
12
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE\Blip;
13
14
class Escher
15
{
16
    /**
17
     * The object we are writing.
18
     */
19
    private $object;
20
21
    /**
22
     * The written binary data.
23
     */
24
    private $data;
25
26
    /**
27
     * Shape offsets. Positions in binary stream where a new shape record begins.
28
     *
29
     * @var array
30
     */
31
    private $spOffsets;
32
33
    /**
34
     * Shape types.
35
     *
36
     * @var array
37
     */
38
    private $spTypes;
39
40
    /**
41
     * Constructor.
42
     *
43
     * @param mixed $object
44
     */
45 10
    public function __construct($object)
46
    {
47 10
        $this->object = $object;
48 10
    }
49
50
    /**
51
     * Process the object to be written.
52
     *
53
     * @return string
54
     */
55 10
    public function close()
56
    {
57
        // initialize
58 10
        $this->data = '';
59
60 10
        switch (get_class($this->object)) {
61
            case \PhpOffice\PhpSpreadsheet\Shared\Escher::class:
62 10
                if ($dggContainer = $this->object->getDggContainer()) {
63 10
                    $writer = new self($dggContainer);
64 10
                    $this->data = $writer->close();
65 10
                } elseif ($dgContainer = $this->object->getDgContainer()) {
66 10
                    $writer = new self($dgContainer);
67 10
                    $this->data = $writer->close();
68 10
                    $this->spOffsets = $writer->getSpOffsets();
69 10
                    $this->spTypes = $writer->getSpTypes();
70
                }
71
72 10
                break;
73
            case DggContainer::class:
74
                // this is a container record
75
76
                // initialize
77 10
                $innerData = '';
78
79
                // write the dgg
80 10
                $recVer = 0x0;
81 10
                $recInstance = 0x0000;
82 10
                $recType = 0xF006;
83
84 10
                $recVerInstance = $recVer;
85 10
                $recVerInstance |= $recInstance << 4;
86
87
                // dgg data
88
                $dggData =
89 10
                    pack(
90 10
                        'VVVV',
91 10
                        $this->object->getSpIdMax(), // maximum shape identifier increased by one
92 10
                        $this->object->getCDgSaved() + 1, // number of file identifier clusters increased by one
93 10
                        $this->object->getCSpSaved(),
94 10
                        $this->object->getCDgSaved() // count total number of drawings saved
95
                    );
96
97
                // add file identifier clusters (one per drawing)
98 10
                $IDCLs = $this->object->getIDCLs();
99
100 10
                foreach ($IDCLs as $dgId => $maxReducedSpId) {
101 10
                    $dggData .= pack('VV', $dgId, $maxReducedSpId + 1);
102
                }
103
104 10
                $header = pack('vvV', $recVerInstance, $recType, strlen($dggData));
105 10
                $innerData .= $header . $dggData;
106
107
                // write the bstoreContainer
108 10
                if ($bstoreContainer = $this->object->getBstoreContainer()) {
109 10
                    $writer = new self($bstoreContainer);
110 10
                    $innerData .= $writer->close();
111
                }
112
113
                // write the record
114 10
                $recVer = 0xF;
115 10
                $recInstance = 0x0000;
116 10
                $recType = 0xF000;
117 10
                $length = strlen($innerData);
118
119 10
                $recVerInstance = $recVer;
120 10
                $recVerInstance |= $recInstance << 4;
121
122 10
                $header = pack('vvV', $recVerInstance, $recType, $length);
123
124 10
                $this->data = $header . $innerData;
125
126 10
                break;
127
            case BstoreContainer::class:
128
                // this is a container record
129
130
                // initialize
131 10
                $innerData = '';
132
133
                // treat the inner data
134 10
                if ($BSECollection = $this->object->getBSECollection()) {
135 7
                    foreach ($BSECollection as $BSE) {
136 7
                        $writer = new self($BSE);
137 7
                        $innerData .= $writer->close();
138
                    }
139
                }
140
141
                // write the record
142 10
                $recVer = 0xF;
143 10
                $recInstance = count($this->object->getBSECollection());
144 10
                $recType = 0xF001;
145 10
                $length = strlen($innerData);
146
147 10
                $recVerInstance = $recVer;
148 10
                $recVerInstance |= $recInstance << 4;
149
150 10
                $header = pack('vvV', $recVerInstance, $recType, $length);
151
152 10
                $this->data = $header . $innerData;
153
154 10
                break;
155
            case BSE::class:
156
                // this is a semi-container record
157
158
                // initialize
159 7
                $innerData = '';
160
161
                // here we treat the inner data
162 7
                if ($blip = $this->object->getBlip()) {
163 7
                    $writer = new self($blip);
164 7
                    $innerData .= $writer->close();
165
                }
166
167
                // initialize
168 7
                $data = '';
169
170 7
                $btWin32 = $this->object->getBlipType();
171 7
                $btMacOS = $this->object->getBlipType();
172 7
                $data .= pack('CC', $btWin32, $btMacOS);
173
174 7
                $rgbUid = pack('VVVV', 0, 0, 0, 0); // todo
175 7
                $data .= $rgbUid;
176
177 7
                $tag = 0;
178 7
                $size = strlen($innerData);
179 7
                $cRef = 1;
180 7
                $foDelay = 0; //todo
181 7
                $unused1 = 0x0;
182 7
                $cbName = 0x0;
183 7
                $unused2 = 0x0;
184 7
                $unused3 = 0x0;
185 7
                $data .= pack('vVVVCCCC', $tag, $size, $cRef, $foDelay, $unused1, $cbName, $unused2, $unused3);
186
187 7
                $data .= $innerData;
188
189
                // write the record
190 7
                $recVer = 0x2;
191 7
                $recInstance = $this->object->getBlipType();
192 7
                $recType = 0xF007;
193 7
                $length = strlen($data);
194
195 7
                $recVerInstance = $recVer;
196 7
                $recVerInstance |= $recInstance << 4;
197
198 7
                $header = pack('vvV', $recVerInstance, $recType, $length);
199
200 7
                $this->data = $header;
201
202 7
                $this->data .= $data;
203
204 7
                break;
205
            case Blip::class:
206
                // this is an atom record
207
208
                // write the record
209 7
                switch ($this->object->getParent()->getBlipType()) {
210
                    case BSE::BLIPTYPE_JPEG:
211
                        // initialize
212 7
                        $innerData = '';
213
214 7
                        $rgbUid1 = pack('VVVV', 0, 0, 0, 0); // todo
215 7
                        $innerData .= $rgbUid1;
216
217 7
                        $tag = 0xFF; // todo
218 7
                        $innerData .= pack('C', $tag);
219
220 7
                        $innerData .= $this->object->getData();
221
222 7
                        $recVer = 0x0;
223 7
                        $recInstance = 0x46A;
224 7
                        $recType = 0xF01D;
225 7
                        $length = strlen($innerData);
226
227 7
                        $recVerInstance = $recVer;
228 7
                        $recVerInstance |= $recInstance << 4;
229
230 7
                        $header = pack('vvV', $recVerInstance, $recType, $length);
231
232 7
                        $this->data = $header;
233
234 7
                        $this->data .= $innerData;
235
236 7
                        break;
237
                    case BSE::BLIPTYPE_PNG:
238
                        // initialize
239 7
                        $innerData = '';
240
241 7
                        $rgbUid1 = pack('VVVV', 0, 0, 0, 0); // todo
242 7
                        $innerData .= $rgbUid1;
243
244 7
                        $tag = 0xFF; // todo
245 7
                        $innerData .= pack('C', $tag);
246
247 7
                        $innerData .= $this->object->getData();
248
249 7
                        $recVer = 0x0;
250 7
                        $recInstance = 0x6E0;
251 7
                        $recType = 0xF01E;
252 7
                        $length = strlen($innerData);
253
254 7
                        $recVerInstance = $recVer;
255 7
                        $recVerInstance |= $recInstance << 4;
256
257 7
                        $header = pack('vvV', $recVerInstance, $recType, $length);
258
259 7
                        $this->data = $header;
260
261 7
                        $this->data .= $innerData;
262
263 7
                        break;
264
                }
265
266 7
                break;
267
            case DgContainer::class:
268
                // this is a container record
269
270
                // initialize
271 10
                $innerData = '';
272
273
                // write the dg
274 10
                $recVer = 0x0;
275 10
                $recInstance = $this->object->getDgId();
276 10
                $recType = 0xF008;
277 10
                $length = 8;
278
279 10
                $recVerInstance = $recVer;
280 10
                $recVerInstance |= $recInstance << 4;
281
282 10
                $header = pack('vvV', $recVerInstance, $recType, $length);
283
284
                // number of shapes in this drawing (including group shape)
285 10
                $countShapes = count($this->object->getSpgrContainer()->getChildren());
286 10
                $innerData .= $header . pack('VV', $countShapes, $this->object->getLastSpId());
287
288
                // write the spgrContainer
289 10
                if ($spgrContainer = $this->object->getSpgrContainer()) {
290 10
                    $writer = new self($spgrContainer);
291 10
                    $innerData .= $writer->close();
292
293
                    // get the shape offsets relative to the spgrContainer record
294 10
                    $spOffsets = $writer->getSpOffsets();
295 10
                    $spTypes = $writer->getSpTypes();
296
297
                    // save the shape offsets relative to dgContainer
298 10
                    foreach ($spOffsets as &$spOffset) {
299 10
                        $spOffset += 24; // add length of dgContainer header data (8 bytes) plus dg data (16 bytes)
300
                    }
301
302 10
                    $this->spOffsets = $spOffsets;
303 10
                    $this->spTypes = $spTypes;
304
                }
305
306
                // write the record
307 10
                $recVer = 0xF;
308 10
                $recInstance = 0x0000;
309 10
                $recType = 0xF002;
310 10
                $length = strlen($innerData);
311
312 10
                $recVerInstance = $recVer;
313 10
                $recVerInstance |= $recInstance << 4;
314
315 10
                $header = pack('vvV', $recVerInstance, $recType, $length);
316
317 10
                $this->data = $header . $innerData;
318
319 10
                break;
320
            case SpgrContainer::class:
321
                // this is a container record
322
323
                // initialize
324 10
                $innerData = '';
325
326
                // initialize spape offsets
327 10
                $totalSize = 8;
328 10
                $spOffsets = [];
329 10
                $spTypes = [];
330
331
                // treat the inner data
332 10
                foreach ($this->object->getChildren() as $spContainer) {
333 10
                    $writer = new self($spContainer);
334 10
                    $spData = $writer->close();
335 10
                    $innerData .= $spData;
336
337
                    // save the shape offsets (where new shape records begin)
338 10
                    $totalSize += strlen($spData);
339 10
                    $spOffsets[] = $totalSize;
340
341 10
                    $spTypes = array_merge($spTypes, $writer->getSpTypes());
342
                }
343
344
                // write the record
345 10
                $recVer = 0xF;
346 10
                $recInstance = 0x0000;
347 10
                $recType = 0xF003;
348 10
                $length = strlen($innerData);
349
350 10
                $recVerInstance = $recVer;
351 10
                $recVerInstance |= $recInstance << 4;
352
353 10
                $header = pack('vvV', $recVerInstance, $recType, $length);
354
355 10
                $this->data = $header . $innerData;
356 10
                $this->spOffsets = $spOffsets;
357 10
                $this->spTypes = $spTypes;
358
359 10
                break;
360
            case SpContainer::class:
361
                // initialize
362 10
                $data = '';
363
364
                // build the data
365
366
                // write group shape record, if necessary?
367 10
                if ($this->object->getSpgr()) {
368 10
                    $recVer = 0x1;
369 10
                    $recInstance = 0x0000;
370 10
                    $recType = 0xF009;
371 10
                    $length = 0x00000010;
372
373 10
                    $recVerInstance = $recVer;
374 10
                    $recVerInstance |= $recInstance << 4;
375
376 10
                    $header = pack('vvV', $recVerInstance, $recType, $length);
377
378 10
                    $data .= $header . pack('VVVV', 0, 0, 0, 0);
379
                }
380 10
                $this->spTypes[] = ($this->object->getSpType());
381
382
                // write the shape record
383 10
                $recVer = 0x2;
384 10
                $recInstance = $this->object->getSpType(); // shape type
385 10
                $recType = 0xF00A;
386 10
                $length = 0x00000008;
387
388 10
                $recVerInstance = $recVer;
389 10
                $recVerInstance |= $recInstance << 4;
390
391 10
                $header = pack('vvV', $recVerInstance, $recType, $length);
392
393 10
                $data .= $header . pack('VV', $this->object->getSpId(), $this->object->getSpgr() ? 0x0005 : 0x0A00);
394
395
                // the options
396 10
                if ($this->object->getOPTCollection()) {
397 10
                    $optData = '';
398
399 10
                    $recVer = 0x3;
400 10
                    $recInstance = count($this->object->getOPTCollection());
401 10
                    $recType = 0xF00B;
402 10
                    foreach ($this->object->getOPTCollection() as $property => $value) {
403 10
                        $optData .= pack('vV', $property, $value);
404
                    }
405 10
                    $length = strlen($optData);
406
407 10
                    $recVerInstance = $recVer;
408 10
                    $recVerInstance |= $recInstance << 4;
409
410 10
                    $header = pack('vvV', $recVerInstance, $recType, $length);
411 10
                    $data .= $header . $optData;
412
                }
413
414
                // the client anchor
415 10
                if ($this->object->getStartCoordinates()) {
416 10
                    $clientAnchorData = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $clientAnchorData is dead and can be removed.
Loading history...
417
418 10
                    $recVer = 0x0;
419 10
                    $recInstance = 0x0;
420 10
                    $recType = 0xF010;
421
422
                    // start coordinates
423 10
                    list($column, $row) = Coordinate::coordinateFromString($this->object->getStartCoordinates());
424 10
                    $c1 = Coordinate::columnIndexFromString($column) - 1;
425 10
                    $r1 = $row - 1;
426
427
                    // start offsetX
428 10
                    $startOffsetX = $this->object->getStartOffsetX();
429
430
                    // start offsetY
431 10
                    $startOffsetY = $this->object->getStartOffsetY();
432
433
                    // end coordinates
434 10
                    list($column, $row) = Coordinate::coordinateFromString($this->object->getEndCoordinates());
435 10
                    $c2 = Coordinate::columnIndexFromString($column) - 1;
436 10
                    $r2 = $row - 1;
437
438
                    // end offsetX
439 10
                    $endOffsetX = $this->object->getEndOffsetX();
440
441
                    // end offsetY
442 10
                    $endOffsetY = $this->object->getEndOffsetY();
443
444 10
                    $clientAnchorData = pack('vvvvvvvvv', $this->object->getSpFlag(), $c1, $startOffsetX, $r1, $startOffsetY, $c2, $endOffsetX, $r2, $endOffsetY);
445
446 10
                    $length = strlen($clientAnchorData);
447
448 10
                    $recVerInstance = $recVer;
449 10
                    $recVerInstance |= $recInstance << 4;
450
451 10
                    $header = pack('vvV', $recVerInstance, $recType, $length);
452 10
                    $data .= $header . $clientAnchorData;
453
                }
454
455
                // the client data, just empty for now
456 10
                if (!$this->object->getSpgr()) {
457 10
                    $clientDataData = '';
458
459 10
                    $recVer = 0x0;
460 10
                    $recInstance = 0x0;
461 10
                    $recType = 0xF011;
462
463 10
                    $length = strlen($clientDataData);
464
465 10
                    $recVerInstance = $recVer;
466 10
                    $recVerInstance |= $recInstance << 4;
467
468 10
                    $header = pack('vvV', $recVerInstance, $recType, $length);
469 10
                    $data .= $header . $clientDataData;
470
                }
471
472
                // write the record
473 10
                $recVer = 0xF;
474 10
                $recInstance = 0x0000;
475 10
                $recType = 0xF004;
476 10
                $length = strlen($data);
477
478 10
                $recVerInstance = $recVer;
479 10
                $recVerInstance |= $recInstance << 4;
480
481 10
                $header = pack('vvV', $recVerInstance, $recType, $length);
482
483 10
                $this->data = $header . $data;
484
485 10
                break;
486
        }
487
488 10
        return $this->data;
489
    }
490
491
    /**
492
     * Gets the shape offsets.
493
     *
494
     * @return array
495
     */
496 10
    public function getSpOffsets()
497
    {
498 10
        return $this->spOffsets;
499
    }
500
501
    /**
502
     * Gets the shape types.
503
     *
504
     * @return array
505
     */
506 10
    public function getSpTypes()
507
    {
508 10
        return $this->spTypes;
509
    }
510
}
511