Failed Conditions
Push — develop ( 11b055...32a55a )
by Adrien
30:13
created

Xlsx   F

Complexity

Total Complexity 509

Size/Duplication

Total Lines 2168
Duplicated Lines 9.55 %

Test Coverage

Coverage 72.3%

Importance

Changes 0
Metric Value
dl 207
loc 2168
rs 0.6314
c 0
b 0
f 0
ccs 916
cts 1267
cp 0.723
wmc 509

19 Methods

Rating   Name   Duplication   Size   Complexity  
B canRead() 0 32 6
A castToBoolean() 0 10 4
A castToError() 0 3 2
B listWorksheetNames() 0 32 5
C listWorksheetInfo() 0 93 12
A getFromZipArchive() 0 18 3
A castToFormula() 0 21 4
A __construct() 0 4 1
A castToString() 0 3 2
A boolean() 0 10 4
C parseRichText() 29 55 26
A dirAdd() 0 3 1
A readBorder() 0 7 3
C readColor() 0 23 7
A getArrayItem() 0 3 2
C readRibbon() 0 39 8
F readStyle() 37 123 39
B toCSSArray() 12 29 6
F load() 129 1540 374

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Xlsx often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Xlsx, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader;
4
5
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
6
use PhpOffice\PhpSpreadsheet\Document\Properties;
7
use PhpOffice\PhpSpreadsheet\NamedRange;
8
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Chart;
9
use PhpOffice\PhpSpreadsheet\ReferenceHelper;
10
use PhpOffice\PhpSpreadsheet\RichText\RichText;
11
use PhpOffice\PhpSpreadsheet\Settings;
12
use PhpOffice\PhpSpreadsheet\Shared\Date;
13
use PhpOffice\PhpSpreadsheet\Shared\Drawing;
14
use PhpOffice\PhpSpreadsheet\Shared\File;
15
use PhpOffice\PhpSpreadsheet\Shared\Font;
16
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
17
use PhpOffice\PhpSpreadsheet\Spreadsheet;
18
use PhpOffice\PhpSpreadsheet\Style\Border;
19
use PhpOffice\PhpSpreadsheet\Style\Borders;
20
use PhpOffice\PhpSpreadsheet\Style\Color;
21
use PhpOffice\PhpSpreadsheet\Style\Conditional;
22
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
23
use PhpOffice\PhpSpreadsheet\Style\Protection;
24
use PhpOffice\PhpSpreadsheet\Style\Style;
25
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
26
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
27
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
28
use XMLReader;
29
use ZipArchive;
30
31
class Xlsx extends BaseReader
32
{
33
    /**
34
     * ReferenceHelper instance.
35
     *
36
     * @var ReferenceHelper
37
     */
38
    private $referenceHelper;
39
40
    /**
41
     * Xlsx\Theme instance.
42
     *
43
     * @var Xlsx\Theme
44
     */
45
    private static $theme = null;
46
47
    /**
48
     * Create a new Xlsx Reader instance.
49
     */
50 16
    public function __construct()
51
    {
52 16
        $this->readFilter = new DefaultReadFilter();
53 16
        $this->referenceHelper = ReferenceHelper::getInstance();
54 16
    }
55
56
    /**
57
     * Can the current IReader read the file?
58
     *
59
     * @param string $pFilename
60
     *
61
     * @throws Exception
62
     *
63
     * @return bool
64
     */
65 6
    public function canRead($pFilename)
66
    {
67 6
        File::assertFile($pFilename);
68
69 6
        $xl = false;
70
        // Load file
71 6
        $zip = new ZipArchive();
72 6
        if ($zip->open($pFilename) === true) {
73
            // check if it is an OOXML archive
74 6
            $rels = simplexml_load_string(
75 6
                $this->securityScan(
76 6
                    $this->getFromZipArchive($zip, '_rels/.rels')
77
                ),
78 6
                'SimpleXMLElement',
79 6
                Settings::getLibXmlLoaderOptions()
80
            );
81 6
            if ($rels !== false) {
82 6
                foreach ($rels->Relationship as $rel) {
83 6
                    switch ($rel['Type']) {
84 6
                        case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
85 6
                            if (basename($rel['Target']) == 'workbook.xml') {
86 6
                                $xl = true;
87
                            }
88
89 6
                            break;
90
                    }
91
                }
92
            }
93 6
            $zip->close();
94
        }
95
96 6
        return $xl;
97
    }
98
99
    /**
100
     * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
101
     *
102
     * @param string $pFilename
103
     *
104
     * @throws Exception
105
     *
106
     * @return array
107
     */
108 1
    public function listWorksheetNames($pFilename)
109
    {
110 1
        File::assertFile($pFilename);
111
112 1
        $worksheetNames = [];
113
114 1
        $zip = new ZipArchive();
115 1
        $zip->open($pFilename);
116
117
        //    The files we're looking at here are small enough that simpleXML is more efficient than XMLReader
118 1
        $rels = simplexml_load_string(
119 1
            $this->securityScan($this->getFromZipArchive($zip, '_rels/.rels'))
120
        ); //~ http://schemas.openxmlformats.org/package/2006/relationships");
121 1
        foreach ($rels->Relationship as $rel) {
122 1
            switch ($rel['Type']) {
123 1
                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
124 1
                    $xmlWorkbook = simplexml_load_string(
125 1
                        $this->securityScan($this->getFromZipArchive($zip, "{$rel['Target']}"))
126
                    ); //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
127
128 1
                    if ($xmlWorkbook->sheets) {
129 1
                        foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
130
                            // Check if sheet should be skipped
131 1
                            $worksheetNames[] = (string) $eleSheet['name'];
132
                        }
133
                    }
134
            }
135
        }
136
137 1
        $zip->close();
138
139 1
        return $worksheetNames;
140
    }
141
142
    /**
143
     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
144
     *
145
     * @param string $pFilename
146
     *
147
     * @throws Exception
148
     *
149
     * @return array
150
     */
151 1
    public function listWorksheetInfo($pFilename)
152
    {
153 1
        File::assertFile($pFilename);
154
155 1
        $worksheetInfo = [];
156
157 1
        $zip = new ZipArchive();
158 1
        $zip->open($pFilename);
159
160 1
        $rels = simplexml_load_string(
161
            //~ http://schemas.openxmlformats.org/package/2006/relationships"
162 1
            $this->securityScan($this->getFromZipArchive($zip, '_rels/.rels')),
163 1
            'SimpleXMLElement',
164 1
            Settings::getLibXmlLoaderOptions()
165
        );
166 1
        foreach ($rels->Relationship as $rel) {
167 1
            if ($rel['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument') {
168 1
                $dir = dirname($rel['Target']);
169 1
                $relsWorkbook = simplexml_load_string(
170
                    //~ http://schemas.openxmlformats.org/package/2006/relationships"
171 1
                    $this->securityScan(
172 1
                        $this->getFromZipArchive($zip, "$dir/_rels/" . basename($rel['Target']) . '.rels')
173
                    ),
174 1
                    'SimpleXMLElement',
175 1
                    Settings::getLibXmlLoaderOptions()
176
                );
177 1
                $relsWorkbook->registerXPathNamespace('rel', 'http://schemas.openxmlformats.org/package/2006/relationships');
178
179 1
                $worksheets = [];
180 1
                foreach ($relsWorkbook->Relationship as $ele) {
181 1
                    if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet') {
182 1
                        $worksheets[(string) $ele['Id']] = $ele['Target'];
183
                    }
184
                }
185
186 1
                $xmlWorkbook = simplexml_load_string(
187
                    //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
188 1
                    $this->securityScan(
189 1
                        $this->getFromZipArchive($zip, "{$rel['Target']}")
190
                    ),
191 1
                    'SimpleXMLElement',
192 1
                    Settings::getLibXmlLoaderOptions()
193
                );
194 1
                if ($xmlWorkbook->sheets) {
195 1
                    $dir = dirname($rel['Target']);
196
                    /** @var \SimpleXMLElement $eleSheet */
197 1
                    foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
198
                        $tmpInfo = [
199 1
                            'worksheetName' => (string) $eleSheet['name'],
200 1
                            'lastColumnLetter' => 'A',
201 1
                            'lastColumnIndex' => 0,
202 1
                            'totalRows' => 0,
203 1
                            'totalColumns' => 0,
204
                        ];
205
206 1
                        $fileWorksheet = $worksheets[(string) self::getArrayItem($eleSheet->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id')];
207
208 1
                        $xml = new XMLReader();
209 1
                        $xml->xml(
210 1
                            $this->securityScanFile(
1 ignored issue
show
Bug introduced by
It seems like $this->securityScanFile(...) . '#' . EncapsedNode) can also be of type false; however, parameter $source of XMLReader::XML() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

210
                            /** @scrutinizer ignore-type */ $this->securityScanFile(
Loading history...
211 1
                                'zip://' . File::realpath($pFilename) . '#' . "$dir/$fileWorksheet"
212
                            ),
213 1
                            null,
214 1
                            Settings::getLibXmlLoaderOptions()
215
                        );
216 1
                        $xml->setParserProperty(2, true);
217
218 1
                        $currCells = 0;
219 1
                        while ($xml->read()) {
220 1
                            if ($xml->name == 'row' && $xml->nodeType == XMLReader::ELEMENT) {
221 1
                                $row = $xml->getAttribute('r');
222 1
                                $tmpInfo['totalRows'] = $row;
223 1
                                $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
224 1
                                $currCells = 0;
225 1
                            } elseif ($xml->name == 'c' && $xml->nodeType == XMLReader::ELEMENT) {
226 1
                                ++$currCells;
227
                            }
228
                        }
229 1
                        $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
230 1
                        $xml->close();
231
232 1
                        $tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1;
233 1
                        $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
234
235 1
                        $worksheetInfo[] = $tmpInfo;
236
                    }
237
                }
238
            }
239
        }
240
241 1
        $zip->close();
242
243 1
        return $worksheetInfo;
244
    }
245
246 1
    private static function castToBoolean($c)
247
    {
248 1
        $value = isset($c->v) ? (string) $c->v : null;
249 1
        if ($value == '0') {
250
            return false;
251 1
        } elseif ($value == '1') {
252 1
            return true;
253
        }
254
255
        return (bool) $c->v;
256
    }
257
258
    private static function castToError($c)
259
    {
260
        return isset($c->v) ? (string) $c->v : null;
261
    }
262
263 7
    private static function castToString($c)
264
    {
265 7
        return isset($c->v) ? (string) $c->v : null;
266
    }
267
268 4
    private function castToFormula($c, $r, &$cellDataType, &$value, &$calculatedValue, &$sharedFormulas, $castBaseType)
269
    {
270 4
        $cellDataType = 'f';
271 4
        $value = "={$c->f}";
272 4
        $calculatedValue = self::$castBaseType($c);
273
274
        // Shared formula?
275 4
        if (isset($c->f['t']) && strtolower((string) $c->f['t']) == 'shared') {
276 2
            $instance = (string) $c->f['si'];
277
278 2
            if (!isset($sharedFormulas[(string) $c->f['si']])) {
279 2
                $sharedFormulas[$instance] = ['master' => $r, 'formula' => $value];
280
            } else {
281 2
                $master = Coordinate::coordinateFromString($sharedFormulas[$instance]['master']);
282 2
                $current = Coordinate::coordinateFromString($r);
283
284 2
                $difference = [0, 0];
285 2
                $difference[0] = Coordinate::columnIndexFromString($current[0]) - Coordinate::columnIndexFromString($master[0]);
286 2
                $difference[1] = $current[1] - $master[1];
287
288 2
                $value = $this->referenceHelper->updateFormulaReferences($sharedFormulas[$instance]['formula'], 'A1', $difference[0], $difference[1]);
0 ignored issues
show
Bug introduced by
'A1' of type string is incompatible with the type integer expected by parameter $pBefore of PhpOffice\PhpSpreadsheet...dateFormulaReferences(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

288
                $value = $this->referenceHelper->updateFormulaReferences($sharedFormulas[$instance]['formula'], /** @scrutinizer ignore-type */ 'A1', $difference[0], $difference[1]);
Loading history...
289
            }
290
        }
291 4
    }
292
293
    /**
294
     * @param ZipArchive $archive
295
     * @param string $fileName
296
     *
297
     * @return string
298
     */
299 15
    private function getFromZipArchive(ZipArchive $archive, $fileName = '')
300
    {
301
        // Root-relative paths
302 15
        if (strpos($fileName, '//') !== false) {
303
            $fileName = substr($fileName, strpos($fileName, '//') + 1);
304
        }
305 15
        $fileName = File::realpath($fileName);
1 ignored issue
show
Bug introduced by
It seems like $fileName can also be of type false; however, parameter $pFilename of PhpOffice\PhpSpreadsheet\Shared\File::realpath() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

305
        $fileName = File::realpath(/** @scrutinizer ignore-type */ $fileName);
Loading history...
306
307
        // Sadly, some 3rd party xlsx generators don't use consistent case for filenaming
308
        //    so we need to load case-insensitively from the zip file
309
310
        // Apache POI fixes
311 15
        $contents = $archive->getFromName($fileName, 0, ZipArchive::FL_NOCASE);
312 15
        if ($contents === false) {
313
            $contents = $archive->getFromName(substr($fileName, 1), 0, ZipArchive::FL_NOCASE);
314
        }
315
316 15
        return $contents;
317
    }
318
319
    /**
320
     * Loads Spreadsheet from file.
321
     *
322
     * @param string $pFilename
323
     *
324
     * @throws Exception
325
     *
326
     * @return Spreadsheet
327
     */
328 12
    public function load($pFilename)
329
    {
330 12
        File::assertFile($pFilename);
331
332
        // Initialisations
333 12
        $excel = new Spreadsheet();
334 12
        $excel->removeSheetByIndex(0);
335 12
        if (!$this->readDataOnly) {
336 12
            $excel->removeCellStyleXfByIndex(0); // remove the default style
337 12
            $excel->removeCellXfByIndex(0); // remove the default style
338
        }
339
340 12
        $zip = new ZipArchive();
341 12
        $zip->open($pFilename);
342
343
        //    Read the theme first, because we need the colour scheme when reading the styles
344 12
        $wbRels = simplexml_load_string(
345
            //~ http://schemas.openxmlformats.org/package/2006/relationships"
346 12
            $this->securityScan($this->getFromZipArchive($zip, 'xl/_rels/workbook.xml.rels')),
347 12
            'SimpleXMLElement',
348 12
            Settings::getLibXmlLoaderOptions()
349
        );
350 12
        foreach ($wbRels->Relationship as $rel) {
351 12
            switch ($rel['Type']) {
352 12
                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme':
353 12
                    $themeOrderArray = ['lt1', 'dk1', 'lt2', 'dk2'];
354 12
                    $themeOrderAdditional = count($themeOrderArray);
355
356 12
                    $xmlTheme = simplexml_load_string(
357 12
                        $this->securityScan($this->getFromZipArchive($zip, "xl/{$rel['Target']}")),
358 12
                        'SimpleXMLElement',
359 12
                        Settings::getLibXmlLoaderOptions()
360
                    );
361 12
                    if (is_object($xmlTheme)) {
362 12
                        $xmlThemeName = $xmlTheme->attributes();
363 12
                        $xmlTheme = $xmlTheme->children('http://schemas.openxmlformats.org/drawingml/2006/main');
364 12
                        $themeName = (string) $xmlThemeName['name'];
365
366 12
                        $colourScheme = $xmlTheme->themeElements->clrScheme->attributes();
367 12
                        $colourSchemeName = (string) $colourScheme['name'];
368 12
                        $colourScheme = $xmlTheme->themeElements->clrScheme->children('http://schemas.openxmlformats.org/drawingml/2006/main');
369
370 12
                        $themeColours = [];
371 12
                        foreach ($colourScheme as $k => $xmlColour) {
372 12
                            $themePos = array_search($k, $themeOrderArray);
373 12
                            if ($themePos === false) {
374 12
                                $themePos = $themeOrderAdditional++;
375
                            }
376 12
                            if (isset($xmlColour->sysClr)) {
377 12
                                $xmlColourData = $xmlColour->sysClr->attributes();
378 12
                                $themeColours[$themePos] = $xmlColourData['lastClr'];
379 12
                            } elseif (isset($xmlColour->srgbClr)) {
380 12
                                $xmlColourData = $xmlColour->srgbClr->attributes();
381 12
                                $themeColours[$themePos] = $xmlColourData['val'];
382
                            }
383
                        }
384 12
                        self::$theme = new Xlsx\Theme($themeName, $colourSchemeName, $themeColours);
385
                    }
386
387 12
                    break;
388
            }
389
        }
390
391 12
        $rels = simplexml_load_string(
392
            //~ http://schemas.openxmlformats.org/package/2006/relationships"
393 12
            $this->securityScan($this->getFromZipArchive($zip, '_rels/.rels')),
394 12
            'SimpleXMLElement',
395 12
            Settings::getLibXmlLoaderOptions()
396
        );
397 12
        foreach ($rels->Relationship as $rel) {
398 12
            switch ($rel['Type']) {
399 12
                case 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties':
400 11
                    $xmlCore = simplexml_load_string(
401 11
                        $this->securityScan($this->getFromZipArchive($zip, "{$rel['Target']}")),
402 11
                        'SimpleXMLElement',
403 11
                        Settings::getLibXmlLoaderOptions()
404
                    );
405 11
                    if (is_object($xmlCore)) {
406 11
                        $xmlCore->registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/');
407 11
                        $xmlCore->registerXPathNamespace('dcterms', 'http://purl.org/dc/terms/');
408 11
                        $xmlCore->registerXPathNamespace('cp', 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties');
409 11
                        $docProps = $excel->getProperties();
410 11
                        $docProps->setCreator((string) self::getArrayItem($xmlCore->xpath('dc:creator')));
411 11
                        $docProps->setLastModifiedBy((string) self::getArrayItem($xmlCore->xpath('cp:lastModifiedBy')));
412 11
                        $docProps->setCreated(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:created')))); //! respect xsi:type
0 ignored issues
show
Bug introduced by
strtotime(self::getArray...th('dcterms:created'))) of type integer|false is incompatible with the type PhpOffice\PhpSpreadsheet\Document\datetime expected by parameter $pValue of PhpOffice\PhpSpreadsheet...roperties::setCreated(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

412
                        $docProps->setCreated(/** @scrutinizer ignore-type */ strtotime(self::getArrayItem($xmlCore->xpath('dcterms:created')))); //! respect xsi:type
Loading history...
413 11
                        $docProps->setModified(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:modified')))); //! respect xsi:type
0 ignored issues
show
Bug introduced by
strtotime(self::getArray...h('dcterms:modified'))) of type integer|false is incompatible with the type PhpOffice\PhpSpreadsheet\Document\datetime expected by parameter $pValue of PhpOffice\PhpSpreadsheet...operties::setModified(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

413
                        $docProps->setModified(/** @scrutinizer ignore-type */ strtotime(self::getArrayItem($xmlCore->xpath('dcterms:modified')))); //! respect xsi:type
Loading history...
414 11
                        $docProps->setTitle((string) self::getArrayItem($xmlCore->xpath('dc:title')));
415 11
                        $docProps->setDescription((string) self::getArrayItem($xmlCore->xpath('dc:description')));
416 11
                        $docProps->setSubject((string) self::getArrayItem($xmlCore->xpath('dc:subject')));
417 11
                        $docProps->setKeywords((string) self::getArrayItem($xmlCore->xpath('cp:keywords')));
418 11
                        $docProps->setCategory((string) self::getArrayItem($xmlCore->xpath('cp:category')));
419
                    }
420
421 11
                    break;
422 12
                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties':
423 11
                    $xmlCore = simplexml_load_string(
424 11
                        $this->securityScan($this->getFromZipArchive($zip, "{$rel['Target']}")),
425 11
                        'SimpleXMLElement',
426 11
                        Settings::getLibXmlLoaderOptions()
427
                    );
428 11
                    if (is_object($xmlCore)) {
429 11
                        $docProps = $excel->getProperties();
430 11
                        if (isset($xmlCore->Company)) {
431 9
                            $docProps->setCompany((string) $xmlCore->Company);
432
                        }
433 11
                        if (isset($xmlCore->Manager)) {
434 7
                            $docProps->setManager((string) $xmlCore->Manager);
435
                        }
436
                    }
437
438 11
                    break;
439 12
                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties':
440 3
                    $xmlCore = simplexml_load_string(
441 3
                        $this->securityScan($this->getFromZipArchive($zip, "{$rel['Target']}")),
442 3
                        'SimpleXMLElement',
443 3
                        Settings::getLibXmlLoaderOptions()
444
                    );
445 3
                    if (is_object($xmlCore)) {
446 3
                        $docProps = $excel->getProperties();
447
                        /** @var \SimpleXMLElement $xmlProperty */
448 3
                        foreach ($xmlCore as $xmlProperty) {
449 3
                            $cellDataOfficeAttributes = $xmlProperty->attributes();
450 3
                            if (isset($cellDataOfficeAttributes['name'])) {
451 3
                                $propertyName = (string) $cellDataOfficeAttributes['name'];
452 3
                                $cellDataOfficeChildren = $xmlProperty->children('http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
453 3
                                $attributeType = $cellDataOfficeChildren->getName();
454 3
                                $attributeValue = (string) $cellDataOfficeChildren->{$attributeType};
455 3
                                $attributeValue = Properties::convertProperty($attributeValue, $attributeType);
456 3
                                $attributeType = Properties::convertPropertyType($attributeType);
457 3
                                $docProps->setCustomProperty($propertyName, $attributeValue, $attributeType);
458
                            }
459
                        }
460
                    }
461
462 3
                    break;
463
                //Ribbon
464 12
                case 'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility':
465
                    $customUI = $rel['Target'];
466
                    if ($customUI !== null) {
467
                        $this->readRibbon($excel, $customUI, $zip);
468
                    }
469
470
                    break;
471 12
                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
472 12
                    $dir = dirname($rel['Target']);
473 12
                    $relsWorkbook = simplexml_load_string(
474
                        //~ http://schemas.openxmlformats.org/package/2006/relationships"
475 12
                        $this->securityScan($this->getFromZipArchive($zip, "$dir/_rels/" . basename($rel['Target']) . '.rels')),
476 12
                        'SimpleXMLElement',
477 12
                        Settings::getLibXmlLoaderOptions()
478
                    );
479 12
                    $relsWorkbook->registerXPathNamespace('rel', 'http://schemas.openxmlformats.org/package/2006/relationships');
480
481 12
                    $sharedStrings = [];
482 12
                    $xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings']"));
483 12
                    $xmlStrings = simplexml_load_string(
484
                        //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
485 12
                        $this->securityScan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
486 12
                        'SimpleXMLElement',
487 12
                        Settings::getLibXmlLoaderOptions()
488
                    );
489 12
                    if (isset($xmlStrings, $xmlStrings->si)) {
490 11
                        foreach ($xmlStrings->si as $val) {
491 11
                            if (isset($val->t)) {
492 11
                                $sharedStrings[] = StringHelper::controlCharacterOOXML2PHP((string) $val->t);
493 3
                            } elseif (isset($val->r)) {
494 11
                                $sharedStrings[] = $this->parseRichText($val);
495
                            }
496
                        }
497
                    }
498
499 12
                    $worksheets = [];
500 12
                    $macros = $customUI = null;
501 12
                    foreach ($relsWorkbook->Relationship as $ele) {
502 12
                        switch ($ele['Type']) {
503 12
                            case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet':
504 12
                                $worksheets[(string) $ele['Id']] = $ele['Target'];
505
506 12
                                break;
507
                            // a vbaProject ? (: some macros)
508 12
                            case 'http://schemas.microsoft.com/office/2006/relationships/vbaProject':
509
                                $macros = $ele['Target'];
510
511 12
                                break;
512
                        }
513
                    }
514
515 12
                    if ($macros !== null) {
516
                        $macrosCode = $this->getFromZipArchive($zip, 'xl/vbaProject.bin'); //vbaProject.bin always in 'xl' dir and always named vbaProject.bin
517
                        if ($macrosCode !== false) {
518
                            $excel->setMacrosCode($macrosCode);
519
                            $excel->setHasMacros(true);
520
                            //short-circuit : not reading vbaProject.bin.rel to get Signature =>allways vbaProjectSignature.bin in 'xl' dir
521
                            $Certificate = $this->getFromZipArchive($zip, 'xl/vbaProjectSignature.bin');
522
                            if ($Certificate !== false) {
523
                                $excel->setMacrosCertificate($Certificate);
524
                            }
525
                        }
526
                    }
527 12
                    $styles = [];
528 12
                    $cellStyles = [];
529 12
                    $xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles']"));
530 12
                    $xmlStyles = simplexml_load_string(
531
                        //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
532 12
                        $this->securityScan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
533 12
                        'SimpleXMLElement',
534 12
                        Settings::getLibXmlLoaderOptions()
535
                    );
536 12
                    $numFmts = null;
537 12
                    if ($xmlStyles && $xmlStyles->numFmts[0]) {
538 6
                        $numFmts = $xmlStyles->numFmts[0];
539
                    }
540 12
                    if (isset($numFmts) && ($numFmts !== null)) {
541 6
                        $numFmts->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
542
                    }
543 12
                    if (!$this->readDataOnly && $xmlStyles) {
544 12
                        foreach ($xmlStyles->cellXfs->xf as $xf) {
545 12
                            $numFmt = NumberFormat::FORMAT_GENERAL;
546
547 12
                            if ($xf['numFmtId']) {
548 12
                                if (isset($numFmts)) {
549 6
                                    $tmpNumFmt = self::getArrayItem($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
550
551 6
                                    if (isset($tmpNumFmt['formatCode'])) {
552 3
                                        $numFmt = (string) $tmpNumFmt['formatCode'];
553
                                    }
554
                                }
555
556
                                // We shouldn't override any of the built-in MS Excel values (values below id 164)
557
                                //  But there's a lot of naughty homebrew xlsx writers that do use "reserved" id values that aren't actually used
558
                                //  So we make allowance for them rather than lose formatting masks
559 12
                                if ((int) $xf['numFmtId'] < 164 &&
560 12
                                    NumberFormat::builtInFormatCode((int) $xf['numFmtId']) !== '') {
561 12
                                    $numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']);
562
                                }
563
                            }
564 12
                            $quotePrefix = false;
565 12
                            if (isset($xf['quotePrefix'])) {
566
                                $quotePrefix = (bool) $xf['quotePrefix'];
567
                            }
568
569
                            $style = (object) [
570 12
                                'numFmt' => $numFmt,
571 12
                                'font' => $xmlStyles->fonts->font[(int) ($xf['fontId'])],
572 12
                                'fill' => $xmlStyles->fills->fill[(int) ($xf['fillId'])],
573 12
                                'border' => $xmlStyles->borders->border[(int) ($xf['borderId'])],
574 12
                                'alignment' => $xf->alignment,
575 12
                                'protection' => $xf->protection,
576 12
                                'quotePrefix' => $quotePrefix,
577
                            ];
578 12
                            $styles[] = $style;
579
580
                            // add style to cellXf collection
581 12
                            $objStyle = new Style();
582 12
                            self::readStyle($objStyle, $style);
583 12
                            $excel->addCellXf($objStyle);
584
                        }
585
586 12
                        foreach ($xmlStyles->cellStyleXfs->xf as $xf) {
587 12
                            $numFmt = NumberFormat::FORMAT_GENERAL;
588 12
                            if ($numFmts && $xf['numFmtId']) {
589 6
                                $tmpNumFmt = self::getArrayItem($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
590 6
                                if (isset($tmpNumFmt['formatCode'])) {
591 1
                                    $numFmt = (string) $tmpNumFmt['formatCode'];
592 6
                                } elseif ((int) $xf['numFmtId'] < 165) {
593 6
                                    $numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']);
594
                                }
595
                            }
596
597
                            $cellStyle = (object) [
598 12
                                'numFmt' => $numFmt,
599 12
                                'font' => $xmlStyles->fonts->font[(int) ($xf['fontId'])],
600 12
                                'fill' => $xmlStyles->fills->fill[(int) ($xf['fillId'])],
601 12
                                'border' => $xmlStyles->borders->border[(int) ($xf['borderId'])],
602 12
                                'alignment' => $xf->alignment,
603 12
                                'protection' => $xf->protection,
604 12
                                'quotePrefix' => $quotePrefix,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $quotePrefix does not seem to be defined for all execution paths leading up to this point.
Loading history...
605
                            ];
606 12
                            $cellStyles[] = $cellStyle;
607
608
                            // add style to cellStyleXf collection
609 12
                            $objStyle = new Style();
610 12
                            self::readStyle($objStyle, $cellStyle);
611 12
                            $excel->addCellStyleXf($objStyle);
612
                        }
613
                    }
614
615 12
                    $dxfs = [];
616 12
                    if (!$this->readDataOnly && $xmlStyles) {
617
                        //    Conditional Styles
618 12
                        if ($xmlStyles->dxfs) {
619 12
                            foreach ($xmlStyles->dxfs->dxf as $dxf) {
620
                                $style = new Style(false, true);
621
                                self::readStyle($style, $dxf);
622
                                $dxfs[] = $style;
623
                            }
624
                        }
625
                        //    Cell Styles
626 12
                        if ($xmlStyles->cellStyles) {
627 12
                            foreach ($xmlStyles->cellStyles->cellStyle as $cellStyle) {
628 12
                                if ((int) ($cellStyle['builtinId']) == 0) {
629 12
                                    if (isset($cellStyles[(int) ($cellStyle['xfId'])])) {
630
                                        // Set default style
631 12
                                        $style = new Style();
632 12
                                        self::readStyle($style, $cellStyles[(int) ($cellStyle['xfId'])]);
633
634
                                        // normal style, currently not using it for anything
635
                                    }
636
                                }
637
                            }
638
                        }
639
                    }
640
641 12
                    $xmlWorkbook = simplexml_load_string(
642
                        //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
643 12
                        $this->securityScan($this->getFromZipArchive($zip, "{$rel['Target']}")),
644 12
                        'SimpleXMLElement',
645 12
                        Settings::getLibXmlLoaderOptions()
646
                    );
647
648
                    // Set base date
649 12
                    if ($xmlWorkbook->workbookPr) {
650 11
                        Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
651 11
                        if (isset($xmlWorkbook->workbookPr['date1904'])) {
652
                            if (self::boolean((string) $xmlWorkbook->workbookPr['date1904'])) {
653
                                Date::setExcelCalendar(Date::CALENDAR_MAC_1904);
654
                            }
655
                        }
656
                    }
657
658 12
                    $sheetId = 0; // keep track of new sheet id in final workbook
659 12
                    $oldSheetId = -1; // keep track of old sheet id in final workbook
660 12
                    $countSkippedSheets = 0; // keep track of number of skipped sheets
661 12
                    $mapSheetId = []; // mapping of sheet ids from old to new
662
663 12
                    $charts = $chartDetails = [];
664
665 12
                    if ($xmlWorkbook->sheets) {
666
                        /** @var \SimpleXMLElement $eleSheet */
667 12
                        foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
668 12
                            ++$oldSheetId;
669
670
                            // Check if sheet should be skipped
671 12
                            if (isset($this->loadSheetsOnly) && !in_array((string) $eleSheet['name'], $this->loadSheetsOnly)) {
672
                                ++$countSkippedSheets;
673
                                $mapSheetId[$oldSheetId] = null;
674
675
                                continue;
676
                            }
677
678
                            // Map old sheet id in original workbook to new sheet id.
679
                            // They will differ if loadSheetsOnly() is being used
680 12
                            $mapSheetId[$oldSheetId] = $oldSheetId - $countSkippedSheets;
681
682
                            // Load sheet
683 12
                            $docSheet = $excel->createSheet();
684
                            //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet
685
                            //        references in formula cells... during the load, all formulae should be correct,
686
                            //        and we're simply bringing the worksheet name in line with the formula, not the
687
                            //        reverse
688 12
                            $docSheet->setTitle((string) $eleSheet['name'], false, false);
689 12
                            $fileWorksheet = $worksheets[(string) self::getArrayItem($eleSheet->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id')];
690 12
                            $xmlSheet = simplexml_load_string(
691
                                //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
692 12
                                $this->securityScan($this->getFromZipArchive($zip, "$dir/$fileWorksheet")),
693 12
                                'SimpleXMLElement',
694 12
                                Settings::getLibXmlLoaderOptions()
695
                            );
696
697 12
                            $sharedFormulas = [];
698
699 12
                            if (isset($eleSheet['state']) && (string) $eleSheet['state'] != '') {
700
                                $docSheet->setSheetState((string) $eleSheet['state']);
701
                            }
702
703 12
                            if (isset($xmlSheet->sheetViews, $xmlSheet->sheetViews->sheetView)) {
704 12 View Code Duplication
                                if (isset($xmlSheet->sheetViews->sheetView['zoomScale'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
705
                                    $docSheet->getSheetView()->setZoomScale((int) ($xmlSheet->sheetViews->sheetView['zoomScale']));
706
                                }
707 12 View Code Duplication
                                if (isset($xmlSheet->sheetViews->sheetView['zoomScaleNormal'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
708
                                    $docSheet->getSheetView()->setZoomScaleNormal((int) ($xmlSheet->sheetViews->sheetView['zoomScaleNormal']));
709
                                }
710 12 View Code Duplication
                                if (isset($xmlSheet->sheetViews->sheetView['view'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
711
                                    $docSheet->getSheetView()->setView((string) $xmlSheet->sheetViews->sheetView['view']);
712
                                }
713 12 View Code Duplication
                                if (isset($xmlSheet->sheetViews->sheetView['showGridLines'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
714 5
                                    $docSheet->setShowGridLines(self::boolean((string) $xmlSheet->sheetViews->sheetView['showGridLines']));
715
                                }
716 12 View Code Duplication
                                if (isset($xmlSheet->sheetViews->sheetView['showRowColHeaders'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
717 5
                                    $docSheet->setShowRowColHeaders(self::boolean((string) $xmlSheet->sheetViews->sheetView['showRowColHeaders']));
718
                                }
719 12 View Code Duplication
                                if (isset($xmlSheet->sheetViews->sheetView['rightToLeft'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
720
                                    $docSheet->setRightToLeft(self::boolean((string) $xmlSheet->sheetViews->sheetView['rightToLeft']));
721
                                }
722 12
                                if (isset($xmlSheet->sheetViews->sheetView->pane)) {
723 1
                                    $xSplit = 0;
724 1
                                    $ySplit = 0;
725 1
                                    $topLeftCell = null;
726
727 1
                                    if (isset($xmlSheet->sheetViews->sheetView->pane['xSplit'])) {
728
                                        $xSplit = (int) ($xmlSheet->sheetViews->sheetView->pane['xSplit']);
729
                                    }
730
731 1
                                    if (isset($xmlSheet->sheetViews->sheetView->pane['ySplit'])) {
732 1
                                        $ySplit = (int) ($xmlSheet->sheetViews->sheetView->pane['ySplit']);
733
                                    }
734
735 1
                                    if (isset($xmlSheet->sheetViews->sheetView->pane['topLeftCell'])) {
736 1
                                        $topLeftCell = (string) $xmlSheet->sheetViews->sheetView->pane['topLeftCell'];
737
                                    }
738
739 1
                                    $docSheet->freezePane(Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1), $topLeftCell);
740
                                }
741
742 12
                                if (isset($xmlSheet->sheetViews->sheetView->selection)) {
743 10
                                    if (isset($xmlSheet->sheetViews->sheetView->selection['sqref'])) {
744 10
                                        $sqref = (string) $xmlSheet->sheetViews->sheetView->selection['sqref'];
745 10
                                        $sqref = explode(' ', $sqref);
746 10
                                        $sqref = $sqref[0];
747 10
                                        $docSheet->setSelectedCells($sqref);
748
                                    }
749
                                }
750
                            }
751
752 12
                            if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr->tabColor)) {
753 2
                                if (isset($xmlSheet->sheetPr->tabColor['rgb'])) {
754 2
                                    $docSheet->getTabColor()->setARGB((string) $xmlSheet->sheetPr->tabColor['rgb']);
755
                                }
756
                            }
757 12
                            if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr['codeName'])) {
758
                                $docSheet->setCodeName((string) $xmlSheet->sheetPr['codeName'], false);
759
                            }
760 12
                            if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr->outlinePr)) {
761 5 View Code Duplication
                                if (isset($xmlSheet->sheetPr->outlinePr['summaryRight']) &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
762 5
                                    !self::boolean((string) $xmlSheet->sheetPr->outlinePr['summaryRight'])) {
763
                                    $docSheet->setShowSummaryRight(false);
764
                                } else {
765 5
                                    $docSheet->setShowSummaryRight(true);
766
                                }
767
768 5 View Code Duplication
                                if (isset($xmlSheet->sheetPr->outlinePr['summaryBelow']) &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
769 5
                                    !self::boolean((string) $xmlSheet->sheetPr->outlinePr['summaryBelow'])) {
770
                                    $docSheet->setShowSummaryBelow(false);
771
                                } else {
772 5
                                    $docSheet->setShowSummaryBelow(true);
773
                                }
774
                            }
775
776 12
                            if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr->pageSetUpPr)) {
777
                                if (isset($xmlSheet->sheetPr->pageSetUpPr['fitToPage']) &&
778
                                    !self::boolean((string) $xmlSheet->sheetPr->pageSetUpPr['fitToPage'])) {
779
                                    $docSheet->getPageSetup()->setFitToPage(false);
780
                                } else {
781
                                    $docSheet->getPageSetup()->setFitToPage(true);
782
                                }
783
                            }
784
785 12
                            if (isset($xmlSheet->sheetFormatPr)) {
786 12
                                if (isset($xmlSheet->sheetFormatPr['customHeight']) &&
787 12
                                    self::boolean((string) $xmlSheet->sheetFormatPr['customHeight']) &&
788 12
                                    isset($xmlSheet->sheetFormatPr['defaultRowHeight'])) {
789 1
                                    $docSheet->getDefaultRowDimension()->setRowHeight((float) $xmlSheet->sheetFormatPr['defaultRowHeight']);
790
                                }
791 12
                                if (isset($xmlSheet->sheetFormatPr['defaultColWidth'])) {
792 1
                                    $docSheet->getDefaultColumnDimension()->setWidth((float) $xmlSheet->sheetFormatPr['defaultColWidth']);
793
                                }
794 12
                                if (isset($xmlSheet->sheetFormatPr['zeroHeight']) &&
795 12
                                    ((string) $xmlSheet->sheetFormatPr['zeroHeight'] == '1')) {
796
                                    $docSheet->getDefaultRowDimension()->setZeroHeight(true);
797
                                }
798
                            }
799
800 12
                            if (isset($xmlSheet->cols) && !$this->readDataOnly) {
801 7
                                foreach ($xmlSheet->cols->col as $col) {
802 7
                                    for ($i = (int) ($col['min']); $i <= (int) ($col['max']); ++$i) {
803 7
                                        if ($col['style'] && !$this->readDataOnly) {
804 3
                                            $docSheet->getColumnDimension(Coordinate::stringFromColumnIndex($i))->setXfIndex((int) ($col['style']));
805
                                        }
806 7
                                        if (self::boolean($col['hidden'])) {
807 1
                                            $docSheet->getColumnDimension(Coordinate::stringFromColumnIndex($i))->setVisible(false);
808
                                        }
809 7
                                        if (self::boolean($col['collapsed'])) {
810 1
                                            $docSheet->getColumnDimension(Coordinate::stringFromColumnIndex($i))->setCollapsed(true);
811
                                        }
812 7
                                        if ($col['outlineLevel'] > 0) {
813 1
                                            $docSheet->getColumnDimension(Coordinate::stringFromColumnIndex($i))->setOutlineLevel((int) ($col['outlineLevel']));
814
                                        }
815 7
                                        $docSheet->getColumnDimension(Coordinate::stringFromColumnIndex($i))->setWidth((float) ($col['width']));
816
817 7
                                        if ((int) ($col['max']) == 16384) {
818
                                            break;
819
                                        }
820
                                    }
821
                                }
822
                            }
823
824 12
                            if (isset($xmlSheet->printOptions) && !$this->readDataOnly) {
825 5
                                if (self::boolean((string) $xmlSheet->printOptions['gridLinesSet'])) {
826 5
                                    $docSheet->setShowGridlines(true);
827
                                }
828 5
                                if (self::boolean((string) $xmlSheet->printOptions['gridLines'])) {
829
                                    $docSheet->setPrintGridlines(true);
830
                                }
831 5
                                if (self::boolean((string) $xmlSheet->printOptions['horizontalCentered'])) {
832
                                    $docSheet->getPageSetup()->setHorizontalCentered(true);
833
                                }
834 5
                                if (self::boolean((string) $xmlSheet->printOptions['verticalCentered'])) {
835
                                    $docSheet->getPageSetup()->setVerticalCentered(true);
836
                                }
837
                            }
838
839 12
                            if ($xmlSheet && $xmlSheet->sheetData && $xmlSheet->sheetData->row) {
840 12
                                $cIndex = 1; // Cell Start from 1
841 12
                                foreach ($xmlSheet->sheetData->row as $row) {
842 12
                                    if ($row['ht'] && !$this->readDataOnly) {
843 2
                                        $docSheet->getRowDimension((int) ($row['r']))->setRowHeight((float) ($row['ht']));
844
                                    }
845 12
                                    if (self::boolean($row['hidden']) && !$this->readDataOnly) {
846
                                        $docSheet->getRowDimension((int) ($row['r']))->setVisible(false);
847
                                    }
848 12
                                    if (self::boolean($row['collapsed'])) {
849
                                        $docSheet->getRowDimension((int) ($row['r']))->setCollapsed(true);
850
                                    }
851 12
                                    if ($row['outlineLevel'] > 0) {
852
                                        $docSheet->getRowDimension((int) ($row['r']))->setOutlineLevel((int) ($row['outlineLevel']));
853
                                    }
854 12
                                    if ($row['s'] && !$this->readDataOnly) {
855
                                        $docSheet->getRowDimension((int) ($row['r']))->setXfIndex((int) ($row['s']));
856
                                    }
857
858 12
                                    $rowIndex = 1;
859 12
                                    foreach ($row->c as $c) {
860 12
                                        $r = (string) $c['r'];
861 12
                                        if ($r == '') {
862 1
                                            $r = Coordinate::stringFromColumnIndex($rowIndex) . $cIndex;
863
                                        }
864 12
                                        $cellDataType = (string) $c['t'];
865 12
                                        $value = null;
866 12
                                        $calculatedValue = null;
867
868
                                        // Read cell?
869 12
                                        if ($this->getReadFilter() !== null) {
870 12
                                            $coordinates = Coordinate::coordinateFromString($r);
871
872 12
                                            if (!$this->getReadFilter()->readCell($coordinates[0], $coordinates[1], $docSheet->getTitle())) {
873 1
                                                continue;
874
                                            }
875
                                        }
876
877
                                        // Read cell!
878
                                        switch ($cellDataType) {
879 12
                                            case 's':
880 11
                                                if ((string) $c->v != '') {
881 11
                                                    $value = $sharedStrings[(int) ($c->v)];
882
883 11
                                                    if ($value instanceof RichText) {
884 11
                                                        $value = clone $value;
885
                                                    }
886
                                                } else {
887
                                                    $value = '';
888
                                                }
889
890 11
                                                break;
891 8
                                            case 'b':
892 1
                                                if (!isset($c->f)) {
893 1
                                                    $value = self::castToBoolean($c);
894
                                                } else {
895
                                                    // Formula
896
                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToBoolean');
897
                                                    if (isset($c->f['t'])) {
898
                                                        $att = $c->f;
899
                                                        $docSheet->getCell($r)->setFormulaAttributes($att);
900
                                                    }
901
                                                }
902
903 1
                                                break;
904 7 View Code Duplication
                                            case 'inlineStr':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
905 2
                                                if (isset($c->f)) {
906
                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToError');
907
                                                } else {
908 2
                                                    $value = $this->parseRichText($c->is);
909
                                                }
910
911 2
                                                break;
912 7 View Code Duplication
                                            case 'e':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
913
                                                if (!isset($c->f)) {
914
                                                    $value = self::castToError($c);
915
                                                } else {
916
                                                    // Formula
917
                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToError');
918
                                                }
919
920
                                                break;
921
                                            default:
922 7
                                                if (!isset($c->f)) {
923 7
                                                    $value = self::castToString($c);
924
                                                } else {
925
                                                    // Formula
926 4
                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToString');
927
                                                }
928
929 7
                                                break;
930
                                        }
931
932
                                        // Check for numeric values
933 12
                                        if (is_numeric($value) && $cellDataType != 's') {
934 7
                                            if ($value == (int) $value) {
935 7
                                                $value = (int) $value;
936
                                            } elseif ($value == (float) $value) {
937
                                                $value = (float) $value;
938
                                            } elseif ($value == (float) $value) {
939
                                                $value = (float) $value;
940
                                            }
941
                                        }
942
943
                                        // Rich text?
944 12
                                        if ($value instanceof RichText && $this->readDataOnly) {
945
                                            $value = $value->getPlainText();
946
                                        }
947
948 12
                                        $cell = $docSheet->getCell($r);
949
                                        // Assign value
950 12
                                        if ($cellDataType != '') {
951 11
                                            $cell->setValueExplicit($value, $cellDataType);
952
                                        } else {
953 7
                                            $cell->setValue($value);
954
                                        }
955 12
                                        if ($calculatedValue !== null) {
956 4
                                            $cell->setCalculatedValue($calculatedValue);
957
                                        }
958
959
                                        // Style information?
960 12
                                        if ($c['s'] && !$this->readDataOnly) {
961
                                            // no style index means 0, it seems
962 7
                                            $cell->setXfIndex(isset($styles[(int) ($c['s'])]) ?
963 7
                                                (int) ($c['s']) : 0);
964
                                        }
965 12
                                        $rowIndex += 1;
966
                                    }
967 12
                                    $cIndex += 1;
968
                                }
969
                            }
970
971 12
                            $conditionals = [];
972 12
                            if (!$this->readDataOnly && $xmlSheet && $xmlSheet->conditionalFormatting) {
973
                                foreach ($xmlSheet->conditionalFormatting as $conditional) {
974
                                    foreach ($conditional->cfRule as $cfRule) {
975
                                        if (((string) $cfRule['type'] == Conditional::CONDITION_NONE || (string) $cfRule['type'] == Conditional::CONDITION_CELLIS || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT || (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION) && isset($dxfs[(int) ($cfRule['dxfId'])])) {
976
                                            $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
977
                                        }
978
                                    }
979
                                }
980
981
                                foreach ($conditionals as $ref => $cfRules) {
982
                                    ksort($cfRules);
983
                                    $conditionalStyles = [];
984
                                    foreach ($cfRules as $cfRule) {
985
                                        $objConditional = new Conditional();
986
                                        $objConditional->setConditionType((string) $cfRule['type']);
987
                                        $objConditional->setOperatorType((string) $cfRule['operator']);
988
989
                                        if ((string) $cfRule['text'] != '') {
990
                                            $objConditional->setText((string) $cfRule['text']);
991
                                        }
992
993
                                        if (count($cfRule->formula) > 1) {
994
                                            foreach ($cfRule->formula as $formula) {
995
                                                $objConditional->addCondition((string) $formula);
996
                                            }
997
                                        } else {
998
                                            $objConditional->addCondition((string) $cfRule->formula);
999
                                        }
1000
                                        $objConditional->setStyle(clone $dxfs[(int) ($cfRule['dxfId'])]);
1001
                                        $conditionalStyles[] = $objConditional;
1002
                                    }
1003
1004
                                    // Extract all cell references in $ref
1005
                                    $cellBlocks = explode(' ', str_replace('$', '', strtoupper($ref)));
1006
                                    foreach ($cellBlocks as $cellBlock) {
1007
                                        $docSheet->getStyle($cellBlock)->setConditionalStyles($conditionalStyles);
1008
                                    }
1009
                                }
1010
                            }
1011
1012 12
                            $aKeys = ['sheet', 'objects', 'scenarios', 'formatCells', 'formatColumns', 'formatRows', 'insertColumns', 'insertRows', 'insertHyperlinks', 'deleteColumns', 'deleteRows', 'selectLockedCells', 'sort', 'autoFilter', 'pivotTables', 'selectUnlockedCells'];
1013 12
                            if (!$this->readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) {
1014 7
                                foreach ($aKeys as $key) {
1015 7
                                    $method = 'set' . ucfirst($key);
1016 7
                                    $docSheet->getProtection()->$method(self::boolean((string) $xmlSheet->sheetProtection[$key]));
1017
                                }
1018
                            }
1019
1020 12
                            if (!$this->readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) {
1021 7
                                $docSheet->getProtection()->setPassword((string) $xmlSheet->sheetProtection['password'], true);
1022 7
                                if ($xmlSheet->protectedRanges->protectedRange) {
1023 2
                                    foreach ($xmlSheet->protectedRanges->protectedRange as $protectedRange) {
1024 2
                                        $docSheet->protectCells((string) $protectedRange['sqref'], (string) $protectedRange['password'], true);
1025
                                    }
1026
                                }
1027
                            }
1028
1029 12
                            if ($xmlSheet && $xmlSheet->autoFilter && !$this->readDataOnly) {
1030
                                $autoFilterRange = (string) $xmlSheet->autoFilter['ref'];
1031
                                if (strpos($autoFilterRange, ':') !== false) {
1032
                                    $autoFilter = $docSheet->getAutoFilter();
1033
                                    $autoFilter->setRange($autoFilterRange);
1034
1035
                                    foreach ($xmlSheet->autoFilter->filterColumn as $filterColumn) {
1036
                                        $column = $autoFilter->getColumnByOffset((int) $filterColumn['colId']);
1037
                                        //    Check for standard filters
1038
                                        if ($filterColumn->filters) {
1039
                                            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER);
1040
                                            $filters = $filterColumn->filters;
1041
                                            if ((isset($filters['blank'])) && ($filters['blank'] == 1)) {
1042
                                                //    Operator is undefined, but always treated as EQUAL
1043
                                                $column->createRule()->setRule(null, '')->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_FILTER);
1044
                                            }
1045
                                            //    Standard filters are always an OR join, so no join rule needs to be set
1046
                                            //    Entries can be either filter elements
1047 View Code Duplication
                                            foreach ($filters->filter as $filterRule) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1048
                                                //    Operator is undefined, but always treated as EQUAL
1049
                                                $column->createRule()->setRule(null, (string) $filterRule['val'])->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_FILTER);
1050
                                            }
1051
                                            //    Or Date Group elements
1052
                                            foreach ($filters->dateGroupItem as $dateGroupItem) {
1053
                                                $column->createRule()->setRule(
1054
                                                    //    Operator is undefined, but always treated as EQUAL
1055
                                                    null,
1056
                                                    [
1057
                                                        'year' => (string) $dateGroupItem['year'],
1058
                                                        'month' => (string) $dateGroupItem['month'],
1059
                                                        'day' => (string) $dateGroupItem['day'],
1060
                                                        'hour' => (string) $dateGroupItem['hour'],
1061
                                                        'minute' => (string) $dateGroupItem['minute'],
1062
                                                        'second' => (string) $dateGroupItem['second'],
1063
                                                    ],
1064
                                                    (string) $dateGroupItem['dateTimeGrouping']
1065
                                                )
1066
                                                ->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP);
1067
                                            }
1068
                                        }
1069
                                        //    Check for custom filters
1070
                                        if ($filterColumn->customFilters) {
1071
                                            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
1072
                                            $customFilters = $filterColumn->customFilters;
1073
                                            //    Custom filters can an AND or an OR join;
1074
                                            //        and there should only ever be one or two entries
1075
                                            if ((isset($customFilters['and'])) && ($customFilters['and'] == 1)) {
1076
                                                $column->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND);
1077
                                            }
1078 View Code Duplication
                                            foreach ($customFilters->customFilter as $filterRule) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1079
                                                $column->createRule()->setRule(
1080
                                                    (string) $filterRule['operator'],
1081
                                                    (string) $filterRule['val']
1082
                                                )
1083
                                                ->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
1084
                                            }
1085
                                        }
1086
                                        //    Check for dynamic filters
1087
                                        if ($filterColumn->dynamicFilter) {
1088
                                            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
1089
                                            //    We should only ever have one dynamic filter
1090
                                            foreach ($filterColumn->dynamicFilter as $filterRule) {
1091
                                                $column->createRule()->setRule(
1092
                                                    //    Operator is undefined, but always treated as EQUAL
1093
                                                    null,
1094
                                                    (string) $filterRule['val'],
1095
                                                    (string) $filterRule['type']
1096
                                                )
1097
                                                ->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
1098
                                                if (isset($filterRule['val'])) {
1099
                                                    $column->setAttribute('val', (string) $filterRule['val']);
1100
                                                }
1101
                                                if (isset($filterRule['maxVal'])) {
1102
                                                    $column->setAttribute('maxVal', (string) $filterRule['maxVal']);
1103
                                                }
1104
                                            }
1105
                                        }
1106
                                        //    Check for dynamic filters
1107
                                        if ($filterColumn->top10) {
1108
                                            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER);
1109
                                            //    We should only ever have one top10 filter
1110
                                            foreach ($filterColumn->top10 as $filterRule) {
1111
                                                $column->createRule()->setRule(
1112
                                                    (((isset($filterRule['percent'])) && ($filterRule['percent'] == 1))
1113
                                                        ? Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT
1114
                                                        : Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE
1115
                                                    ),
1116
                                                    (string) $filterRule['val'],
1117
                                                    (((isset($filterRule['top'])) && ($filterRule['top'] == 1))
1118
                                                        ? Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP
1119
                                                        : Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM
1120
                                                    )
1121
                                                )
1122
                                                ->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_TOPTENFILTER);
1123
                                            }
1124
                                        }
1125
                                    }
1126
                                }
1127
                            }
1128
1129 12
                            if ($xmlSheet && $xmlSheet->mergeCells && $xmlSheet->mergeCells->mergeCell && !$this->readDataOnly) {
1130 5
                                foreach ($xmlSheet->mergeCells->mergeCell as $mergeCell) {
1131 5
                                    $mergeRef = (string) $mergeCell['ref'];
1132 5
                                    if (strpos($mergeRef, ':') !== false) {
1133 5
                                        $docSheet->mergeCells((string) $mergeCell['ref']);
1134
                                    }
1135
                                }
1136
                            }
1137
1138 12
                            if ($xmlSheet && $xmlSheet->pageMargins && !$this->readDataOnly) {
1139 11
                                $docPageMargins = $docSheet->getPageMargins();
1140 11
                                $docPageMargins->setLeft((float) ($xmlSheet->pageMargins['left']));
1141 11
                                $docPageMargins->setRight((float) ($xmlSheet->pageMargins['right']));
1142 11
                                $docPageMargins->setTop((float) ($xmlSheet->pageMargins['top']));
1143 11
                                $docPageMargins->setBottom((float) ($xmlSheet->pageMargins['bottom']));
1144 11
                                $docPageMargins->setHeader((float) ($xmlSheet->pageMargins['header']));
1145 11
                                $docPageMargins->setFooter((float) ($xmlSheet->pageMargins['footer']));
1146
                            }
1147
1148 12
                            if ($xmlSheet && $xmlSheet->pageSetup && !$this->readDataOnly) {
1149 11
                                $docPageSetup = $docSheet->getPageSetup();
1150
1151 11
                                if (isset($xmlSheet->pageSetup['orientation'])) {
1152 11
                                    $docPageSetup->setOrientation((string) $xmlSheet->pageSetup['orientation']);
1153
                                }
1154 11
                                if (isset($xmlSheet->pageSetup['paperSize'])) {
1155 9
                                    $docPageSetup->setPaperSize((int) ($xmlSheet->pageSetup['paperSize']));
1156
                                }
1157 11
                                if (isset($xmlSheet->pageSetup['scale'])) {
1158 5
                                    $docPageSetup->setScale((int) ($xmlSheet->pageSetup['scale']), false);
1159
                                }
1160 11 View Code Duplication
                                if (isset($xmlSheet->pageSetup['fitToHeight']) && (int) ($xmlSheet->pageSetup['fitToHeight']) >= 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1161 5
                                    $docPageSetup->setFitToHeight((int) ($xmlSheet->pageSetup['fitToHeight']), false);
1162
                                }
1163 11 View Code Duplication
                                if (isset($xmlSheet->pageSetup['fitToWidth']) && (int) ($xmlSheet->pageSetup['fitToWidth']) >= 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1164 5
                                    $docPageSetup->setFitToWidth((int) ($xmlSheet->pageSetup['fitToWidth']), false);
1165
                                }
1166 11
                                if (isset($xmlSheet->pageSetup['firstPageNumber'], $xmlSheet->pageSetup['useFirstPageNumber']) &&
1167 11
                                    self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])) {
1168
                                    $docPageSetup->setFirstPageNumber((int) ($xmlSheet->pageSetup['firstPageNumber']));
1169
                                }
1170
                            }
1171
1172 12
                            if ($xmlSheet && $xmlSheet->headerFooter && !$this->readDataOnly) {
1173 7
                                $docHeaderFooter = $docSheet->getHeaderFooter();
1174
1175 7 View Code Duplication
                                if (isset($xmlSheet->headerFooter['differentOddEven']) &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1176 7
                                    self::boolean((string) $xmlSheet->headerFooter['differentOddEven'])) {
1177
                                    $docHeaderFooter->setDifferentOddEven(true);
1178
                                } else {
1179 7
                                    $docHeaderFooter->setDifferentOddEven(false);
1180
                                }
1181 7 View Code Duplication
                                if (isset($xmlSheet->headerFooter['differentFirst']) &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1182 7
                                    self::boolean((string) $xmlSheet->headerFooter['differentFirst'])) {
1183
                                    $docHeaderFooter->setDifferentFirst(true);
1184
                                } else {
1185 7
                                    $docHeaderFooter->setDifferentFirst(false);
1186
                                }
1187 7 View Code Duplication
                                if (isset($xmlSheet->headerFooter['scaleWithDoc']) &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1188 7
                                    !self::boolean((string) $xmlSheet->headerFooter['scaleWithDoc'])) {
1189
                                    $docHeaderFooter->setScaleWithDocument(false);
1190
                                } else {
1191 7
                                    $docHeaderFooter->setScaleWithDocument(true);
1192
                                }
1193 7 View Code Duplication
                                if (isset($xmlSheet->headerFooter['alignWithMargins']) &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1194 7
                                    !self::boolean((string) $xmlSheet->headerFooter['alignWithMargins'])) {
1195 3
                                    $docHeaderFooter->setAlignWithMargins(false);
1196
                                } else {
1197 4
                                    $docHeaderFooter->setAlignWithMargins(true);
1198
                                }
1199
1200 7
                                $docHeaderFooter->setOddHeader((string) $xmlSheet->headerFooter->oddHeader);
1201 7
                                $docHeaderFooter->setOddFooter((string) $xmlSheet->headerFooter->oddFooter);
1202 7
                                $docHeaderFooter->setEvenHeader((string) $xmlSheet->headerFooter->evenHeader);
1203 7
                                $docHeaderFooter->setEvenFooter((string) $xmlSheet->headerFooter->evenFooter);
1204 7
                                $docHeaderFooter->setFirstHeader((string) $xmlSheet->headerFooter->firstHeader);
1205 7
                                $docHeaderFooter->setFirstFooter((string) $xmlSheet->headerFooter->firstFooter);
1206
                            }
1207
1208 12
                            if ($xmlSheet && $xmlSheet->rowBreaks && $xmlSheet->rowBreaks->brk && !$this->readDataOnly) {
1209
                                foreach ($xmlSheet->rowBreaks->brk as $brk) {
1210
                                    if ($brk['man']) {
1211
                                        $docSheet->setBreak("A$brk[id]", Worksheet::BREAK_ROW);
1212
                                    }
1213
                                }
1214
                            }
1215 12
                            if ($xmlSheet && $xmlSheet->colBreaks && $xmlSheet->colBreaks->brk && !$this->readDataOnly) {
1216
                                foreach ($xmlSheet->colBreaks->brk as $brk) {
1217
                                    if ($brk['man']) {
1218
                                        $docSheet->setBreak(Coordinate::stringFromColumnIndex((string) $brk['id'] + 1) . '1', Worksheet::BREAK_COLUMN);
1219
                                    }
1220
                                }
1221
                            }
1222
1223 12
                            if ($xmlSheet && $xmlSheet->dataValidations && !$this->readDataOnly) {
1224
                                foreach ($xmlSheet->dataValidations->dataValidation as $dataValidation) {
1225
                                    // Uppercase coordinate
1226
                                    $range = strtoupper($dataValidation['sqref']);
1227
                                    $rangeSet = explode(' ', $range);
1228
                                    foreach ($rangeSet as $range) {
1229
                                        $stRange = $docSheet->shrinkRangeToFit($range);
1230
1231
                                        // Extract all cell references in $range
1232
                                        foreach (Coordinate::extractAllCellReferencesInRange($stRange) as $reference) {
1233
                                            // Create validation
1234
                                            $docValidation = $docSheet->getCell($reference)->getDataValidation();
1235
                                            $docValidation->setType((string) $dataValidation['type']);
1236
                                            $docValidation->setErrorStyle((string) $dataValidation['errorStyle']);
1237
                                            $docValidation->setOperator((string) $dataValidation['operator']);
1238
                                            $docValidation->setAllowBlank($dataValidation['allowBlank'] != 0);
1239
                                            $docValidation->setShowDropDown($dataValidation['showDropDown'] == 0);
1240
                                            $docValidation->setShowInputMessage($dataValidation['showInputMessage'] != 0);
1241
                                            $docValidation->setShowErrorMessage($dataValidation['showErrorMessage'] != 0);
1242
                                            $docValidation->setErrorTitle((string) $dataValidation['errorTitle']);
1243
                                            $docValidation->setError((string) $dataValidation['error']);
1244
                                            $docValidation->setPromptTitle((string) $dataValidation['promptTitle']);
1245
                                            $docValidation->setPrompt((string) $dataValidation['prompt']);
1246
                                            $docValidation->setFormula1((string) $dataValidation->formula1);
1247
                                            $docValidation->setFormula2((string) $dataValidation->formula2);
1248
                                        }
1249
                                    }
1250
                                }
1251
                            }
1252
1253
                            // Add hyperlinks
1254 12
                            $hyperlinks = [];
1255 12
                            if (!$this->readDataOnly) {
1256
                                // Locate hyperlink relations
1257 12
                                if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
1258 11
                                    $relsWorksheet = simplexml_load_string(
1259
                                        //~ http://schemas.openxmlformats.org/package/2006/relationships"
1260 11
                                        $this->securityScan(
1261 11
                                            $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
1262
                                        ),
1263 11
                                        'SimpleXMLElement',
1264 11
                                        Settings::getLibXmlLoaderOptions()
1265
                                    );
1266 11
                                    foreach ($relsWorksheet->Relationship as $ele) {
1267 8
                                        if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink') {
1268 8
                                            $hyperlinks[(string) $ele['Id']] = (string) $ele['Target'];
1269
                                        }
1270
                                    }
1271
                                }
1272
1273
                                // Loop through hyperlinks
1274 12
                                if ($xmlSheet && $xmlSheet->hyperlinks) {
1275
                                    /** @var \SimpleXMLElement $hyperlink */
1276 2
                                    foreach ($xmlSheet->hyperlinks->hyperlink as $hyperlink) {
1277
                                        // Link url
1278 2
                                        $linkRel = $hyperlink->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
1279
1280 2
                                        foreach (Coordinate::extractAllCellReferencesInRange($hyperlink['ref']) as $cellReference) {
1281 2
                                            $cell = $docSheet->getCell($cellReference);
1282 2
                                            if (isset($linkRel['id'])) {
1283 2
                                                $hyperlinkUrl = $hyperlinks[(string) $linkRel['id']];
1284 2
                                                if (isset($hyperlink['location'])) {
1285
                                                    $hyperlinkUrl .= '#' . (string) $hyperlink['location'];
1286
                                                }
1287 2
                                                $cell->getHyperlink()->setUrl($hyperlinkUrl);
1288 2
                                            } elseif (isset($hyperlink['location'])) {
1289 2
                                                $cell->getHyperlink()->setUrl('sheet://' . (string) $hyperlink['location']);
1290
                                            }
1291
1292
                                            // Tooltip
1293 2
                                            if (isset($hyperlink['tooltip'])) {
1294 2
                                                $cell->getHyperlink()->setTooltip((string) $hyperlink['tooltip']);
1295
                                            }
1296
                                        }
1297
                                    }
1298
                                }
1299
                            }
1300
1301
                            // Add comments
1302 12
                            $comments = [];
1303 12
                            $vmlComments = [];
1304 12
                            if (!$this->readDataOnly) {
1305
                                // Locate comment relations
1306 12
                                if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
1307 11
                                    $relsWorksheet = simplexml_load_string(
1308
                                        //~ http://schemas.openxmlformats.org/package/2006/relationships"
1309 11
                                        $this->securityScan(
1310 11
                                            $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
1311
                                        ),
1312 11
                                        'SimpleXMLElement',
1313 11
                                        Settings::getLibXmlLoaderOptions()
1314
                                    );
1315 11
                                    foreach ($relsWorksheet->Relationship as $ele) {
1316 8
                                        if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments') {
1317 2
                                            $comments[(string) $ele['Id']] = (string) $ele['Target'];
1318
                                        }
1319 8
                                        if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing') {
1320 8
                                            $vmlComments[(string) $ele['Id']] = (string) $ele['Target'];
1321
                                        }
1322
                                    }
1323
                                }
1324
1325
                                // Loop through comments
1326 12
                                foreach ($comments as $relName => $relPath) {
1327
                                    // Load comments file
1328 2
                                    $relPath = File::realpath(dirname("$dir/$fileWorksheet") . '/' . $relPath);
1329 2
                                    $commentsFile = simplexml_load_string(
1330 2
                                        $this->securityScan($this->getFromZipArchive($zip, $relPath)),
1331 2
                                        'SimpleXMLElement',
1332 2
                                        Settings::getLibXmlLoaderOptions()
1333
                                    );
1334
1335
                                    // Utility variables
1336 2
                                    $authors = [];
1337
1338
                                    // Loop through authors
1339 2
                                    foreach ($commentsFile->authors->author as $author) {
1340 2
                                        $authors[] = (string) $author;
1341
                                    }
1342
1343
                                    // Loop through contents
1344 2
                                    foreach ($commentsFile->commentList->comment as $comment) {
1345 2
                                        if (!empty($comment['authorId'])) {
1346
                                            $docSheet->getComment((string) $comment['ref'])->setAuthor($authors[(string) $comment['authorId']]);
1347
                                        }
1348 2
                                        $docSheet->getComment((string) $comment['ref'])->setText($this->parseRichText($comment->text));
1349
                                    }
1350
                                }
1351
1352
                                // Loop through VML comments
1353 12
                                foreach ($vmlComments as $relName => $relPath) {
1354
                                    // Load VML comments file
1355 2
                                    $relPath = File::realpath(dirname("$dir/$fileWorksheet") . '/' . $relPath);
1356 2
                                    $vmlCommentsFile = simplexml_load_string(
1357 2
                                        $this->securityScan($this->getFromZipArchive($zip, $relPath)),
1358 2
                                        'SimpleXMLElement',
1359 2
                                        Settings::getLibXmlLoaderOptions()
1360
                                    );
1361 2
                                    $vmlCommentsFile->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
1362
1363 2
                                    $shapes = $vmlCommentsFile->xpath('//v:shape');
1364 2
                                    foreach ($shapes as $shape) {
1365 2
                                        $shape->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
1366
1367 2
                                        if (isset($shape['style'])) {
1368 2
                                            $style = (string) $shape['style'];
1369 2
                                            $fillColor = strtoupper(substr((string) $shape['fillcolor'], 1));
1 ignored issue
show
Bug introduced by
It seems like substr((string)$shape['fillcolor'], 1) can also be of type false; however, parameter $string of strtoupper() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1369
                                            $fillColor = strtoupper(/** @scrutinizer ignore-type */ substr((string) $shape['fillcolor'], 1));
Loading history...
1370 2
                                            $column = null;
1371 2
                                            $row = null;
1372
1373 2
                                            $clientData = $shape->xpath('.//x:ClientData');
1374 2
                                            if (is_array($clientData) && !empty($clientData)) {
1375 2
                                                $clientData = $clientData[0];
1376
1377 2
                                                if (isset($clientData['ObjectType']) && (string) $clientData['ObjectType'] == 'Note') {
1378 2
                                                    $temp = $clientData->xpath('.//x:Row');
1379 2
                                                    if (is_array($temp)) {
1380 2
                                                        $row = $temp[0];
1381
                                                    }
1382
1383 2
                                                    $temp = $clientData->xpath('.//x:Column');
1384 2
                                                    if (is_array($temp)) {
1385 2
                                                        $column = $temp[0];
1386
                                                    }
1387
                                                }
1388
                                            }
1389
1390 2
                                            if (($column !== null) && ($row !== null)) {
1391
                                                // Set comment properties
1392 2
                                                $comment = $docSheet->getCommentByColumnAndRow((string) $column, $row + 1);
1393 2
                                                $comment->getFillColor()->setRGB($fillColor);
1394
1395
                                                // Parse style
1396 2
                                                $styleArray = explode(';', str_replace(' ', '', $style));
1397 2
                                                foreach ($styleArray as $stylePair) {
1398 2
                                                    $stylePair = explode(':', $stylePair);
1399
1400 2
                                                    if ($stylePair[0] == 'margin-left') {
1401 2
                                                        $comment->setMarginLeft($stylePair[1]);
1402
                                                    }
1403 2
                                                    if ($stylePair[0] == 'margin-top') {
1404 2
                                                        $comment->setMarginTop($stylePair[1]);
1405
                                                    }
1406 2
                                                    if ($stylePair[0] == 'width') {
1407 2
                                                        $comment->setWidth($stylePair[1]);
1408
                                                    }
1409 2
                                                    if ($stylePair[0] == 'height') {
1410 2
                                                        $comment->setHeight($stylePair[1]);
1411
                                                    }
1412 2
                                                    if ($stylePair[0] == 'visibility') {
1413 2
                                                        $comment->setVisible($stylePair[1] == 'visible');
1414
                                                    }
1415
                                                }
1416
                                            }
1417
                                        }
1418
                                    }
1419
                                }
1420
1421
                                // Header/footer images
1422 12
                                if ($xmlSheet && $xmlSheet->legacyDrawingHF && !$this->readDataOnly) {
1423
                                    if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
1424
                                        $relsWorksheet = simplexml_load_string(
1425
                                            //~ http://schemas.openxmlformats.org/package/2006/relationships"
1426
                                            $this->securityScan(
1427
                                                $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
1428
                                            ),
1429
                                            'SimpleXMLElement',
1430
                                            Settings::getLibXmlLoaderOptions()
1431
                                        );
1432
                                        $vmlRelationship = '';
1433
1434
                                        foreach ($relsWorksheet->Relationship as $ele) {
1435
                                            if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing') {
1436
                                                $vmlRelationship = self::dirAdd("$dir/$fileWorksheet", $ele['Target']);
1437
                                            }
1438
                                        }
1439
1440
                                        if ($vmlRelationship != '') {
1441
                                            // Fetch linked images
1442
                                            $relsVML = simplexml_load_string(
1443
                                                //~ http://schemas.openxmlformats.org/package/2006/relationships"
1444
                                                $this->securityScan(
1445
                                                    $this->getFromZipArchive($zip, dirname($vmlRelationship) . '/_rels/' . basename($vmlRelationship) . '.rels')
1446
                                                ),
1447
                                                'SimpleXMLElement',
1448
                                                Settings::getLibXmlLoaderOptions()
1449
                                            );
1450
                                            $drawings = [];
1451 View Code Duplication
                                            foreach ($relsVML->Relationship as $ele) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1452
                                                if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') {
1453
                                                    $drawings[(string) $ele['Id']] = self::dirAdd($vmlRelationship, $ele['Target']);
1454
                                                }
1455
                                            }
1456
1457
                                            // Fetch VML document
1458
                                            $vmlDrawing = simplexml_load_string(
1459
                                                $this->securityScan($this->getFromZipArchive($zip, $vmlRelationship)),
1460
                                                'SimpleXMLElement',
1461
                                                Settings::getLibXmlLoaderOptions()
1462
                                            );
1463
                                            $vmlDrawing->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
1464
1465
                                            $hfImages = [];
1466
1467
                                            $shapes = $vmlDrawing->xpath('//v:shape');
1468
                                            foreach ($shapes as $idx => $shape) {
1469
                                                $shape->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
1470
                                                $imageData = $shape->xpath('//v:imagedata');
1471
1472
                                                if (!$imageData) {
1473
                                                    continue;
1474
                                                }
1475
1476
                                                $imageData = $imageData[$idx];
1477
1478
                                                $imageData = $imageData->attributes('urn:schemas-microsoft-com:office:office');
1479
                                                $style = self::toCSSArray((string) $shape['style']);
1480
1481
                                                $hfImages[(string) $shape['id']] = new HeaderFooterDrawing();
1482
                                                if (isset($imageData['title'])) {
1483
                                                    $hfImages[(string) $shape['id']]->setName((string) $imageData['title']);
1484
                                                }
1485
1486
                                                $hfImages[(string) $shape['id']]->setPath('zip://' . File::realpath($pFilename) . '#' . $drawings[(string) $imageData['relid']], false);
1487
                                                $hfImages[(string) $shape['id']]->setResizeProportional(false);
1488
                                                $hfImages[(string) $shape['id']]->setWidth($style['width']);
1489
                                                $hfImages[(string) $shape['id']]->setHeight($style['height']);
1490
                                                if (isset($style['margin-left'])) {
1491
                                                    $hfImages[(string) $shape['id']]->setOffsetX($style['margin-left']);
1492
                                                }
1493
                                                $hfImages[(string) $shape['id']]->setOffsetY($style['margin-top']);
1494
                                                $hfImages[(string) $shape['id']]->setResizeProportional(true);
1495
                                            }
1496
1497
                                            $docSheet->getHeaderFooter()->setImages($hfImages);
1498
                                        }
1499
                                    }
1500
                                }
1501
                            }
1502
1503
                            // TODO: Autoshapes from twoCellAnchors!
1504 12
                            if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
1505 11
                                $relsWorksheet = simplexml_load_string(
1506
                                    //~ http://schemas.openxmlformats.org/package/2006/relationships"
1507 11
                                    $this->securityScan(
1508 11
                                        $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
1509
                                    ),
1510 11
                                    'SimpleXMLElement',
1511 11
                                    Settings::getLibXmlLoaderOptions()
1512
                                );
1513 11
                                $drawings = [];
1514 11 View Code Duplication
                                foreach ($relsWorksheet->Relationship as $ele) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1515 8
                                    if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing') {
1516 8
                                        $drawings[(string) $ele['Id']] = self::dirAdd("$dir/$fileWorksheet", $ele['Target']);
1517
                                    }
1518
                                }
1519 11
                                if ($xmlSheet->drawing && !$this->readDataOnly) {
1520 4
                                    foreach ($xmlSheet->drawing as $drawing) {
1521 4
                                        $fileDrawing = $drawings[(string) self::getArrayItem($drawing->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id')];
1522 4
                                        $relsDrawing = simplexml_load_string(
1523
                                            //~ http://schemas.openxmlformats.org/package/2006/relationships"
1524 4
                                            $this->securityScan(
1525 4
                                                $this->getFromZipArchive($zip, dirname($fileDrawing) . '/_rels/' . basename($fileDrawing) . '.rels')
1526
                                            ),
1527 4
                                            'SimpleXMLElement',
1528 4
                                            Settings::getLibXmlLoaderOptions()
1529
                                        );
1530 4
                                        $images = [];
1531
1532 4
                                        if ($relsDrawing && $relsDrawing->Relationship) {
1533 4
                                            foreach ($relsDrawing->Relationship as $ele) {
1534 4
                                                if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') {
1535 4
                                                    $images[(string) $ele['Id']] = self::dirAdd($fileDrawing, $ele['Target']);
1536 2
                                                } elseif ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart') {
1537 2
                                                    if ($this->includeCharts) {
1538 2
                                                        $charts[self::dirAdd($fileDrawing, $ele['Target'])] = [
1539 2
                                                            'id' => (string) $ele['Id'],
1540 4
                                                            'sheet' => $docSheet->getTitle(),
1541
                                                        ];
1542
                                                    }
1543
                                                }
1544
                                            }
1545
                                        }
1546 4
                                        $xmlDrawing = simplexml_load_string(
1547 4
                                            $this->securityScan($this->getFromZipArchive($zip, $fileDrawing)),
1548 4
                                            'SimpleXMLElement',
1549 4
                                            Settings::getLibXmlLoaderOptions()
1550 4
                                        )->children('http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing');
1551
1552 4
                                        if ($xmlDrawing->oneCellAnchor) {
1553 2
                                            foreach ($xmlDrawing->oneCellAnchor as $oneCellAnchor) {
1554 2
                                                if ($oneCellAnchor->pic->blipFill) {
1555
                                                    /** @var \SimpleXMLElement $blip */
1556 2
                                                    $blip = $oneCellAnchor->pic->blipFill->children('http://schemas.openxmlformats.org/drawingml/2006/main')->blip;
1557
                                                    /** @var \SimpleXMLElement $xfrm */
1558 2
                                                    $xfrm = $oneCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->xfrm;
1559
                                                    /** @var \SimpleXMLElement $outerShdw */
1560 2
                                                    $outerShdw = $oneCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->effectLst->outerShdw;
1561 2
                                                    $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
1562 2
                                                    $objDrawing->setName((string) self::getArrayItem($oneCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'name'));
1563 2
                                                    $objDrawing->setDescription((string) self::getArrayItem($oneCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'descr'));
1564 2
                                                    $objDrawing->setPath(
1565 2
                                                        'zip://' . File::realpath($pFilename) . '#' .
1566 2
                                                        $images[(string) self::getArrayItem(
1567 2
                                                            $blip->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'),
1568 2
                                                            'embed'
1569
                                                        )],
1570 2
                                                        false
1571
                                                    );
1572 2
                                                    $objDrawing->setCoordinates(Coordinate::stringFromColumnIndex(((string) $oneCellAnchor->from->col) + 1) . ($oneCellAnchor->from->row + 1));
1573 2
                                                    $objDrawing->setOffsetX(Drawing::EMUToPixels($oneCellAnchor->from->colOff));
1574 2
                                                    $objDrawing->setOffsetY(Drawing::EMUToPixels($oneCellAnchor->from->rowOff));
1575 2
                                                    $objDrawing->setResizeProportional(false);
1576 2
                                                    $objDrawing->setWidth(Drawing::EMUToPixels(self::getArrayItem($oneCellAnchor->ext->attributes(), 'cx')));
1577 2
                                                    $objDrawing->setHeight(Drawing::EMUToPixels(self::getArrayItem($oneCellAnchor->ext->attributes(), 'cy')));
1578 2
                                                    if ($xfrm) {
1579 2
                                                        $objDrawing->setRotation(Drawing::angleToDegrees(self::getArrayItem($xfrm->attributes(), 'rot')));
1580
                                                    }
1581 2 View Code Duplication
                                                    if ($outerShdw) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1582 2
                                                        $shadow = $objDrawing->getShadow();
1583 2
                                                        $shadow->setVisible(true);
1584 2
                                                        $shadow->setBlurRadius(Drawing::EMUTopixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
1585 2
                                                        $shadow->setDistance(Drawing::EMUTopixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
1586 2
                                                        $shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem($outerShdw->attributes(), 'dir')));
1587 2
                                                        $shadow->setAlignment((string) self::getArrayItem($outerShdw->attributes(), 'algn'));
1588 2
                                                        $shadow->getColor()->setRGB(self::getArrayItem($outerShdw->srgbClr->attributes(), 'val'));
1589 2
                                                        $shadow->setAlpha(self::getArrayItem($outerShdw->srgbClr->alpha->attributes(), 'val') / 1000);
1590
                                                    }
1591 2
                                                    $objDrawing->setWorksheet($docSheet);
1592
                                                } else {
1593
                                                    //    ? Can charts be positioned with a oneCellAnchor ?
1594
                                                    $coordinates = Coordinate::stringFromColumnIndex(((string) $oneCellAnchor->from->col) + 1) . ($oneCellAnchor->from->row + 1);
0 ignored issues
show
Unused Code introduced by
The assignment to $coordinates is dead and can be removed.
Loading history...
1595
                                                    $offsetX = Drawing::EMUToPixels($oneCellAnchor->from->colOff);
0 ignored issues
show
Unused Code introduced by
The assignment to $offsetX is dead and can be removed.
Loading history...
1596
                                                    $offsetY = Drawing::EMUToPixels($oneCellAnchor->from->rowOff);
0 ignored issues
show
Unused Code introduced by
The assignment to $offsetY is dead and can be removed.
Loading history...
1597
                                                    $width = Drawing::EMUToPixels(self::getArrayItem($oneCellAnchor->ext->attributes(), 'cx'));
0 ignored issues
show
Unused Code introduced by
The assignment to $width is dead and can be removed.
Loading history...
1598 2
                                                    $height = Drawing::EMUToPixels(self::getArrayItem($oneCellAnchor->ext->attributes(), 'cy'));
0 ignored issues
show
Unused Code introduced by
The assignment to $height is dead and can be removed.
Loading history...
1599
                                                }
1600
                                            }
1601
                                        }
1602 4
                                        if ($xmlDrawing->twoCellAnchor) {
1603 2
                                            foreach ($xmlDrawing->twoCellAnchor as $twoCellAnchor) {
1604 2
                                                if ($twoCellAnchor->pic->blipFill) {
1605 2
                                                    $blip = $twoCellAnchor->pic->blipFill->children('http://schemas.openxmlformats.org/drawingml/2006/main')->blip;
1606 2
                                                    $xfrm = $twoCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->xfrm;
1607 2
                                                    $outerShdw = $twoCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->effectLst->outerShdw;
1608 2
                                                    $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
1609 2
                                                    $objDrawing->setName((string) self::getArrayItem($twoCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'name'));
1610 2
                                                    $objDrawing->setDescription((string) self::getArrayItem($twoCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'descr'));
1611 2
                                                    $objDrawing->setPath(
1612 2
                                                        'zip://' . File::realpath($pFilename) . '#' .
1613 2
                                                        $images[(string) self::getArrayItem(
1614 2
                                                            $blip->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'),
1615 2
                                                            'embed'
1616
                                                        )],
1617 2
                                                        false
1618
                                                    );
1619 2
                                                    $objDrawing->setCoordinates(Coordinate::stringFromColumnIndex(((string) $twoCellAnchor->from->col) + 1) . ($twoCellAnchor->from->row + 1));
1620 2
                                                    $objDrawing->setOffsetX(Drawing::EMUToPixels($twoCellAnchor->from->colOff));
1621 2
                                                    $objDrawing->setOffsetY(Drawing::EMUToPixels($twoCellAnchor->from->rowOff));
1622 2
                                                    $objDrawing->setResizeProportional(false);
1623
1624 2
                                                    if ($xfrm) {
1625 2
                                                        $objDrawing->setWidth(Drawing::EMUToPixels(self::getArrayItem($xfrm->ext->attributes(), 'cx')));
1626 2
                                                        $objDrawing->setHeight(Drawing::EMUToPixels(self::getArrayItem($xfrm->ext->attributes(), 'cy')));
1627 2
                                                        $objDrawing->setRotation(Drawing::angleToDegrees(self::getArrayItem($xfrm->attributes(), 'rot')));
1628
                                                    }
1629 2 View Code Duplication
                                                    if ($outerShdw) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1630
                                                        $shadow = $objDrawing->getShadow();
1631
                                                        $shadow->setVisible(true);
1632
                                                        $shadow->setBlurRadius(Drawing::EMUTopixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
1633
                                                        $shadow->setDistance(Drawing::EMUTopixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
1634
                                                        $shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem($outerShdw->attributes(), 'dir')));
1635
                                                        $shadow->setAlignment((string) self::getArrayItem($outerShdw->attributes(), 'algn'));
0 ignored issues
show
Bug introduced by
(string)self::getArrayIt...->attributes(), 'algn') of type string is incompatible with the type integer expected by parameter $pValue of PhpOffice\PhpSpreadsheet...\Shadow::setAlignment(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1635
                                                        $shadow->setAlignment(/** @scrutinizer ignore-type */ (string) self::getArrayItem($outerShdw->attributes(), 'algn'));
Loading history...
1636
                                                        $shadow->getColor()->setRGB(self::getArrayItem($outerShdw->srgbClr->attributes(), 'val'));
1637
                                                        $shadow->setAlpha(self::getArrayItem($outerShdw->srgbClr->alpha->attributes(), 'val') / 1000);
1638
                                                    }
1639 2
                                                    $objDrawing->setWorksheet($docSheet);
1640 2
                                                } elseif (($this->includeCharts) && ($twoCellAnchor->graphicFrame)) {
1641 2
                                                    $fromCoordinate = Coordinate::stringFromColumnIndex(((string) $twoCellAnchor->from->col) + 1) . ($twoCellAnchor->from->row + 1);
1642 2
                                                    $fromOffsetX = Drawing::EMUToPixels($twoCellAnchor->from->colOff);
1643 2
                                                    $fromOffsetY = Drawing::EMUToPixels($twoCellAnchor->from->rowOff);
1644 2
                                                    $toCoordinate = Coordinate::stringFromColumnIndex(((string) $twoCellAnchor->to->col) + 1) . ($twoCellAnchor->to->row + 1);
1645 2
                                                    $toOffsetX = Drawing::EMUToPixels($twoCellAnchor->to->colOff);
1646 2
                                                    $toOffsetY = Drawing::EMUToPixels($twoCellAnchor->to->rowOff);
1647 2
                                                    $graphic = $twoCellAnchor->graphicFrame->children('http://schemas.openxmlformats.org/drawingml/2006/main')->graphic;
1648
                                                    /** @var \SimpleXMLElement $chartRef */
1649 2
                                                    $chartRef = $graphic->graphicData->children('http://schemas.openxmlformats.org/drawingml/2006/chart')->chart;
1650 2
                                                    $thisChart = (string) $chartRef->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
1651
1652 2
                                                    $chartDetails[$docSheet->getTitle() . '!' . $thisChart] = [
1653 2
                                                        'fromCoordinate' => $fromCoordinate,
1654 2
                                                        'fromOffsetX' => $fromOffsetX,
1655 2
                                                        'fromOffsetY' => $fromOffsetY,
1656 2
                                                        'toCoordinate' => $toCoordinate,
1657 2
                                                        'toOffsetX' => $toOffsetX,
1658 2
                                                        'toOffsetY' => $toOffsetY,
1659 4
                                                        'worksheetTitle' => $docSheet->getTitle(),
1660
                                                    ];
1661
                                                }
1662
                                            }
1663
                                        }
1664
                                    }
1665
                                }
1666
                            }
1667
1668
                            // Loop through definedNames
1669 12
                            if ($xmlWorkbook->definedNames) {
1670 5
                                foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
1671
                                    // Extract range
1672 1
                                    $extractedRange = (string) $definedName;
1673 1 View Code Duplication
                                    if (($spos = strpos($extractedRange, '!')) !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1674 1
                                        $extractedRange = substr($extractedRange, 0, $spos) . str_replace('$', '', substr($extractedRange, $spos));
1 ignored issue
show
Bug introduced by
Are you sure substr($extractedRange, 0, $spos) of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1674
                                        $extractedRange = /** @scrutinizer ignore-type */ substr($extractedRange, 0, $spos) . str_replace('$', '', substr($extractedRange, $spos));
Loading history...
1675
                                    } else {
1676
                                        $extractedRange = str_replace('$', '', $extractedRange);
1677
                                    }
1678
1679
                                    // Valid range?
1680 1
                                    if (stripos((string) $definedName, '#REF!') !== false || $extractedRange == '') {
1681
                                        continue;
1682
                                    }
1683
1684
                                    // Some definedNames are only applicable if we are on the same sheet...
1685 1
                                    if ((string) $definedName['localSheetId'] != '' && (string) $definedName['localSheetId'] == $sheetId) {
1686
                                        // Switch on type
1687 1
                                        switch ((string) $definedName['name']) {
1688 1
                                            case '_xlnm._FilterDatabase':
1689
                                                if ((string) $definedName['hidden'] !== '1') {
1690
                                                    $extractedRange = explode(',', $extractedRange);
1691
                                                    foreach ($extractedRange as $range) {
1692
                                                        $autoFilterRange = $range;
1693
                                                        if (strpos($autoFilterRange, ':') !== false) {
1694
                                                            $docSheet->getAutoFilter()->setRange($autoFilterRange);
1695
                                                        }
1696
                                                    }
1697
                                                }
1698
1699
                                                break;
1700 1
                                            case '_xlnm.Print_Titles':
1701
                                                // Split $extractedRange
1702 1
                                                $extractedRange = explode(',', $extractedRange);
1703
1704
                                                // Set print titles
1705 1
                                                foreach ($extractedRange as $range) {
1706 1
                                                    $matches = [];
1707 1
                                                    $range = str_replace('$', '', $range);
1708
1709
                                                    // check for repeating columns, e g. 'A:A' or 'A:D'
1710 1
                                                    if (preg_match('/!?([A-Z]+)\:([A-Z]+)$/', $range, $matches)) {
1711
                                                        $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$matches[1], $matches[2]]);
1712 1
                                                    } elseif (preg_match('/!?(\d+)\:(\d+)$/', $range, $matches)) {
1713
                                                        // check for repeating rows, e.g. '1:1' or '1:5'
1714 1
                                                        $docSheet->getPageSetup()->setRowsToRepeatAtTop([$matches[1], $matches[2]]);
1715
                                                    }
1716
                                                }
1717
1718 1
                                                break;
1719
                                            case '_xlnm.Print_Area':
1720
                                                $rangeSets = preg_split("/'(.*?)'(?:![A-Z0-9]+:[A-Z0-9]+,?)/", $extractedRange, PREG_SPLIT_NO_EMPTY);
1721
                                                $newRangeSets = [];
1722
                                                foreach ($rangeSets as $rangeSet) {
1723
                                                    $range = explode('!', $rangeSet); // FIXME: what if sheetname contains exclamation mark?
1724
                                                    $rangeSet = isset($range[1]) ? $range[1] : $range[0];
1725
                                                    if (strpos($rangeSet, ':') === false) {
1726
                                                        $rangeSet = $rangeSet . ':' . $rangeSet;
1727
                                                    }
1728
                                                    $newRangeSets[] = str_replace('$', '', $rangeSet);
1729
                                                }
1730
                                                $docSheet->getPageSetup()->setPrintArea(implode(',', $newRangeSets));
1731
1732
                                                break;
1733
                                            default:
1734 1
                                                break;
1735
                                        }
1736
                                    }
1737
                                }
1738
                            }
1739
1740
                            // Next sheet id
1741 12
                            ++$sheetId;
1742
                        }
1743
1744
                        // Loop through definedNames
1745 12
                        if ($xmlWorkbook->definedNames) {
1746 5
                            foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
1747
                                // Extract range
1748 1
                                $extractedRange = (string) $definedName;
1749 1 View Code Duplication
                                if (($spos = strpos($extractedRange, '!')) !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1750 1
                                    $extractedRange = substr($extractedRange, 0, $spos) . str_replace('$', '', substr($extractedRange, $spos));
1751
                                } else {
1752
                                    $extractedRange = str_replace('$', '', $extractedRange);
1753
                                }
1754
1755
                                // Valid range?
1756 1
                                if (stripos((string) $definedName, '#REF!') !== false || $extractedRange == '') {
1757
                                    continue;
1758
                                }
1759
1760
                                // Some definedNames are only applicable if we are on the same sheet...
1761 1
                                if ((string) $definedName['localSheetId'] != '') {
1762
                                    // Local defined name
1763
                                    // Switch on type
1764 1
                                    switch ((string) $definedName['name']) {
1765 1
                                        case '_xlnm._FilterDatabase':
1766 1
                                        case '_xlnm.Print_Titles':
1767
                                        case '_xlnm.Print_Area':
1768 1
                                            break;
1769
                                        default:
1770
                                            if ($mapSheetId[(int) $definedName['localSheetId']] !== null) {
1771
                                                $range = explode('!', (string) $definedName);
1772
                                                if (count($range) == 2) {
1773
                                                    $range[0] = str_replace("''", "'", $range[0]);
1774
                                                    $range[0] = str_replace("'", '', $range[0]);
1775
                                                    if ($worksheet = $docSheet->getParent()->getSheetByName($range[0])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $docSheet does not seem to be defined for all execution paths leading up to this point.
Loading history...
1776
                                                        $extractedRange = str_replace('$', '', $range[1]);
1777
                                                        $scope = $docSheet->getParent()->getSheet($mapSheetId[(int) $definedName['localSheetId']]);
1778
                                                        $excel->addNamedRange(new NamedRange((string) $definedName['name'], $worksheet, $extractedRange, true, $scope));
1779
                                                    }
1780
                                                }
1781
                                            }
1782
1783 1
                                            break;
1784
                                    }
1785
                                } elseif (!isset($definedName['localSheetId'])) {
1786
                                    // "Global" definedNames
1787
                                    $locatedSheet = null;
1788
                                    $extractedSheetName = '';
1789
                                    if (strpos((string) $definedName, '!') !== false) {
1790
                                        // Extract sheet name
1791
                                        $extractedSheetName = Worksheet::extractSheetTitle((string) $definedName, true);
1792
                                        $extractedSheetName = $extractedSheetName[0];
1793
1794
                                        // Locate sheet
1795
                                        $locatedSheet = $excel->getSheetByName($extractedSheetName);
1796
1797
                                        // Modify range
1798
                                        $range = explode('!', $extractedRange);
1799
                                        $extractedRange = isset($range[1]) ? $range[1] : $range[0];
1800
                                    }
1801
1802
                                    if ($locatedSheet !== null) {
1803 1
                                        $excel->addNamedRange(new NamedRange((string) $definedName['name'], $locatedSheet, $extractedRange, false));
1804
                                    }
1805
                                }
1806
                            }
1807
                        }
1808
                    }
1809
1810 12
                    if ((!$this->readDataOnly) || (!empty($this->loadSheetsOnly))) {
1811
                        // active sheet index
1812 12
                        $activeTab = (int) ($xmlWorkbook->bookViews->workbookView['activeTab']); // refers to old sheet index
1813
1814
                        // keep active sheet index if sheet is still loaded, else first sheet is set as the active
1815 12
                        if (isset($mapSheetId[$activeTab]) && $mapSheetId[$activeTab] !== null) {
1816 12
                            $excel->setActiveSheetIndex($mapSheetId[$activeTab]);
1817
                        } else {
1818
                            if ($excel->getSheetCount() == 0) {
1819
                                $excel->createSheet();
1820
                            }
1821
                            $excel->setActiveSheetIndex(0);
1822
                        }
1823
                    }
1824
1825 12
                    break;
1826
            }
1827
        }
1828
1829 12
        if (!$this->readDataOnly) {
1830 12
            $contentTypes = simplexml_load_string(
1831 12
                $this->securityScan(
1832 12
                    $this->getFromZipArchive($zip, '[Content_Types].xml')
1833
                ),
1834 12
                'SimpleXMLElement',
1835 12
                Settings::getLibXmlLoaderOptions()
1836
            );
1837 12
            foreach ($contentTypes->Override as $contentType) {
1838 12
                switch ($contentType['ContentType']) {
1839 12
                    case 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml':
1840 2
                        if ($this->includeCharts) {
1841 2
                            $chartEntryRef = ltrim($contentType['PartName'], '/');
1842 2
                            $chartElements = simplexml_load_string(
1843 2
                                $this->securityScan(
1844 2
                                    $this->getFromZipArchive($zip, $chartEntryRef)
1845
                                ),
1846 2
                                'SimpleXMLElement',
1847 2
                                Settings::getLibXmlLoaderOptions()
1848
                            );
1849 2
                            $objChart = Chart::readChart($chartElements, basename($chartEntryRef, '.xml'));
1850
1851 2
                            if (isset($charts[$chartEntryRef])) {
1852 2
                                $chartPositionRef = $charts[$chartEntryRef]['sheet'] . '!' . $charts[$chartEntryRef]['id'];
1853 2
                                if (isset($chartDetails[$chartPositionRef])) {
1854 2
                                    $excel->getSheetByName($charts[$chartEntryRef]['sheet'])->addChart($objChart);
1855 2
                                    $objChart->setWorksheet($excel->getSheetByName($charts[$chartEntryRef]['sheet']));
1856 2
                                    $objChart->setTopLeftPosition($chartDetails[$chartPositionRef]['fromCoordinate'], $chartDetails[$chartPositionRef]['fromOffsetX'], $chartDetails[$chartPositionRef]['fromOffsetY']);
1857 12
                                    $objChart->setBottomRightPosition($chartDetails[$chartPositionRef]['toCoordinate'], $chartDetails[$chartPositionRef]['toOffsetX'], $chartDetails[$chartPositionRef]['toOffsetY']);
1858
                                }
1859
                            }
1860
                        }
1861
                }
1862
            }
1863
        }
1864
1865 12
        $zip->close();
1866
1867 12
        return $excel;
1868
    }
1869
1870 12
    private static function readColor($color, $background = false)
1871
    {
1872 12
        if (isset($color['rgb'])) {
1873 8
            return (string) $color['rgb'];
1874 8
        } elseif (isset($color['indexed'])) {
1875 7
            return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
1876 5
        } elseif (isset($color['theme'])) {
1877 5
            if (self::$theme !== null) {
1878 5
                $returnColour = self::$theme->getColourByIndex((int) $color['theme']);
1879 5
                if (isset($color['tint'])) {
1880 1
                    $tintAdjust = (float) $color['tint'];
1881 1
                    $returnColour = Color::changeBrightness($returnColour, $tintAdjust);
1882
                }
1883
1884 5
                return 'FF' . $returnColour;
1885
            }
1886
        }
1887
1888 1
        if ($background) {
1889
            return 'FFFFFFFF';
1890
        }
1891
1892 1
        return 'FF000000';
1893
    }
1894
1895
    /**
1896
     * @param Style $docStyle
1897
     * @param \SimpleXMLElement|\stdClass $style
1898
     */
1899 12
    private static function readStyle(Style $docStyle, $style)
1900
    {
1901 12
        $docStyle->getNumberFormat()->setFormatCode($style->numFmt);
1902
1903
        // font
1904 12
        if (isset($style->font)) {
1905 12
            $docStyle->getFont()->setName((string) $style->font->name['val']);
1906 12
            $docStyle->getFont()->setSize((string) $style->font->sz['val']);
0 ignored issues
show
Bug introduced by
(string)$style->font->sz['val'] of type string is incompatible with the type double expected by parameter $pValue of PhpOffice\PhpSpreadsheet\Style\Font::setSize(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1906
            $docStyle->getFont()->setSize(/** @scrutinizer ignore-type */ (string) $style->font->sz['val']);
Loading history...
1907 12 View Code Duplication
            if (isset($style->font->b)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1908 9
                $docStyle->getFont()->setBold(!isset($style->font->b['val']) || self::boolean((string) $style->font->b['val']));
1909
            }
1910 12 View Code Duplication
            if (isset($style->font->i)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1911 6
                $docStyle->getFont()->setItalic(!isset($style->font->i['val']) || self::boolean((string) $style->font->i['val']));
1912
            }
1913 12 View Code Duplication
            if (isset($style->font->strike)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1914 5
                $docStyle->getFont()->setStrikethrough(!isset($style->font->strike['val']) || self::boolean((string) $style->font->strike['val']));
1915
            }
1916 12
            $docStyle->getFont()->getColor()->setARGB(self::readColor($style->font->color));
1917
1918 12 View Code Duplication
            if (isset($style->font->u) && !isset($style->font->u['val'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1919
                $docStyle->getFont()->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
1920 12
            } elseif (isset($style->font->u, $style->font->u['val'])) {
1921 5
                $docStyle->getFont()->setUnderline((string) $style->font->u['val']);
1922
            }
1923
1924 12 View Code Duplication
            if (isset($style->font->vertAlign, $style->font->vertAlign['val'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1925
                $vertAlign = strtolower((string) $style->font->vertAlign['val']);
1926
                if ($vertAlign == 'superscript') {
1927
                    $docStyle->getFont()->setSuperscript(true);
1928
                }
1929
                if ($vertAlign == 'subscript') {
1930
                    $docStyle->getFont()->setSubscript(true);
1931
                }
1932
            }
1933
        }
1934
1935
        // fill
1936 12
        if (isset($style->fill)) {
1937 12
            if ($style->fill->gradientFill) {
1938
                /** @var \SimpleXMLElement $gradientFill */
1939 2
                $gradientFill = $style->fill->gradientFill[0];
1940 2
                if (!empty($gradientFill['type'])) {
1941 2
                    $docStyle->getFill()->setFillType((string) $gradientFill['type']);
1942
                }
1943 2
                $docStyle->getFill()->setRotation((float) ($gradientFill['degree']));
1944 2
                $gradientFill->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
1945 2
                $docStyle->getFill()->getStartColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
1946 2
                $docStyle->getFill()->getEndColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
1947 12
            } elseif ($style->fill->patternFill) {
1948 12
                $patternType = (string) $style->fill->patternFill['patternType'] != '' ? (string) $style->fill->patternFill['patternType'] : 'solid';
1949 12
                $docStyle->getFill()->setFillType($patternType);
1950 12
                if ($style->fill->patternFill->fgColor) {
1951 4
                    $docStyle->getFill()->getStartColor()->setARGB(self::readColor($style->fill->patternFill->fgColor, true));
1952
                } else {
1953 12
                    $docStyle->getFill()->getStartColor()->setARGB('FF000000');
1954
                }
1955 12
                if ($style->fill->patternFill->bgColor) {
1956 4
                    $docStyle->getFill()->getEndColor()->setARGB(self::readColor($style->fill->patternFill->bgColor, true));
1957
                }
1958
            }
1959
        }
1960
1961
        // border
1962 12
        if (isset($style->border)) {
1963 12
            $diagonalUp = self::boolean((string) $style->border['diagonalUp']);
1964 12
            $diagonalDown = self::boolean((string) $style->border['diagonalDown']);
1965 12
            if (!$diagonalUp && !$diagonalDown) {
1966 12
                $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_NONE);
1967
            } elseif ($diagonalUp && !$diagonalDown) {
1968
                $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_UP);
1969
            } elseif (!$diagonalUp && $diagonalDown) {
1970
                $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_DOWN);
1971
            } else {
1972
                $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_BOTH);
1973
            }
1974 12
            self::readBorder($docStyle->getBorders()->getLeft(), $style->border->left);
1975 12
            self::readBorder($docStyle->getBorders()->getRight(), $style->border->right);
1976 12
            self::readBorder($docStyle->getBorders()->getTop(), $style->border->top);
1977 12
            self::readBorder($docStyle->getBorders()->getBottom(), $style->border->bottom);
1978 12
            self::readBorder($docStyle->getBorders()->getDiagonal(), $style->border->diagonal);
1979
        }
1980
1981
        // alignment
1982 12
        if (isset($style->alignment)) {
1983 12
            $docStyle->getAlignment()->setHorizontal((string) $style->alignment['horizontal']);
1984 12
            $docStyle->getAlignment()->setVertical((string) $style->alignment['vertical']);
1985
1986 12
            $textRotation = 0;
1987 12
            if ((int) $style->alignment['textRotation'] <= 90) {
1988 12
                $textRotation = (int) $style->alignment['textRotation'];
1989
            } elseif ((int) $style->alignment['textRotation'] > 90) {
1990
                $textRotation = 90 - (int) $style->alignment['textRotation'];
1991
            }
1992
1993 12
            $docStyle->getAlignment()->setTextRotation((int) $textRotation);
1994 12
            $docStyle->getAlignment()->setWrapText(self::boolean((string) $style->alignment['wrapText']));
1995 12
            $docStyle->getAlignment()->setShrinkToFit(self::boolean((string) $style->alignment['shrinkToFit']));
1996 12
            $docStyle->getAlignment()->setIndent((int) ((string) $style->alignment['indent']) > 0 ? (int) ((string) $style->alignment['indent']) : 0);
1997 12
            $docStyle->getAlignment()->setReadOrder((int) ((string) $style->alignment['readingOrder']) > 0 ? (int) ((string) $style->alignment['readingOrder']) : 0);
1998
        }
1999
2000
        // protection
2001 12
        if (isset($style->protection)) {
2002 12 View Code Duplication
            if (isset($style->protection['locked'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2003 2
                if (self::boolean((string) $style->protection['locked'])) {
2004
                    $docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
2005
                } else {
2006 2
                    $docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
2007
                }
2008
            }
2009
2010 12 View Code Duplication
            if (isset($style->protection['hidden'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2011
                if (self::boolean((string) $style->protection['hidden'])) {
2012
                    $docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
2013
                } else {
2014
                    $docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
2015
                }
2016
            }
2017
        }
2018
2019
        // top-level style settings
2020 12
        if (isset($style->quotePrefix)) {
2021 12
            $docStyle->setQuotePrefix($style->quotePrefix);
0 ignored issues
show
Bug introduced by
$style->quotePrefix of type SimpleXMLElement is incompatible with the type boolean expected by parameter $pValue of PhpOffice\PhpSpreadsheet...Style::setQuotePrefix(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2021
            $docStyle->setQuotePrefix(/** @scrutinizer ignore-type */ $style->quotePrefix);
Loading history...
2022
        }
2023 12
    }
2024
2025
    /**
2026
     * @param Border $docBorder
2027
     * @param \SimpleXMLElement $eleBorder
2028
     */
2029 12
    private static function readBorder(Border $docBorder, $eleBorder)
2030
    {
2031 12
        if (isset($eleBorder['style'])) {
2032 3
            $docBorder->setBorderStyle((string) $eleBorder['style']);
2033
        }
2034 12
        if (isset($eleBorder->color)) {
2035 3
            $docBorder->getColor()->setARGB(self::readColor($eleBorder->color));
2036
        }
2037 12
    }
2038
2039
    /**
2040
     * @param \SimpleXMLElement | null $is
2041
     *
2042
     * @return RichText
2043
     */
2044 3
    private function parseRichText($is)
2045
    {
2046 3
        $value = new RichText();
2047
2048 3
        if (isset($is->t)) {
2049
            $value->createText(StringHelper::controlCharacterOOXML2PHP((string) $is->t));
2050
        } else {
2051 3
            if (is_object($is->r)) {
2052 3
                foreach ($is->r as $run) {
2053 3
                    if (!isset($run->rPr)) {
2054 3
                        $objText = $value->createText(StringHelper::controlCharacterOOXML2PHP((string) $run->t));
0 ignored issues
show
Unused Code introduced by
The assignment to $objText is dead and can be removed.
Loading history...
2055
                    } else {
2056 3
                        $objText = $value->createTextRun(StringHelper::controlCharacterOOXML2PHP((string) $run->t));
2057
2058 3 View Code Duplication
                        if (isset($run->rPr->rFont['val'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2059 3
                            $objText->getFont()->setName((string) $run->rPr->rFont['val']);
2060
                        }
2061 3
                        if (isset($run->rPr->sz['val'])) {
2062 3
                            $objText->getFont()->setSize((string) $run->rPr->sz['val']);
0 ignored issues
show
Bug introduced by
(string)$run->rPr->sz['val'] of type string is incompatible with the type double expected by parameter $pValue of PhpOffice\PhpSpreadsheet\Style\Font::setSize(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2062
                            $objText->getFont()->setSize(/** @scrutinizer ignore-type */ (string) $run->rPr->sz['val']);
Loading history...
2063
                        }
2064 3
                        if (isset($run->rPr->color)) {
2065 3
                            $objText->getFont()->setColor(new Color(self::readColor($run->rPr->color)));
2066
                        }
2067 3 View Code Duplication
                        if ((isset($run->rPr->b['val']) && self::boolean((string) $run->rPr->b['val'])) ||
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2068 3
                            (isset($run->rPr->b) && !isset($run->rPr->b['val']))) {
2069 3
                            $objText->getFont()->setBold(true);
2070
                        }
2071 3 View Code Duplication
                        if ((isset($run->rPr->i['val']) && self::boolean((string) $run->rPr->i['val'])) ||
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2072 3
                            (isset($run->rPr->i) && !isset($run->rPr->i['val']))) {
2073 2
                            $objText->getFont()->setItalic(true);
2074
                        }
2075 3 View Code Duplication
                        if (isset($run->rPr->vertAlign, $run->rPr->vertAlign['val'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2076
                            $vertAlign = strtolower((string) $run->rPr->vertAlign['val']);
2077
                            if ($vertAlign == 'superscript') {
2078
                                $objText->getFont()->setSuperscript(true);
2079
                            }
2080
                            if ($vertAlign == 'subscript') {
2081
                                $objText->getFont()->setSubscript(true);
2082
                            }
2083
                        }
2084 3 View Code Duplication
                        if (isset($run->rPr->u) && !isset($run->rPr->u['val'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2085
                            $objText->getFont()->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
2086 3
                        } elseif (isset($run->rPr->u, $run->rPr->u['val'])) {
2087 2
                            $objText->getFont()->setUnderline((string) $run->rPr->u['val']);
2088
                        }
2089 3 View Code Duplication
                        if ((isset($run->rPr->strike['val']) && self::boolean((string) $run->rPr->strike['val'])) ||
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2090 3
                            (isset($run->rPr->strike) && !isset($run->rPr->strike['val']))) {
2091 3
                            $objText->getFont()->setStrikethrough(true);
2092
                        }
2093
                    }
2094
                }
2095
            }
2096
        }
2097
2098 3
        return $value;
2099
    }
2100
2101
    /**
2102
     * @param Spreadsheet $excel
2103
     * @param mixed $customUITarget
2104
     * @param mixed $zip
2105
     */
2106
    private function readRibbon(Spreadsheet $excel, $customUITarget, $zip)
2107
    {
2108
        $baseDir = dirname($customUITarget);
2109
        $nameCustomUI = basename($customUITarget);
2110
        // get the xml file (ribbon)
2111
        $localRibbon = $this->getFromZipArchive($zip, $customUITarget);
2112
        $customUIImagesNames = [];
2113
        $customUIImagesBinaries = [];
2114
        // something like customUI/_rels/customUI.xml.rels
2115
        $pathRels = $baseDir . '/_rels/' . $nameCustomUI . '.rels';
2116
        $dataRels = $this->getFromZipArchive($zip, $pathRels);
2117
        if ($dataRels) {
2118
            // exists and not empty if the ribbon have some pictures (other than internal MSO)
2119
            $UIRels = simplexml_load_string(
2120
                $this->securityScan($dataRels),
2121
                'SimpleXMLElement',
2122
                Settings::getLibXmlLoaderOptions()
2123
            );
2124
            if ($UIRels) {
2125
                // we need to save id and target to avoid parsing customUI.xml and "guess" if it's a pseudo callback who load the image
2126
                foreach ($UIRels->Relationship as $ele) {
2127
                    if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') {
2128
                        // an image ?
2129
                        $customUIImagesNames[(string) $ele['Id']] = (string) $ele['Target'];
2130
                        $customUIImagesBinaries[(string) $ele['Target']] = $this->getFromZipArchive($zip, $baseDir . '/' . (string) $ele['Target']);
2131
                    }
2132
                }
2133
            }
2134
        }
2135
        if ($localRibbon) {
2136
            $excel->setRibbonXMLData($customUITarget, $localRibbon);
2137
            if (count($customUIImagesNames) > 0 && count($customUIImagesBinaries) > 0) {
2138
                $excel->setRibbonBinObjects($customUIImagesNames, $customUIImagesBinaries);
2139
            } else {
2140
                $excel->setRibbonBinObjects(null);
0 ignored issues
show
Bug introduced by
The call to PhpOffice\PhpSpreadsheet...::setRibbonBinObjects() has too few arguments starting with BinObjectsData. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2140
                $excel->/** @scrutinizer ignore-call */ 
2141
                        setRibbonBinObjects(null);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2141
            }
2142
        } else {
2143
            $excel->setRibbonXMLData(null);
0 ignored issues
show
Bug introduced by
The call to PhpOffice\PhpSpreadsheet...eet::setRibbonXMLData() has too few arguments starting with xmlData. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2143
            $excel->/** @scrutinizer ignore-call */ 
2144
                    setRibbonXMLData(null);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2144
            $excel->setRibbonBinObjects(null);
2145
        }
2146
    }
2147
2148 13
    private static function getArrayItem($array, $key = 0)
2149
    {
2150 13
        return isset($array[$key]) ? $array[$key] : null;
2151
    }
2152
2153 4
    private static function dirAdd($base, $add)
2154
    {
2155 4
        return preg_replace('~[^/]+/\.\./~', '', dirname($base) . "/$add");
2156
    }
2157
2158
    private static function toCSSArray($style)
2159
    {
2160
        $style = str_replace(["\r", "\n"], '', $style);
2161
2162
        $temp = explode(';', $style);
2163
        $style = [];
2164
        foreach ($temp as $item) {
2165
            $item = explode(':', $item);
2166
2167
            if (strpos($item[1], 'px') !== false) {
2168
                $item[1] = str_replace('px', '', $item[1]);
2169
            }
2170 View Code Duplication
            if (strpos($item[1], 'pt') !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2171
                $item[1] = str_replace('pt', '', $item[1]);
2172
                $item[1] = Font::fontSizeToPixels($item[1]);
2173
            }
2174 View Code Duplication
            if (strpos($item[1], 'in') !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2175
                $item[1] = str_replace('in', '', $item[1]);
2176
                $item[1] = Font::inchSizeToPixels($item[1]);
2177
            }
2178 View Code Duplication
            if (strpos($item[1], 'cm') !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2179
                $item[1] = str_replace('cm', '', $item[1]);
2180
                $item[1] = Font::centimeterSizeToPixels($item[1]);
2181
            }
2182
2183
            $style[$item[0]] = $item[1];
2184
        }
2185
2186
        return $style;
2187
    }
2188
2189 12
    private static function boolean($value)
2190
    {
2191 12
        if (is_object($value)) {
2192 1
            $value = (string) $value;
2193
        }
2194 12
        if (is_numeric($value)) {
2195 9
            return (bool) $value;
2196
        }
2197
2198 12
        return $value === 'true' || $value === 'TRUE';
2199
    }
2200
}
2201