Failed Conditions
Push — develop ( eb5856...11b055 )
by Adrien
31:22
created

Xlsx::listWorksheetNames()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 32
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 16
nc 5
nop 1
dl 0
loc 32
ccs 17
cts 17
cp 1
crap 5
rs 8.439
c 0
b 0
f 0
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 17
    public function __construct()
51
    {
52 17
        $this->readFilter = new DefaultReadFilter();
53 17
        $this->referenceHelper = ReferenceHelper::getInstance();
54 17
    }
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 16
    private function getFromZipArchive(ZipArchive $archive, $fileName = '')
300
    {
301
        // Root-relative paths
302 16
        if (strpos($fileName, '//') !== false) {
303
            $fileName = substr($fileName, strpos($fileName, '//') + 1);
304
        }
305 16
        $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 16
        $contents = $archive->getFromName($fileName, 0, ZipArchive::FL_NOCASE);
312 16
        if ($contents === false) {
313
            $contents = $archive->getFromName(substr($fileName, 1), 0, ZipArchive::FL_NOCASE);
314
        }
315
316 16
        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 13
    public function load($pFilename)
329
    {
330 13
        File::assertFile($pFilename);
331
332
        // Initialisations
333 13
        $excel = new Spreadsheet();
334 13
        $excel->removeSheetByIndex(0);
335 13
        if (!$this->readDataOnly) {
336 13
            $excel->removeCellStyleXfByIndex(0); // remove the default style
337 13
            $excel->removeCellXfByIndex(0); // remove the default style
338
        }
339
340 13
        $zip = new ZipArchive();
341 13
        $zip->open($pFilename);
342
343
        //    Read the theme first, because we need the colour scheme when reading the styles
344 13
        $wbRels = simplexml_load_string(
345
            //~ http://schemas.openxmlformats.org/package/2006/relationships"
346 13
            $this->securityScan($this->getFromZipArchive($zip, 'xl/_rels/workbook.xml.rels')),
347 13
            'SimpleXMLElement',
348 13
            Settings::getLibXmlLoaderOptions()
349
        );
350 13
        foreach ($wbRels->Relationship as $rel) {
351 13
            switch ($rel['Type']) {
352 13
                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme':
353 13
                    $themeOrderArray = ['lt1', 'dk1', 'lt2', 'dk2'];
354 13
                    $themeOrderAdditional = count($themeOrderArray);
355
356 13
                    $xmlTheme = simplexml_load_string(
357 13
                        $this->securityScan($this->getFromZipArchive($zip, "xl/{$rel['Target']}")),
358 13
                        'SimpleXMLElement',
359 13
                        Settings::getLibXmlLoaderOptions()
360
                    );
361 13
                    if (is_object($xmlTheme)) {
362 13
                        $xmlThemeName = $xmlTheme->attributes();
363 13
                        $xmlTheme = $xmlTheme->children('http://schemas.openxmlformats.org/drawingml/2006/main');
364 13
                        $themeName = (string) $xmlThemeName['name'];
365
366 13
                        $colourScheme = $xmlTheme->themeElements->clrScheme->attributes();
367 13
                        $colourSchemeName = (string) $colourScheme['name'];
368 13
                        $colourScheme = $xmlTheme->themeElements->clrScheme->children('http://schemas.openxmlformats.org/drawingml/2006/main');
369
370 13
                        $themeColours = [];
371 13
                        foreach ($colourScheme as $k => $xmlColour) {
372 13
                            $themePos = array_search($k, $themeOrderArray);
373 13
                            if ($themePos === false) {
374 13
                                $themePos = $themeOrderAdditional++;
375
                            }
376 13
                            if (isset($xmlColour->sysClr)) {
377 13
                                $xmlColourData = $xmlColour->sysClr->attributes();
378 13
                                $themeColours[$themePos] = $xmlColourData['lastClr'];
379 13
                            } elseif (isset($xmlColour->srgbClr)) {
380 13
                                $xmlColourData = $xmlColour->srgbClr->attributes();
381 13
                                $themeColours[$themePos] = $xmlColourData['val'];
382
                            }
383
                        }
384 13
                        self::$theme = new Xlsx\Theme($themeName, $colourSchemeName, $themeColours);
385
                    }
386
387 13
                    break;
388
            }
389
        }
390
391 13
        $rels = simplexml_load_string(
392
            //~ http://schemas.openxmlformats.org/package/2006/relationships"
393 13
            $this->securityScan($this->getFromZipArchive($zip, '_rels/.rels')),
394 13
            'SimpleXMLElement',
395 13
            Settings::getLibXmlLoaderOptions()
396
        );
397 13
        foreach ($rels->Relationship as $rel) {
398 13
            switch ($rel['Type']) {
399 13
                case 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties':
400 12
                    $xmlCore = simplexml_load_string(
401 12
                        $this->securityScan($this->getFromZipArchive($zip, "{$rel['Target']}")),
402 12
                        'SimpleXMLElement',
403 12
                        Settings::getLibXmlLoaderOptions()
404
                    );
405 12
                    if (is_object($xmlCore)) {
406 12
                        $xmlCore->registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/');
407 12
                        $xmlCore->registerXPathNamespace('dcterms', 'http://purl.org/dc/terms/');
408 12
                        $xmlCore->registerXPathNamespace('cp', 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties');
409 12
                        $docProps = $excel->getProperties();
410 12
                        $docProps->setCreator((string) self::getArrayItem($xmlCore->xpath('dc:creator')));
411 12
                        $docProps->setLastModifiedBy((string) self::getArrayItem($xmlCore->xpath('cp:lastModifiedBy')));
412 12
                        $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 12
                        $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 12
                        $docProps->setTitle((string) self::getArrayItem($xmlCore->xpath('dc:title')));
415 12
                        $docProps->setDescription((string) self::getArrayItem($xmlCore->xpath('dc:description')));
416 12
                        $docProps->setSubject((string) self::getArrayItem($xmlCore->xpath('dc:subject')));
417 12
                        $docProps->setKeywords((string) self::getArrayItem($xmlCore->xpath('cp:keywords')));
418 12
                        $docProps->setCategory((string) self::getArrayItem($xmlCore->xpath('cp:category')));
419
                    }
420
421 12
                    break;
422 13
                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties':
423 12
                    $xmlCore = simplexml_load_string(
424 12
                        $this->securityScan($this->getFromZipArchive($zip, "{$rel['Target']}")),
425 12
                        'SimpleXMLElement',
426 12
                        Settings::getLibXmlLoaderOptions()
427
                    );
428 12
                    if (is_object($xmlCore)) {
429 12
                        $docProps = $excel->getProperties();
430 12
                        if (isset($xmlCore->Company)) {
431 10
                            $docProps->setCompany((string) $xmlCore->Company);
432
                        }
433 12
                        if (isset($xmlCore->Manager)) {
434 8
                            $docProps->setManager((string) $xmlCore->Manager);
435
                        }
436
                    }
437
438 12
                    break;
439 13
                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 13
                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 13
                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
472 13
                    $dir = dirname($rel['Target']);
473 13
                    $relsWorkbook = simplexml_load_string(
474
                        //~ http://schemas.openxmlformats.org/package/2006/relationships"
475 13
                        $this->securityScan($this->getFromZipArchive($zip, "$dir/_rels/" . basename($rel['Target']) . '.rels')),
476 13
                        'SimpleXMLElement',
477 13
                        Settings::getLibXmlLoaderOptions()
478
                    );
479 13
                    $relsWorkbook->registerXPathNamespace('rel', 'http://schemas.openxmlformats.org/package/2006/relationships');
480
481 13
                    $sharedStrings = [];
482 13
                    $xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings']"));
483 13
                    $xmlStrings = simplexml_load_string(
484
                        //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
485 13
                        $this->securityScan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
486 13
                        'SimpleXMLElement',
487 13
                        Settings::getLibXmlLoaderOptions()
488
                    );
489 13
                    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 13
                    $worksheets = [];
500 13
                    $macros = $customUI = null;
501 13
                    foreach ($relsWorkbook->Relationship as $ele) {
502 13
                        switch ($ele['Type']) {
503 13
                            case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet':
504 13
                                $worksheets[(string) $ele['Id']] = $ele['Target'];
505
506 13
                                break;
507
                            // a vbaProject ? (: some macros)
508 13
                            case 'http://schemas.microsoft.com/office/2006/relationships/vbaProject':
509
                                $macros = $ele['Target'];
510
511 13
                                break;
512
                        }
513
                    }
514
515 13
                    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 13
                    $styles = [];
528 13
                    $cellStyles = [];
529 13
                    $xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles']"));
530 13
                    $xmlStyles = simplexml_load_string(
531
                        //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
532 13
                        $this->securityScan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
533 13
                        'SimpleXMLElement',
534 13
                        Settings::getLibXmlLoaderOptions()
535
                    );
536 13
                    $numFmts = null;
537 13
                    if ($xmlStyles && $xmlStyles->numFmts[0]) {
538 7
                        $numFmts = $xmlStyles->numFmts[0];
539
                    }
540 13
                    if (isset($numFmts) && ($numFmts !== null)) {
541 7
                        $numFmts->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
542
                    }
543 13
                    if (!$this->readDataOnly && $xmlStyles) {
544 13
                        foreach ($xmlStyles->cellXfs->xf as $xf) {
545 13
                            $numFmt = NumberFormat::FORMAT_GENERAL;
546
547 13
                            if ($xf['numFmtId']) {
548 13
                                if (isset($numFmts)) {
549 7
                                    $tmpNumFmt = self::getArrayItem($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
550
551 7
                                    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 13
                                if ((int) $xf['numFmtId'] < 164 &&
560 13
                                    NumberFormat::builtInFormatCode((int) $xf['numFmtId']) !== '') {
561 13
                                    $numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']);
562
                                }
563
                            }
564 13
                            $quotePrefix = false;
565 13
                            if (isset($xf['quotePrefix'])) {
566
                                $quotePrefix = (bool) $xf['quotePrefix'];
567
                            }
568
569
                            $style = (object) [
570 13
                                'numFmt' => $numFmt,
571 13
                                'font' => $xmlStyles->fonts->font[(int) ($xf['fontId'])],
572 13
                                'fill' => $xmlStyles->fills->fill[(int) ($xf['fillId'])],
573 13
                                'border' => $xmlStyles->borders->border[(int) ($xf['borderId'])],
574 13
                                'alignment' => $xf->alignment,
575 13
                                'protection' => $xf->protection,
576 13
                                'quotePrefix' => $quotePrefix,
577
                            ];
578 13
                            $styles[] = $style;
579
580
                            // add style to cellXf collection
581 13
                            $objStyle = new Style();
582 13
                            self::readStyle($objStyle, $style);
583 13
                            $excel->addCellXf($objStyle);
584
                        }
585
586 13
                        foreach ($xmlStyles->cellStyleXfs->xf as $xf) {
587 13
                            $numFmt = NumberFormat::FORMAT_GENERAL;
588 13
                            if ($numFmts && $xf['numFmtId']) {
589 7
                                $tmpNumFmt = self::getArrayItem($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
590 7
                                if (isset($tmpNumFmt['formatCode'])) {
591 1
                                    $numFmt = (string) $tmpNumFmt['formatCode'];
592 7
                                } elseif ((int) $xf['numFmtId'] < 165) {
593 7
                                    $numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']);
594
                                }
595
                            }
596
597
                            $cellStyle = (object) [
598 13
                                'numFmt' => $numFmt,
599 13
                                'font' => $xmlStyles->fonts->font[(int) ($xf['fontId'])],
600 13
                                'fill' => $xmlStyles->fills->fill[(int) ($xf['fillId'])],
601 13
                                'border' => $xmlStyles->borders->border[(int) ($xf['borderId'])],
602 13
                                'alignment' => $xf->alignment,
603 13
                                'protection' => $xf->protection,
604 13
                                '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 13
                            $cellStyles[] = $cellStyle;
607
608
                            // add style to cellStyleXf collection
609 13
                            $objStyle = new Style();
610 13
                            self::readStyle($objStyle, $cellStyle);
611 13
                            $excel->addCellStyleXf($objStyle);
612
                        }
613
                    }
614
615 13
                    $dxfs = [];
616 13
                    if (!$this->readDataOnly && $xmlStyles) {
617
                        //    Conditional Styles
618 13
                        if ($xmlStyles->dxfs) {
619 13
                            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 13
                        if ($xmlStyles->cellStyles) {
627 13
                            foreach ($xmlStyles->cellStyles->cellStyle as $cellStyle) {
628 13
                                if ((int) ($cellStyle['builtinId']) == 0) {
629 13
                                    if (isset($cellStyles[(int) ($cellStyle['xfId'])])) {
630
                                        // Set default style
631 13
                                        $style = new Style();
632 13
                                        self::readStyle($style, $cellStyles[(int) ($cellStyle['xfId'])]);
633
634
                                        // normal style, currently not using it for anything
635
                                    }
636
                                }
637
                            }
638
                        }
639
                    }
640
641 13
                    $xmlWorkbook = simplexml_load_string(
642
                        //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
643 13
                        $this->securityScan($this->getFromZipArchive($zip, "{$rel['Target']}")),
644 13
                        'SimpleXMLElement',
645 13
                        Settings::getLibXmlLoaderOptions()
646
                    );
647
648
                    // Set base date
649 13
                    if ($xmlWorkbook->workbookPr) {
650 12
                        Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
651 12
                        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 13
                    $sheetId = 0; // keep track of new sheet id in final workbook
659 13
                    $oldSheetId = -1; // keep track of old sheet id in final workbook
660 13
                    $countSkippedSheets = 0; // keep track of number of skipped sheets
661 13
                    $mapSheetId = []; // mapping of sheet ids from old to new
662
663 13
                    $charts = $chartDetails = [];
664
665 13
                    if ($xmlWorkbook->sheets) {
666
                        /** @var \SimpleXMLElement $eleSheet */
667 13
                        foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
668 13
                            ++$oldSheetId;
669
670
                            // Check if sheet should be skipped
671 13
                            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 13
                            $mapSheetId[$oldSheetId] = $oldSheetId - $countSkippedSheets;
681
682
                            // Load sheet
683 13
                            $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 13
                            $docSheet->setTitle((string) $eleSheet['name'], false, false);
689 13
                            $fileWorksheet = $worksheets[(string) self::getArrayItem($eleSheet->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id')];
690 13
                            $xmlSheet = simplexml_load_string(
691
                                //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
692 13
                                $this->securityScan($this->getFromZipArchive($zip, "$dir/$fileWorksheet")),
693 13
                                'SimpleXMLElement',
694 13
                                Settings::getLibXmlLoaderOptions()
695
                            );
696
697 13
                            $sharedFormulas = [];
698
699 13
                            if (isset($eleSheet['state']) && (string) $eleSheet['state'] != '') {
700
                                $docSheet->setSheetState((string) $eleSheet['state']);
701
                            }
702
703 13
                            if (isset($xmlSheet->sheetViews, $xmlSheet->sheetViews->sheetView)) {
704 13 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 13 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 13 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 13 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 6
                                    $docSheet->setShowGridLines(self::boolean((string) $xmlSheet->sheetViews->sheetView['showGridLines']));
715
                                }
716 13 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 6
                                    $docSheet->setShowRowColHeaders(self::boolean((string) $xmlSheet->sheetViews->sheetView['showRowColHeaders']));
718
                                }
719 13 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 13
                                if (isset($xmlSheet->sheetViews->sheetView->pane)) {
723 2
                                    $xSplit = 0;
724 2
                                    $ySplit = 0;
725 2
                                    $topLeftCell = null;
726
727 2
                                    if (isset($xmlSheet->sheetViews->sheetView->pane['xSplit'])) {
728 1
                                        $xSplit = (int) ($xmlSheet->sheetViews->sheetView->pane['xSplit']);
729
                                    }
730
731 2
                                    if (isset($xmlSheet->sheetViews->sheetView->pane['ySplit'])) {
732 2
                                        $ySplit = (int) ($xmlSheet->sheetViews->sheetView->pane['ySplit']);
733
                                    }
734
735 2
                                    if (isset($xmlSheet->sheetViews->sheetView->pane['topLeftCell'])) {
736 2
                                        $topLeftCell = (string) $xmlSheet->sheetViews->sheetView->pane['topLeftCell'];
737
                                    }
738
739 2
                                    $docSheet->freezePane(Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1), $topLeftCell);
740
                                }
741
742 13
                                if (isset($xmlSheet->sheetViews->sheetView->selection)) {
743 11
                                    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 13
                            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 13
                            if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr['codeName'])) {
758
                                $docSheet->setCodeName((string) $xmlSheet->sheetPr['codeName'], false);
759
                            }
760 13
                            if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr->outlinePr)) {
761 6 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 6
                                    !self::boolean((string) $xmlSheet->sheetPr->outlinePr['summaryRight'])) {
763
                                    $docSheet->setShowSummaryRight(false);
764
                                } else {
765 6
                                    $docSheet->setShowSummaryRight(true);
766
                                }
767
768 6 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 6
                                    !self::boolean((string) $xmlSheet->sheetPr->outlinePr['summaryBelow'])) {
770
                                    $docSheet->setShowSummaryBelow(false);
771
                                } else {
772 6
                                    $docSheet->setShowSummaryBelow(true);
773
                                }
774
                            }
775
776 13
                            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 13
                            if (isset($xmlSheet->sheetFormatPr)) {
786 13
                                if (isset($xmlSheet->sheetFormatPr['customHeight']) &&
787 13
                                    self::boolean((string) $xmlSheet->sheetFormatPr['customHeight']) &&
788 13
                                    isset($xmlSheet->sheetFormatPr['defaultRowHeight'])) {
789 1
                                    $docSheet->getDefaultRowDimension()->setRowHeight((float) $xmlSheet->sheetFormatPr['defaultRowHeight']);
790
                                }
791 13
                                if (isset($xmlSheet->sheetFormatPr['defaultColWidth'])) {
792 1
                                    $docSheet->getDefaultColumnDimension()->setWidth((float) $xmlSheet->sheetFormatPr['defaultColWidth']);
793
                                }
794 13
                                if (isset($xmlSheet->sheetFormatPr['zeroHeight']) &&
795 13
                                    ((string) $xmlSheet->sheetFormatPr['zeroHeight'] == '1')) {
796
                                    $docSheet->getDefaultRowDimension()->setZeroHeight(true);
797
                                }
798
                            }
799
800 13
                            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 13
                            if (isset($xmlSheet->printOptions) && !$this->readDataOnly) {
825 6
                                if (self::boolean((string) $xmlSheet->printOptions['gridLinesSet'])) {
826 6
                                    $docSheet->setShowGridlines(true);
827
                                }
828 6
                                if (self::boolean((string) $xmlSheet->printOptions['gridLines'])) {
829
                                    $docSheet->setPrintGridlines(true);
830
                                }
831 6
                                if (self::boolean((string) $xmlSheet->printOptions['horizontalCentered'])) {
832
                                    $docSheet->getPageSetup()->setHorizontalCentered(true);
833
                                }
834 6
                                if (self::boolean((string) $xmlSheet->printOptions['verticalCentered'])) {
835
                                    $docSheet->getPageSetup()->setVerticalCentered(true);
836
                                }
837
                            }
838
839 13
                            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 13
                            $conditionals = [];
972 13
                            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 13
                            $aKeys = ['sheet', 'objects', 'scenarios', 'formatCells', 'formatColumns', 'formatRows', 'insertColumns', 'insertRows', 'insertHyperlinks', 'deleteColumns', 'deleteRows', 'selectLockedCells', 'sort', 'autoFilter', 'pivotTables', 'selectUnlockedCells'];
1013 13
                            if (!$this->readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) {
1014 8
                                foreach ($aKeys as $key) {
1015 8
                                    $method = 'set' . ucfirst($key);
1016 8
                                    $docSheet->getProtection()->$method(self::boolean((string) $xmlSheet->sheetProtection[$key]));
1017
                                }
1018
                            }
1019
1020 13
                            if (!$this->readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) {
1021 8
                                $docSheet->getProtection()->setPassword((string) $xmlSheet->sheetProtection['password'], true);
1022 8
                                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 13
                            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 13
                            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 13
                            if ($xmlSheet && $xmlSheet->pageMargins && !$this->readDataOnly) {
1139 12
                                $docPageMargins = $docSheet->getPageMargins();
1140 12
                                $docPageMargins->setLeft((float) ($xmlSheet->pageMargins['left']));
1141 12
                                $docPageMargins->setRight((float) ($xmlSheet->pageMargins['right']));
1142 12
                                $docPageMargins->setTop((float) ($xmlSheet->pageMargins['top']));
1143 12
                                $docPageMargins->setBottom((float) ($xmlSheet->pageMargins['bottom']));
1144 12
                                $docPageMargins->setHeader((float) ($xmlSheet->pageMargins['header']));
1145 12
                                $docPageMargins->setFooter((float) ($xmlSheet->pageMargins['footer']));
1146
                            }
1147
1148 13
                            if ($xmlSheet && $xmlSheet->pageSetup && !$this->readDataOnly) {
1149 12
                                $docPageSetup = $docSheet->getPageSetup();
1150
1151 12
                                if (isset($xmlSheet->pageSetup['orientation'])) {
1152 12
                                    $docPageSetup->setOrientation((string) $xmlSheet->pageSetup['orientation']);
1153
                                }
1154 12
                                if (isset($xmlSheet->pageSetup['paperSize'])) {
1155 10
                                    $docPageSetup->setPaperSize((int) ($xmlSheet->pageSetup['paperSize']));
1156
                                }
1157 12
                                if (isset($xmlSheet->pageSetup['scale'])) {
1158 6
                                    $docPageSetup->setScale((int) ($xmlSheet->pageSetup['scale']), false);
1159
                                }
1160 12 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 6
                                    $docPageSetup->setFitToHeight((int) ($xmlSheet->pageSetup['fitToHeight']), false);
1162
                                }
1163 12 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 6
                                    $docPageSetup->setFitToWidth((int) ($xmlSheet->pageSetup['fitToWidth']), false);
1165
                                }
1166 12
                                if (isset($xmlSheet->pageSetup['firstPageNumber'], $xmlSheet->pageSetup['useFirstPageNumber']) &&
1167 12
                                    self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])) {
1168
                                    $docPageSetup->setFirstPageNumber((int) ($xmlSheet->pageSetup['firstPageNumber']));
1169
                                }
1170
                            }
1171
1172 13
                            if ($xmlSheet && $xmlSheet->headerFooter && !$this->readDataOnly) {
1173 8
                                $docHeaderFooter = $docSheet->getHeaderFooter();
1174
1175 8 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 8
                                    self::boolean((string) $xmlSheet->headerFooter['differentOddEven'])) {
1177
                                    $docHeaderFooter->setDifferentOddEven(true);
1178
                                } else {
1179 8
                                    $docHeaderFooter->setDifferentOddEven(false);
1180
                                }
1181 8 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 8
                                    self::boolean((string) $xmlSheet->headerFooter['differentFirst'])) {
1183
                                    $docHeaderFooter->setDifferentFirst(true);
1184
                                } else {
1185 8
                                    $docHeaderFooter->setDifferentFirst(false);
1186
                                }
1187 8 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 8
                                    !self::boolean((string) $xmlSheet->headerFooter['scaleWithDoc'])) {
1189
                                    $docHeaderFooter->setScaleWithDocument(false);
1190
                                } else {
1191 8
                                    $docHeaderFooter->setScaleWithDocument(true);
1192
                                }
1193 8 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 8
                                    !self::boolean((string) $xmlSheet->headerFooter['alignWithMargins'])) {
1195 3
                                    $docHeaderFooter->setAlignWithMargins(false);
1196
                                } else {
1197 5
                                    $docHeaderFooter->setAlignWithMargins(true);
1198
                                }
1199
1200 8
                                $docHeaderFooter->setOddHeader((string) $xmlSheet->headerFooter->oddHeader);
1201 8
                                $docHeaderFooter->setOddFooter((string) $xmlSheet->headerFooter->oddFooter);
1202 8
                                $docHeaderFooter->setEvenHeader((string) $xmlSheet->headerFooter->evenHeader);
1203 8
                                $docHeaderFooter->setEvenFooter((string) $xmlSheet->headerFooter->evenFooter);
1204 8
                                $docHeaderFooter->setFirstHeader((string) $xmlSheet->headerFooter->firstHeader);
1205 8
                                $docHeaderFooter->setFirstFooter((string) $xmlSheet->headerFooter->firstFooter);
1206
                            }
1207
1208 13
                            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 13
                            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 13
                            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 13
                            $hyperlinks = [];
1255 13
                            if (!$this->readDataOnly) {
1256
                                // Locate hyperlink relations
1257 13
                                if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
1258 12
                                    $relsWorksheet = simplexml_load_string(
1259
                                        //~ http://schemas.openxmlformats.org/package/2006/relationships"
1260 12
                                        $this->securityScan(
1261 12
                                            $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
1262
                                        ),
1263 12
                                        'SimpleXMLElement',
1264 12
                                        Settings::getLibXmlLoaderOptions()
1265
                                    );
1266 12
                                    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 13
                                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 13
                            $comments = [];
1303 13
                            $vmlComments = [];
1304 13
                            if (!$this->readDataOnly) {
1305
                                // Locate comment relations
1306 13
                                if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
1307 12
                                    $relsWorksheet = simplexml_load_string(
1308
                                        //~ http://schemas.openxmlformats.org/package/2006/relationships"
1309 12
                                        $this->securityScan(
1310 12
                                            $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
1311
                                        ),
1312 12
                                        'SimpleXMLElement',
1313 12
                                        Settings::getLibXmlLoaderOptions()
1314
                                    );
1315 12
                                    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 13
                                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 13
                                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 13
                                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 13
                            if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
1505 12
                                $relsWorksheet = simplexml_load_string(
1506
                                    //~ http://schemas.openxmlformats.org/package/2006/relationships"
1507 12
                                    $this->securityScan(
1508 12
                                        $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
1509
                                    ),
1510 12
                                    'SimpleXMLElement',
1511 12
                                    Settings::getLibXmlLoaderOptions()
1512
                                );
1513 12
                                $drawings = [];
1514 12 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 12
                                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 13
                            if ($xmlWorkbook->definedNames) {
1670 6
                                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 13
                            ++$sheetId;
1742
                        }
1743
1744
                        // Loop through definedNames
1745 13
                        if ($xmlWorkbook->definedNames) {
1746 6
                            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 13
                    if ((!$this->readDataOnly) || (!empty($this->loadSheetsOnly))) {
1811
                        // active sheet index
1812 13
                        $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 13
                        if (isset($mapSheetId[$activeTab]) && $mapSheetId[$activeTab] !== null) {
1816 13
                            $excel->setActiveSheetIndex($mapSheetId[$activeTab]);
1817
                        } else {
1818
                            if ($excel->getSheetCount() == 0) {
1819
                                $excel->createSheet();
1820
                            }
1821
                            $excel->setActiveSheetIndex(0);
1822
                        }
1823
                    }
1824
1825 13
                    break;
1826
            }
1827
        }
1828
1829 13
        if (!$this->readDataOnly) {
1830 13
            $contentTypes = simplexml_load_string(
1831 13
                $this->securityScan(
1832 13
                    $this->getFromZipArchive($zip, '[Content_Types].xml')
1833
                ),
1834 13
                'SimpleXMLElement',
1835 13
                Settings::getLibXmlLoaderOptions()
1836
            );
1837 13
            foreach ($contentTypes->Override as $contentType) {
1838 13
                switch ($contentType['ContentType']) {
1839 13
                    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 13
                                    $objChart->setBottomRightPosition($chartDetails[$chartPositionRef]['toCoordinate'], $chartDetails[$chartPositionRef]['toOffsetX'], $chartDetails[$chartPositionRef]['toOffsetY']);
1858
                                }
1859
                            }
1860
                        }
1861
                }
1862
            }
1863
        }
1864
1865 13
        $zip->close();
1866
1867 13
        return $excel;
1868
    }
1869
1870 13
    private static function readColor($color, $background = false)
1871
    {
1872 13
        if (isset($color['rgb'])) {
1873 9
            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 13
    private static function readStyle(Style $docStyle, $style)
1900
    {
1901 13
        $docStyle->getNumberFormat()->setFormatCode($style->numFmt);
1902
1903
        // font
1904 13
        if (isset($style->font)) {
1905 13
            $docStyle->getFont()->setName((string) $style->font->name['val']);
1906 13
            $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 13 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 10
                $docStyle->getFont()->setBold(!isset($style->font->b['val']) || self::boolean((string) $style->font->b['val']));
1909
            }
1910 13 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 7
                $docStyle->getFont()->setItalic(!isset($style->font->i['val']) || self::boolean((string) $style->font->i['val']));
1912
            }
1913 13 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 6
                $docStyle->getFont()->setStrikethrough(!isset($style->font->strike['val']) || self::boolean((string) $style->font->strike['val']));
1915
            }
1916 13
            $docStyle->getFont()->getColor()->setARGB(self::readColor($style->font->color));
1917
1918 13 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 13
            } elseif (isset($style->font->u, $style->font->u['val'])) {
1921 6
                $docStyle->getFont()->setUnderline((string) $style->font->u['val']);
1922
            }
1923
1924 13 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 13
        if (isset($style->fill)) {
1937 13
            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 13
            } elseif ($style->fill->patternFill) {
1948 13
                $patternType = (string) $style->fill->patternFill['patternType'] != '' ? (string) $style->fill->patternFill['patternType'] : 'solid';
1949 13
                $docStyle->getFill()->setFillType($patternType);
1950 13
                if ($style->fill->patternFill->fgColor) {
1951 4
                    $docStyle->getFill()->getStartColor()->setARGB(self::readColor($style->fill->patternFill->fgColor, true));
1952
                } else {
1953 13
                    $docStyle->getFill()->getStartColor()->setARGB('FF000000');
1954
                }
1955 13
                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 13
        if (isset($style->border)) {
1963 13
            $diagonalUp = self::boolean((string) $style->border['diagonalUp']);
1964 13
            $diagonalDown = self::boolean((string) $style->border['diagonalDown']);
1965 13
            if (!$diagonalUp && !$diagonalDown) {
1966 13
                $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 13
            self::readBorder($docStyle->getBorders()->getLeft(), $style->border->left);
1975 13
            self::readBorder($docStyle->getBorders()->getRight(), $style->border->right);
1976 13
            self::readBorder($docStyle->getBorders()->getTop(), $style->border->top);
1977 13
            self::readBorder($docStyle->getBorders()->getBottom(), $style->border->bottom);
1978 13
            self::readBorder($docStyle->getBorders()->getDiagonal(), $style->border->diagonal);
1979
        }
1980
1981
        // alignment
1982 13
        if (isset($style->alignment)) {
1983 13
            $docStyle->getAlignment()->setHorizontal((string) $style->alignment['horizontal']);
1984 13
            $docStyle->getAlignment()->setVertical((string) $style->alignment['vertical']);
1985
1986 13
            $textRotation = 0;
1987 13
            if ((int) $style->alignment['textRotation'] <= 90) {
1988 13
                $textRotation = (int) $style->alignment['textRotation'];
1989
            } elseif ((int) $style->alignment['textRotation'] > 90) {
1990
                $textRotation = 90 - (int) $style->alignment['textRotation'];
1991
            }
1992
1993 13
            $docStyle->getAlignment()->setTextRotation((int) $textRotation);
1994 13
            $docStyle->getAlignment()->setWrapText(self::boolean((string) $style->alignment['wrapText']));
1995 13
            $docStyle->getAlignment()->setShrinkToFit(self::boolean((string) $style->alignment['shrinkToFit']));
1996 13
            $docStyle->getAlignment()->setIndent((int) ((string) $style->alignment['indent']) > 0 ? (int) ((string) $style->alignment['indent']) : 0);
1997 13
            $docStyle->getAlignment()->setReadOrder((int) ((string) $style->alignment['readingOrder']) > 0 ? (int) ((string) $style->alignment['readingOrder']) : 0);
1998
        }
1999
2000
        // protection
2001 13
        if (isset($style->protection)) {
2002 13 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 13 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 13
        if (isset($style->quotePrefix)) {
2021 13
            $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 13
    }
2024
2025
    /**
2026
     * @param Border $docBorder
2027
     * @param \SimpleXMLElement $eleBorder
2028
     */
2029 13
    private static function readBorder(Border $docBorder, $eleBorder)
2030
    {
2031 13
        if (isset($eleBorder['style'])) {
2032 3
            $docBorder->setBorderStyle((string) $eleBorder['style']);
2033
        }
2034 13
        if (isset($eleBorder->color)) {
2035 3
            $docBorder->getColor()->setARGB(self::readColor($eleBorder->color));
2036
        }
2037 13
    }
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 14
    private static function getArrayItem($array, $key = 0)
2149
    {
2150 14
        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 13
    private static function boolean($value)
2190
    {
2191 13
        if (is_object($value)) {
2192 1
            $value = (string) $value;
2193
        }
2194 13
        if (is_numeric($value)) {
2195 10
            return (bool) $value;
2196
        }
2197
2198 13
        return $value === 'true' || $value === 'TRUE';
2199
    }
2200
}
2201