Completed
Push — master ( e5409f...6a2e0c )
by Adrien
269:11 queued 261:49
created

Xlsx::parseRichText()   D

Complexity

Conditions 26
Paths 3

Size

Total Lines 55
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 34.0756

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 26
eloc 36
c 2
b 0
f 0
nc 3
nop 1
dl 0
loc 55
ccs 27
cts 35
cp 0.7714
crap 34.0756
rs 4.1666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader;
4
5
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
6
use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
7
use PhpOffice\PhpSpreadsheet\NamedRange;
8
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
9
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\AutoFilter;
10
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Chart;
11
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\ColumnAndRowAttributes;
12
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\ConditionalStyles;
13
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\DataValidations;
14
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Hyperlinks;
15
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\PageSetup;
16
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Properties as PropertyReader;
17
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViewOptions;
18
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViews;
19
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Styles;
20
use PhpOffice\PhpSpreadsheet\ReferenceHelper;
21
use PhpOffice\PhpSpreadsheet\RichText\RichText;
22
use PhpOffice\PhpSpreadsheet\Settings;
23
use PhpOffice\PhpSpreadsheet\Shared\Date;
24
use PhpOffice\PhpSpreadsheet\Shared\Drawing;
25
use PhpOffice\PhpSpreadsheet\Shared\File;
26
use PhpOffice\PhpSpreadsheet\Shared\Font;
27
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
28
use PhpOffice\PhpSpreadsheet\Spreadsheet;
29
use PhpOffice\PhpSpreadsheet\Style\Border;
30
use PhpOffice\PhpSpreadsheet\Style\Borders;
31
use PhpOffice\PhpSpreadsheet\Style\Color;
32
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
33
use PhpOffice\PhpSpreadsheet\Style\Protection;
34
use PhpOffice\PhpSpreadsheet\Style\Style;
35
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
36
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
37
use SimpleXMLElement;
38
use XMLReader;
39
use ZipArchive;
40
41
class Xlsx extends BaseReader
42
{
43
    /**
44
     * ReferenceHelper instance.
45
     *
46
     * @var ReferenceHelper
47
     */
48
    private $referenceHelper;
49
50
    /**
51
     * Xlsx\Theme instance.
52
     *
53
     * @var Xlsx\Theme
54
     */
55
    private static $theme = null;
56
57
    /**
58
     * Create a new Xlsx Reader instance.
59
     */
60 55
    public function __construct()
61
    {
62 55
        parent::__construct();
63 55
        $this->referenceHelper = ReferenceHelper::getInstance();
64 55
        $this->securityScanner = XmlScanner::getInstance($this);
65 55
    }
66
67
    /**
68
     * Can the current IReader read the file?
69
     *
70
     * @param string $pFilename
71
     *
72
     * @throws Exception
73
     *
74
     * @return bool
75
     */
76 7
    public function canRead($pFilename)
77
    {
78 7
        File::assertFile($pFilename);
79
80 7
        $result = false;
81 7
        $zip = new ZipArchive();
82
83 7
        if ($zip->open($pFilename) === true) {
84 7
            $workbookBasename = $this->getWorkbookBaseName($zip);
85 7
            $result = !empty($workbookBasename);
86
87 7
            $zip->close();
88
        }
89
90 7
        return $result;
91
    }
92
93
    /**
94
     * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
95
     *
96
     * @param string $pFilename
97
     *
98
     * @throws Exception
99
     *
100
     * @return array
101
     */
102 1
    public function listWorksheetNames($pFilename)
103
    {
104 1
        File::assertFile($pFilename);
105
106 1
        $worksheetNames = [];
107
108 1
        $zip = new ZipArchive();
109 1
        $zip->open($pFilename);
110
111
        //    The files we're looking at here are small enough that simpleXML is more efficient than XMLReader
112
        //~ http://schemas.openxmlformats.org/package/2006/relationships");
113 1
        $rels = simplexml_load_string(
114 1
            $this->securityScanner->scan($this->getFromZipArchive($zip, '_rels/.rels'))
115
        );
116 1
        foreach ($rels->Relationship as $rel) {
117 1
            switch ($rel['Type']) {
118 1
                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
119
                    //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
120 1
                    $xmlWorkbook = simplexml_load_string(
121 1
                        $this->securityScanner->scan($this->getFromZipArchive($zip, "{$rel['Target']}"))
122
                    );
123
124 1
                    if ($xmlWorkbook->sheets) {
125 1
                        foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
126
                            // Check if sheet should be skipped
127 1
                            $worksheetNames[] = (string) $eleSheet['name'];
128
                        }
129
                    }
130
            }
131
        }
132
133 1
        $zip->close();
134
135 1
        return $worksheetNames;
136
    }
137
138
    /**
139
     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
140
     *
141
     * @param string $pFilename
142
     *
143
     * @throws Exception
144
     *
145
     * @return array
146
     */
147 1
    public function listWorksheetInfo($pFilename)
148
    {
149 1
        File::assertFile($pFilename);
150
151 1
        $worksheetInfo = [];
152
153 1
        $zip = new ZipArchive();
154 1
        $zip->open($pFilename);
155
156
        //~ http://schemas.openxmlformats.org/package/2006/relationships"
157 1
        $rels = simplexml_load_string(
158 1
            $this->securityScanner->scan($this->getFromZipArchive($zip, '_rels/.rels')),
159 1
            'SimpleXMLElement',
160 1
            Settings::getLibXmlLoaderOptions()
161
        );
162 1
        foreach ($rels->Relationship as $rel) {
163 1
            if ($rel['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument') {
164 1
                $dir = dirname($rel['Target']);
165
166
                //~ http://schemas.openxmlformats.org/package/2006/relationships"
167 1
                $relsWorkbook = simplexml_load_string(
168 1
                    $this->securityScanner->scan(
169 1
                        $this->getFromZipArchive($zip, "$dir/_rels/" . basename($rel['Target']) . '.rels')
170
                    ),
171 1
                    'SimpleXMLElement',
172 1
                    Settings::getLibXmlLoaderOptions()
173
                );
174 1
                $relsWorkbook->registerXPathNamespace('rel', 'http://schemas.openxmlformats.org/package/2006/relationships');
175
176 1
                $worksheets = [];
177 1
                foreach ($relsWorkbook->Relationship as $ele) {
178 1
                    if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet') {
179 1
                        $worksheets[(string) $ele['Id']] = $ele['Target'];
180
                    }
181
                }
182
183
                //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
184 1
                $xmlWorkbook = simplexml_load_string(
185 1
                    $this->securityScanner->scan(
186 1
                        $this->getFromZipArchive($zip, "{$rel['Target']}")
187
                    ),
188 1
                    'SimpleXMLElement',
189 1
                    Settings::getLibXmlLoaderOptions()
190
                );
191 1
                if ($xmlWorkbook->sheets) {
192 1
                    $dir = dirname($rel['Target']);
193
                    /** @var SimpleXMLElement $eleSheet */
194 1
                    foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
195
                        $tmpInfo = [
196 1
                            'worksheetName' => (string) $eleSheet['name'],
197 1
                            'lastColumnLetter' => 'A',
198 1
                            'lastColumnIndex' => 0,
199 1
                            'totalRows' => 0,
200 1
                            'totalColumns' => 0,
201
                        ];
202
203 1
                        $fileWorksheet = $worksheets[(string) self::getArrayItem($eleSheet->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id')];
204
205 1
                        $xml = new XMLReader();
206 1
                        $xml->xml(
207 1
                            $this->securityScanner->scanFile(
208 1
                                'zip://' . File::realpath($pFilename) . '#' . "$dir/$fileWorksheet"
209
                            ),
210 1
                            null,
211 1
                            Settings::getLibXmlLoaderOptions()
212
                        );
213 1
                        $xml->setParserProperty(2, true);
214
215 1
                        $currCells = 0;
216 1
                        while ($xml->read()) {
217 1
                            if ($xml->name == 'row' && $xml->nodeType == XMLReader::ELEMENT) {
218 1
                                $row = $xml->getAttribute('r');
219 1
                                $tmpInfo['totalRows'] = $row;
220 1
                                $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
221 1
                                $currCells = 0;
222 1
                            } elseif ($xml->name == 'c' && $xml->nodeType == XMLReader::ELEMENT) {
223 1
                                ++$currCells;
224
                            }
225
                        }
226 1
                        $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
227 1
                        $xml->close();
228
229 1
                        $tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1;
230 1
                        $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
231
232 1
                        $worksheetInfo[] = $tmpInfo;
233
                    }
234
                }
235
            }
236
        }
237
238 1
        $zip->close();
239
240 1
        return $worksheetInfo;
241
    }
242
243 5
    private static function castToBoolean($c)
244
    {
245 5
        $value = isset($c->v) ? (string) $c->v : null;
246 5
        if ($value == '0') {
247
            return false;
248 5
        } elseif ($value == '1') {
249 5
            return true;
250
        }
251
252
        return (bool) $c->v;
253
    }
254
255
    private static function castToError($c)
256
    {
257
        return isset($c->v) ? (string) $c->v : null;
258
    }
259
260 27
    private static function castToString($c)
261
    {
262 27
        return isset($c->v) ? (string) $c->v : null;
263
    }
264
265 8
    private function castToFormula($c, $r, &$cellDataType, &$value, &$calculatedValue, &$sharedFormulas, $castBaseType)
266
    {
267 8
        $cellDataType = 'f';
268 8
        $value = "={$c->f}";
269 8
        $calculatedValue = self::$castBaseType($c);
270
271
        // Shared formula?
272 8
        if (isset($c->f['t']) && strtolower((string) $c->f['t']) == 'shared') {
273 2
            $instance = (string) $c->f['si'];
274
275 2
            if (!isset($sharedFormulas[(string) $c->f['si']])) {
276 2
                $sharedFormulas[$instance] = ['master' => $r, 'formula' => $value];
277
            } else {
278 2
                $master = Coordinate::coordinateFromString($sharedFormulas[$instance]['master']);
279 2
                $current = Coordinate::coordinateFromString($r);
280
281 2
                $difference = [0, 0];
282 2
                $difference[0] = Coordinate::columnIndexFromString($current[0]) - Coordinate::columnIndexFromString($master[0]);
283 2
                $difference[1] = $current[1] - $master[1];
284
285 2
                $value = $this->referenceHelper->updateFormulaReferences($sharedFormulas[$instance]['formula'], 'A1', $difference[0], $difference[1]);
286
            }
287
        }
288 8
    }
289
290
    /**
291
     * @param ZipArchive $archive
292
     * @param string $fileName
293
     *
294
     * @return string
295
     */
296 51
    private function getFromZipArchive(ZipArchive $archive, $fileName = '')
297
    {
298
        // Root-relative paths
299 51
        if (strpos($fileName, '//') !== false) {
300
            $fileName = substr($fileName, strpos($fileName, '//') + 1);
301
        }
302 51
        $fileName = File::realpath($fileName);
303
304
        // Sadly, some 3rd party xlsx generators don't use consistent case for filenaming
305
        //    so we need to load case-insensitively from the zip file
306
307
        // Apache POI fixes
308 51
        $contents = $archive->getFromName($fileName, 0, ZipArchive::FL_NOCASE);
309 51
        if ($contents === false) {
310 3
            $contents = $archive->getFromName(substr($fileName, 1), 0, ZipArchive::FL_NOCASE);
311
        }
312
313 51
        return $contents;
314
    }
315
316
    /**
317
     * Loads Spreadsheet from file.
318
     *
319
     * @param string $pFilename
320
     *
321
     * @throws Exception
322
     *
323
     * @return Spreadsheet
324
     */
325 48
    public function load($pFilename)
326
    {
327 48
        File::assertFile($pFilename);
328
329
        // Initialisations
330 48
        $excel = new Spreadsheet();
331 48
        $excel->removeSheetByIndex(0);
332 48
        if (!$this->readDataOnly) {
333 48
            $excel->removeCellStyleXfByIndex(0); // remove the default style
334 48
            $excel->removeCellXfByIndex(0); // remove the default style
335
        }
336 48
        $unparsedLoadedData = [];
337
338 48
        $zip = new ZipArchive();
339 48
        $zip->open($pFilename);
340
341
        //    Read the theme first, because we need the colour scheme when reading the styles
342
        //~ http://schemas.openxmlformats.org/package/2006/relationships"
343 48
        $workbookBasename = $this->getWorkbookBaseName($zip);
344 48
        $wbRels = simplexml_load_string(
345 48
            $this->securityScanner->scan($this->getFromZipArchive($zip, "xl/_rels/${workbookBasename}.rels")),
346 48
            'SimpleXMLElement',
347 48
            Settings::getLibXmlLoaderOptions()
348
        );
349 48
        foreach ($wbRels->Relationship as $rel) {
350 48
            switch ($rel['Type']) {
351 48
                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme':
352 48
                    $themeOrderArray = ['lt1', 'dk1', 'lt2', 'dk2'];
353 48
                    $themeOrderAdditional = count($themeOrderArray);
354
355 48
                    $xmlTheme = simplexml_load_string(
356 48
                        $this->securityScanner->scan($this->getFromZipArchive($zip, "xl/{$rel['Target']}")),
357 48
                        'SimpleXMLElement',
358 48
                        Settings::getLibXmlLoaderOptions()
359
                    );
360 48
                    if (is_object($xmlTheme)) {
361 48
                        $xmlThemeName = $xmlTheme->attributes();
362 48
                        $xmlTheme = $xmlTheme->children('http://schemas.openxmlformats.org/drawingml/2006/main');
363 48
                        $themeName = (string) $xmlThemeName['name'];
364
365 48
                        $colourScheme = $xmlTheme->themeElements->clrScheme->attributes();
366 48
                        $colourSchemeName = (string) $colourScheme['name'];
367 48
                        $colourScheme = $xmlTheme->themeElements->clrScheme->children('http://schemas.openxmlformats.org/drawingml/2006/main');
368
369 48
                        $themeColours = [];
370 48
                        foreach ($colourScheme as $k => $xmlColour) {
371 48
                            $themePos = array_search($k, $themeOrderArray);
372 48
                            if ($themePos === false) {
373 48
                                $themePos = $themeOrderAdditional++;
374
                            }
375 48
                            if (isset($xmlColour->sysClr)) {
376 48
                                $xmlColourData = $xmlColour->sysClr->attributes();
377 48
                                $themeColours[$themePos] = $xmlColourData['lastClr'];
378 48
                            } elseif (isset($xmlColour->srgbClr)) {
379 48
                                $xmlColourData = $xmlColour->srgbClr->attributes();
380 48
                                $themeColours[$themePos] = $xmlColourData['val'];
381
                            }
382
                        }
383 48
                        self::$theme = new Xlsx\Theme($themeName, $colourSchemeName, $themeColours);
384
                    }
385
386 48
                    break;
387
            }
388
        }
389
390
        //~ http://schemas.openxmlformats.org/package/2006/relationships"
391 48
        $rels = simplexml_load_string(
392 48
            $this->securityScanner->scan($this->getFromZipArchive($zip, '_rels/.rels')),
393 48
            'SimpleXMLElement',
394 48
            Settings::getLibXmlLoaderOptions()
395
        );
396
397 48
        $propertyReader = new PropertyReader($this->securityScanner, $excel->getProperties());
398 48
        foreach ($rels->Relationship as $rel) {
399 48
            switch ($rel['Type']) {
400 48
                case 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties':
401 47
                    $propertyReader->readCoreProperties($this->getFromZipArchive($zip, "{$rel['Target']}"));
402
403 47
                    break;
404 48
                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties':
405 47
                    $propertyReader->readExtendedProperties($this->getFromZipArchive($zip, "{$rel['Target']}"));
406
407 47
                    break;
408 48
                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties':
409 4
                    $propertyReader->readCustomProperties($this->getFromZipArchive($zip, "{$rel['Target']}"));
410
411 4
                    break;
412
                //Ribbon
413 48
                case 'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility':
414
                    $customUI = $rel['Target'];
415
                    if ($customUI !== null) {
416
                        $this->readRibbon($excel, $customUI, $zip);
417
                    }
418
419
                    break;
420 48
                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
421 48
                    $dir = dirname($rel['Target']);
422
                    //~ http://schemas.openxmlformats.org/package/2006/relationships"
423 48
                    $relsWorkbook = simplexml_load_string(
424 48
                        $this->securityScanner->scan($this->getFromZipArchive($zip, "$dir/_rels/" . basename($rel['Target']) . '.rels')),
425 48
                        'SimpleXMLElement',
426 48
                        Settings::getLibXmlLoaderOptions()
427
                    );
428 48
                    $relsWorkbook->registerXPathNamespace('rel', 'http://schemas.openxmlformats.org/package/2006/relationships');
429
430 48
                    $sharedStrings = [];
431 48
                    $xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings']"));
432
                    //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
433 48
                    $xmlStrings = simplexml_load_string(
434 48
                        $this->securityScanner->scan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
435 48
                        'SimpleXMLElement',
436 48
                        Settings::getLibXmlLoaderOptions()
437
                    );
438 48
                    if (isset($xmlStrings, $xmlStrings->si)) {
439 27
                        foreach ($xmlStrings->si as $val) {
440 27
                            if (isset($val->t)) {
441 27
                                $sharedStrings[] = StringHelper::controlCharacterOOXML2PHP((string) $val->t);
442 4
                            } elseif (isset($val->r)) {
443 4
                                $sharedStrings[] = $this->parseRichText($val);
444
                            }
445
                        }
446
                    }
447
448 48
                    $worksheets = [];
449 48
                    $macros = $customUI = null;
450 48
                    foreach ($relsWorkbook->Relationship as $ele) {
451 48
                        switch ($ele['Type']) {
452 48
                            case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet':
453 48
                                $worksheets[(string) $ele['Id']] = $ele['Target'];
454
455 48
                                break;
456
                            // a vbaProject ? (: some macros)
457 48
                            case 'http://schemas.microsoft.com/office/2006/relationships/vbaProject':
458 1
                                $macros = $ele['Target'];
459
460 1
                                break;
461
                        }
462
                    }
463
464 48
                    if ($macros !== null) {
465 1
                        $macrosCode = $this->getFromZipArchive($zip, 'xl/vbaProject.bin'); //vbaProject.bin always in 'xl' dir and always named vbaProject.bin
466 1
                        if ($macrosCode !== false) {
467 1
                            $excel->setMacrosCode($macrosCode);
468 1
                            $excel->setHasMacros(true);
469
                            //short-circuit : not reading vbaProject.bin.rel to get Signature =>allways vbaProjectSignature.bin in 'xl' dir
470 1
                            $Certificate = $this->getFromZipArchive($zip, 'xl/vbaProjectSignature.bin');
471 1
                            if ($Certificate !== false) {
472
                                $excel->setMacrosCertificate($Certificate);
473
                            }
474
                        }
475
                    }
476
477 48
                    $xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles']"));
478
                    //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
479 48
                    $xmlStyles = simplexml_load_string(
480 48
                        $this->securityScanner->scan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
481 48
                        'SimpleXMLElement',
482 48
                        Settings::getLibXmlLoaderOptions()
483
                    );
484
485 48
                    $styles = [];
486 48
                    $cellStyles = [];
487 48
                    $numFmts = null;
488 48
                    if ($xmlStyles && $xmlStyles->numFmts[0]) {
489 35
                        $numFmts = $xmlStyles->numFmts[0];
490
                    }
491 48
                    if (isset($numFmts) && ($numFmts !== null)) {
492 35
                        $numFmts->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
493
                    }
494 48
                    if (!$this->readDataOnly && $xmlStyles) {
495 48
                        foreach ($xmlStyles->cellXfs->xf as $xf) {
496 48
                            $numFmt = NumberFormat::FORMAT_GENERAL;
497
498 48
                            if ($xf['numFmtId']) {
499 48
                                if (isset($numFmts)) {
500 35
                                    $tmpNumFmt = self::getArrayItem($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
501
502 35
                                    if (isset($tmpNumFmt['formatCode'])) {
503 4
                                        $numFmt = (string) $tmpNumFmt['formatCode'];
504
                                    }
505
                                }
506
507
                                // We shouldn't override any of the built-in MS Excel values (values below id 164)
508
                                //  But there's a lot of naughty homebrew xlsx writers that do use "reserved" id values that aren't actually used
509
                                //  So we make allowance for them rather than lose formatting masks
510 48
                                if ((int) $xf['numFmtId'] < 164 &&
511 48
                                    NumberFormat::builtInFormatCode((int) $xf['numFmtId']) !== '') {
512 48
                                    $numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']);
513
                                }
514
                            }
515 48
                            $quotePrefix = false;
516 48
                            if (isset($xf['quotePrefix'])) {
517
                                $quotePrefix = (bool) $xf['quotePrefix'];
518
                            }
519
520
                            $style = (object) [
521 48
                                'numFmt' => $numFmt,
522 48
                                'font' => $xmlStyles->fonts->font[(int) ($xf['fontId'])],
523 48
                                'fill' => $xmlStyles->fills->fill[(int) ($xf['fillId'])],
524 48
                                'border' => $xmlStyles->borders->border[(int) ($xf['borderId'])],
525 48
                                'alignment' => $xf->alignment,
526 48
                                'protection' => $xf->protection,
527 48
                                'quotePrefix' => $quotePrefix,
528
                            ];
529 48
                            $styles[] = $style;
530
531
                            // add style to cellXf collection
532 48
                            $objStyle = new Style();
533 48
                            self::readStyle($objStyle, $style);
534 48
                            $excel->addCellXf($objStyle);
535
                        }
536
537 48
                        foreach (isset($xmlStyles->cellStyleXfs->xf) ? $xmlStyles->cellStyleXfs->xf : [] as $xf) {
538 48
                            $numFmt = NumberFormat::FORMAT_GENERAL;
539 48
                            if ($numFmts && $xf['numFmtId']) {
540 35
                                $tmpNumFmt = self::getArrayItem($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
541 35
                                if (isset($tmpNumFmt['formatCode'])) {
542 1
                                    $numFmt = (string) $tmpNumFmt['formatCode'];
543 35
                                } elseif ((int) $xf['numFmtId'] < 165) {
544 35
                                    $numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']);
545
                                }
546
                            }
547
548
                            $cellStyle = (object) [
549 48
                                'numFmt' => $numFmt,
550 48
                                'font' => $xmlStyles->fonts->font[(int) ($xf['fontId'])],
551 48
                                'fill' => $xmlStyles->fills->fill[(int) ($xf['fillId'])],
552 48
                                'border' => $xmlStyles->borders->border[(int) ($xf['borderId'])],
553 48
                                'alignment' => $xf->alignment,
554 48
                                'protection' => $xf->protection,
555 48
                                '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...
556
                            ];
557 48
                            $cellStyles[] = $cellStyle;
558
559
                            // add style to cellStyleXf collection
560 48
                            $objStyle = new Style();
561 48
                            self::readStyle($objStyle, $cellStyle);
562 48
                            $excel->addCellStyleXf($objStyle);
563
                        }
564
                    }
565
566 48
                    $styleReader = new Styles($xmlStyles);
1 ignored issue
show
Bug introduced by
It seems like $xmlStyles can also be of type false; however, parameter $styleXml of PhpOffice\PhpSpreadsheet...x\Styles::__construct() does only seem to accept SimpleXMLElement, 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

566
                    $styleReader = new Styles(/** @scrutinizer ignore-type */ $xmlStyles);
Loading history...
567 48
                    $styleReader->setStyleBaseData(self::$theme, $styles, $cellStyles);
568 48
                    $dxfs = $styleReader->dxfs($this->readDataOnly);
569 48
                    $styles = $styleReader->styles();
570
571
                    //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
572 48
                    $xmlWorkbook = simplexml_load_string(
573 48
                        $this->securityScanner->scan($this->getFromZipArchive($zip, "{$rel['Target']}")),
574 48
                        'SimpleXMLElement',
575 48
                        Settings::getLibXmlLoaderOptions()
576
                    );
577
578
                    // Set base date
579 48
                    if ($xmlWorkbook->workbookPr) {
580 47
                        Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
581 47
                        if (isset($xmlWorkbook->workbookPr['date1904'])) {
582
                            if (self::boolean((string) $xmlWorkbook->workbookPr['date1904'])) {
583
                                Date::setExcelCalendar(Date::CALENDAR_MAC_1904);
584
                            }
585
                        }
586
                    }
587
588
                    // Set protection
589 48
                    $this->readProtection($excel, $xmlWorkbook);
1 ignored issue
show
Bug introduced by
It seems like $xmlWorkbook can also be of type false; however, parameter $xmlWorkbook of PhpOffice\PhpSpreadsheet...\Xlsx::readProtection() does only seem to accept SimpleXMLElement, 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

589
                    $this->readProtection($excel, /** @scrutinizer ignore-type */ $xmlWorkbook);
Loading history...
590
591 48
                    $sheetId = 0; // keep track of new sheet id in final workbook
592 48
                    $oldSheetId = -1; // keep track of old sheet id in final workbook
593 48
                    $countSkippedSheets = 0; // keep track of number of skipped sheets
594 48
                    $mapSheetId = []; // mapping of sheet ids from old to new
595
596 48
                    $charts = $chartDetails = [];
597
598 48
                    if ($xmlWorkbook->sheets) {
599
                        /** @var SimpleXMLElement $eleSheet */
600 48
                        foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
601 48
                            ++$oldSheetId;
602
603
                            // Check if sheet should be skipped
604 48
                            if (isset($this->loadSheetsOnly) && !in_array((string) $eleSheet['name'], $this->loadSheetsOnly)) {
605 1
                                ++$countSkippedSheets;
606 1
                                $mapSheetId[$oldSheetId] = null;
607
608 1
                                continue;
609
                            }
610
611
                            // Map old sheet id in original workbook to new sheet id.
612
                            // They will differ if loadSheetsOnly() is being used
613 48
                            $mapSheetId[$oldSheetId] = $oldSheetId - $countSkippedSheets;
614
615
                            // Load sheet
616 48
                            $docSheet = $excel->createSheet();
617
                            //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet
618
                            //        references in formula cells... during the load, all formulae should be correct,
619
                            //        and we're simply bringing the worksheet name in line with the formula, not the
620
                            //        reverse
621 48
                            $docSheet->setTitle((string) $eleSheet['name'], false, false);
622 48
                            $fileWorksheet = $worksheets[(string) self::getArrayItem($eleSheet->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id')];
623
                            //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
624 48
                            $xmlSheet = simplexml_load_string(
625 48
                                $this->securityScanner->scan($this->getFromZipArchive($zip, "$dir/$fileWorksheet")),
626 48
                                'SimpleXMLElement',
627 48
                                Settings::getLibXmlLoaderOptions()
628
                            );
629
630 48
                            $sharedFormulas = [];
631
632 48
                            if (isset($eleSheet['state']) && (string) $eleSheet['state'] != '') {
633 2
                                $docSheet->setSheetState((string) $eleSheet['state']);
634
                            }
635
636 48
                            if ($xmlSheet) {
637 48
                                if (isset($xmlSheet->sheetViews, $xmlSheet->sheetViews->sheetView)) {
638 48
                                    $sheetViews = new SheetViews($xmlSheet->sheetViews->sheetView, $docSheet);
639 48
                                    $sheetViews->load();
640
                                }
641
642 48
                                $sheetViewOptions = new SheetViewOptions($docSheet, $xmlSheet);
1 ignored issue
show
Bug introduced by
It seems like $xmlSheet can also be of type false; however, parameter $worksheetXml of PhpOffice\PhpSpreadsheet...wOptions::__construct() does only seem to accept SimpleXMLElement|null, 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

642
                                $sheetViewOptions = new SheetViewOptions($docSheet, /** @scrutinizer ignore-type */ $xmlSheet);
Loading history...
643 48
                                $sheetViewOptions->load($this->getReadDataOnly());
644
645 48
                                (new ColumnAndRowAttributes($docSheet, $xmlSheet))
1 ignored issue
show
Bug introduced by
It seems like $xmlSheet can also be of type false; however, parameter $worksheetXml of PhpOffice\PhpSpreadsheet...tributes::__construct() does only seem to accept SimpleXMLElement|null, 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

645
                                (new ColumnAndRowAttributes($docSheet, /** @scrutinizer ignore-type */ $xmlSheet))
Loading history...
646 48
                                    ->load($this->getReadFilter(), $this->getReadDataOnly());
647
                            }
648
649 48
                            if ($xmlSheet && $xmlSheet->sheetData && $xmlSheet->sheetData->row) {
650 41
                                $cIndex = 1; // Cell Start from 1
651 41
                                foreach ($xmlSheet->sheetData->row as $row) {
652 41
                                    $rowIndex = 1;
653 41
                                    foreach ($row->c as $c) {
654 41
                                        $r = (string) $c['r'];
655 41
                                        if ($r == '') {
656 1
                                            $r = Coordinate::stringFromColumnIndex($rowIndex) . $cIndex;
657
                                        }
658 41
                                        $cellDataType = (string) $c['t'];
659 41
                                        $value = null;
660 41
                                        $calculatedValue = null;
661
662
                                        // Read cell?
663 41
                                        if ($this->getReadFilter() !== null) {
664 41
                                            $coordinates = Coordinate::coordinateFromString($r);
665
666 41
                                            if (!$this->getReadFilter()->readCell($coordinates[0], (int) $coordinates[1], $docSheet->getTitle())) {
667 3
                                                $rowIndex += 1;
668
669 3
                                                continue;
670
                                            }
671
                                        }
672
673
                                        // Read cell!
674 41
                                        switch ($cellDataType) {
675 41
                                            case 's':
676 27
                                                if ((string) $c->v != '') {
677 27
                                                    $value = $sharedStrings[(int) ($c->v)];
678
679 27
                                                    if ($value instanceof RichText) {
680 27
                                                        $value = clone $value;
681
                                                    }
682
                                                } else {
683
                                                    $value = '';
684
                                                }
685
686 27
                                                break;
687 31
                                            case 'b':
688 5
                                                if (!isset($c->f)) {
689 3
                                                    $value = self::castToBoolean($c);
690
                                                } else {
691
                                                    // Formula
692 2
                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToBoolean');
693 2
                                                    if (isset($c->f['t'])) {
694
                                                        $att = $c->f;
695
                                                        $docSheet->getCell($r)->setFormulaAttributes($att);
696
                                                    }
697
                                                }
698
699 5
                                                break;
700 27
                                            case 'inlineStr':
701 2
                                                if (isset($c->f)) {
702
                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToError');
703
                                                } else {
704 2
                                                    $value = $this->parseRichText($c->is);
705
                                                }
706
707 2
                                                break;
708 27
                                            case 'e':
709
                                                if (!isset($c->f)) {
710
                                                    $value = self::castToError($c);
711
                                                } else {
712
                                                    // Formula
713
                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToError');
714
                                                }
715
716
                                                break;
717
                                            default:
718 27
                                                if (!isset($c->f)) {
719 25
                                                    $value = self::castToString($c);
720
                                                } else {
721
                                                    // Formula
722 7
                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToString');
723
                                                }
724
725 27
                                                break;
726
                                        }
727
728
                                        // read empty cells or the cells are not empty
729 41
                                        if ($this->readEmptyCells || ($value !== null && $value !== '')) {
730
                                            // Check for numeric values
731 41
                                            if (is_numeric($value) && $cellDataType != 's') {
732 22
                                                if ($value == (int) $value) {
733 21
                                                    $value = (int) $value;
734 3
                                                } elseif ($value == (float) $value) {
735 3
                                                    $value = (float) $value;
736
                                                }
737
                                            }
738
739
                                            // Rich text?
740 41
                                            if ($value instanceof RichText && $this->readDataOnly) {
741
                                                $value = $value->getPlainText();
742
                                            }
743
744 41
                                            $cell = $docSheet->getCell($r);
745
                                            // Assign value
746 41
                                            if ($cellDataType != '') {
747 31
                                                $cell->setValueExplicit($value, $cellDataType);
748
                                            } else {
749 25
                                                $cell->setValue($value);
750
                                            }
751 41
                                            if ($calculatedValue !== null) {
752 8
                                                $cell->setCalculatedValue($calculatedValue);
753
                                            }
754
755
                                            // Style information?
756 41
                                            if ($c['s'] && !$this->readDataOnly) {
757
                                                // no style index means 0, it seems
758 13
                                                $cell->setXfIndex(isset($styles[(int) ($c['s'])]) ?
759 13
                                                    (int) ($c['s']) : 0);
760
                                            }
761
                                        }
762 41
                                        $rowIndex += 1;
763
                                    }
764 41
                                    $cIndex += 1;
765
                                }
766
                            }
767
768 48
                            if (!$this->readDataOnly && $xmlSheet && $xmlSheet->conditionalFormatting) {
769 2
                                (new ConditionalStyles($docSheet, $xmlSheet, $dxfs))->load();
770
                            }
771
772 48
                            $aKeys = ['sheet', 'objects', 'scenarios', 'formatCells', 'formatColumns', 'formatRows', 'insertColumns', 'insertRows', 'insertHyperlinks', 'deleteColumns', 'deleteRows', 'selectLockedCells', 'sort', 'autoFilter', 'pivotTables', 'selectUnlockedCells'];
773 48
                            if (!$this->readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) {
774 39
                                foreach ($aKeys as $key) {
775 39
                                    $method = 'set' . ucfirst($key);
776 39
                                    $docSheet->getProtection()->$method(self::boolean((string) $xmlSheet->sheetProtection[$key]));
777
                                }
778
                            }
779
780 48
                            if (!$this->readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) {
781 39
                                $docSheet->getProtection()->setPassword((string) $xmlSheet->sheetProtection['password'], true);
782 39
                                if ($xmlSheet->protectedRanges->protectedRange) {
783 2
                                    foreach ($xmlSheet->protectedRanges->protectedRange as $protectedRange) {
784 2
                                        $docSheet->protectCells((string) $protectedRange['sqref'], (string) $protectedRange['password'], true);
785
                                    }
786
                                }
787
                            }
788
789 48
                            if ($xmlSheet && $xmlSheet->autoFilter && !$this->readDataOnly) {
790 1
                                (new AutoFilter($docSheet, $xmlSheet))->load();
791
                            }
792
793 48
                            if ($xmlSheet && $xmlSheet->mergeCells && $xmlSheet->mergeCells->mergeCell && !$this->readDataOnly) {
794 8
                                foreach ($xmlSheet->mergeCells->mergeCell as $mergeCell) {
795 8
                                    $mergeRef = (string) $mergeCell['ref'];
796 8
                                    if (strpos($mergeRef, ':') !== false) {
797 8
                                        $docSheet->mergeCells((string) $mergeCell['ref']);
798
                                    }
799
                                }
800
                            }
801
802 48
                            if ($xmlSheet && !$this->readDataOnly) {
803 48
                                $unparsedLoadedData = (new PageSetup($docSheet, $xmlSheet))->load($unparsedLoadedData);
804
                            }
805
806 48
                            if ($xmlSheet && $xmlSheet->dataValidations && !$this->readDataOnly) {
807 1
                                (new DataValidations($docSheet, $xmlSheet))->load();
808
                            }
809
810
                            // unparsed sheet AlternateContent
811 48
                            if ($xmlSheet && !$this->readDataOnly) {
812 48
                                $mc = $xmlSheet->children('http://schemas.openxmlformats.org/markup-compatibility/2006');
813 48
                                if ($mc->AlternateContent) {
814 1
                                    foreach ($mc->AlternateContent as $alternateContent) {
815 1
                                        $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['AlternateContents'][] = $alternateContent->asXML();
816
                                    }
817
                                }
818
                            }
819
820
                            // Add hyperlinks
821 48
                            if (!$this->readDataOnly) {
822 48
                                $hyperlinkReader = new Hyperlinks($docSheet);
823
                                // Locate hyperlink relations
824 48
                                $relationsFileName = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
825 48
                                if ($zip->locateName($relationsFileName)) {
826
                                    //~ http://schemas.openxmlformats.org/package/2006/relationships"
827 43
                                    $relsWorksheet = simplexml_load_string(
828 43
                                        $this->securityScanner->scan(
829 43
                                            $this->getFromZipArchive($zip, $relationsFileName)
830
                                        ),
831 43
                                        'SimpleXMLElement',
832 43
                                        Settings::getLibXmlLoaderOptions()
833
                                    );
834 43
                                    $hyperlinkReader->readHyperlinks($relsWorksheet);
1 ignored issue
show
Bug introduced by
It seems like $relsWorksheet can also be of type false; however, parameter $relsWorksheet of PhpOffice\PhpSpreadsheet...links::readHyperlinks() does only seem to accept SimpleXMLElement, 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

834
                                    $hyperlinkReader->readHyperlinks(/** @scrutinizer ignore-type */ $relsWorksheet);
Loading history...
835
                                }
836
837
                                // Loop through hyperlinks
838 48
                                if ($xmlSheet && $xmlSheet->hyperlinks) {
839 2
                                    $hyperlinkReader->setHyperlinks($xmlSheet->hyperlinks);
840
                                }
841
                            }
842
843
                            // Add comments
844 48
                            $comments = [];
845 48
                            $vmlComments = [];
846 48
                            if (!$this->readDataOnly) {
847
                                // Locate comment relations
848 48
                                if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
849
                                    //~ http://schemas.openxmlformats.org/package/2006/relationships"
850 43
                                    $relsWorksheet = simplexml_load_string(
851 43
                                        $this->securityScanner->scan(
852 43
                                            $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
853
                                        ),
854 43
                                        'SimpleXMLElement',
855 43
                                        Settings::getLibXmlLoaderOptions()
856
                                    );
857 43
                                    foreach ($relsWorksheet->Relationship as $ele) {
858 16
                                        if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments') {
859 3
                                            $comments[(string) $ele['Id']] = (string) $ele['Target'];
860
                                        }
861 16
                                        if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing') {
862 4
                                            $vmlComments[(string) $ele['Id']] = (string) $ele['Target'];
863
                                        }
864
                                    }
865
                                }
866
867
                                // Loop through comments
868 48
                                foreach ($comments as $relName => $relPath) {
869
                                    // Load comments file
870 3
                                    $relPath = File::realpath(dirname("$dir/$fileWorksheet") . '/' . $relPath);
871 3
                                    $commentsFile = simplexml_load_string(
872 3
                                        $this->securityScanner->scan($this->getFromZipArchive($zip, $relPath)),
873 3
                                        'SimpleXMLElement',
874 3
                                        Settings::getLibXmlLoaderOptions()
875
                                    );
876
877
                                    // Utility variables
878 3
                                    $authors = [];
879
880
                                    // Loop through authors
881 3
                                    foreach ($commentsFile->authors->author as $author) {
882 3
                                        $authors[] = (string) $author;
883
                                    }
884
885
                                    // Loop through contents
886 3
                                    foreach ($commentsFile->commentList->comment as $comment) {
887 3
                                        if (!empty($comment['authorId'])) {
888
                                            $docSheet->getComment((string) $comment['ref'])->setAuthor($authors[(string) $comment['authorId']]);
889
                                        }
890 3
                                        $docSheet->getComment((string) $comment['ref'])->setText($this->parseRichText($comment->text));
891
                                    }
892
                                }
893
894
                                // later we will remove from it real vmlComments
895 48
                                $unparsedVmlDrawings = $vmlComments;
896
897
                                // Loop through VML comments
898 48
                                foreach ($vmlComments as $relName => $relPath) {
899
                                    // Load VML comments file
900 4
                                    $relPath = File::realpath(dirname("$dir/$fileWorksheet") . '/' . $relPath);
901
902
                                    try {
903 4
                                        $vmlCommentsFile = simplexml_load_string(
904 4
                                            $this->securityScanner->scan($this->getFromZipArchive($zip, $relPath)),
905 4
                                            'SimpleXMLElement',
906 4
                                            Settings::getLibXmlLoaderOptions()
907
                                        );
908 4
                                        $vmlCommentsFile->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
909
                                    } catch (\Throwable $ex) {
910
                                        //Ignore unparsable vmlDrawings. Later they will be moved from $unparsedVmlDrawings to $unparsedLoadedData
911
                                        continue;
912
                                    }
913
914 4
                                    $shapes = $vmlCommentsFile->xpath('//v:shape');
915 4
                                    foreach ($shapes as $shape) {
916 4
                                        $shape->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
917
918 4
                                        if (isset($shape['style'])) {
919 4
                                            $style = (string) $shape['style'];
920 4
                                            $fillColor = strtoupper(substr((string) $shape['fillcolor'], 1));
921 4
                                            $column = null;
922 4
                                            $row = null;
923
924 4
                                            $clientData = $shape->xpath('.//x:ClientData');
925 4
                                            if (is_array($clientData) && !empty($clientData)) {
926 4
                                                $clientData = $clientData[0];
927
928 4
                                                if (isset($clientData['ObjectType']) && (string) $clientData['ObjectType'] == 'Note') {
929 3
                                                    $temp = $clientData->xpath('.//x:Row');
930 3
                                                    if (is_array($temp)) {
931 3
                                                        $row = $temp[0];
932
                                                    }
933
934 3
                                                    $temp = $clientData->xpath('.//x:Column');
935 3
                                                    if (is_array($temp)) {
936 3
                                                        $column = $temp[0];
937
                                                    }
938
                                                }
939
                                            }
940
941 4
                                            if (($column !== null) && ($row !== null)) {
942
                                                // Set comment properties
943 3
                                                $comment = $docSheet->getCommentByColumnAndRow($column + 1, $row + 1);
944 3
                                                $comment->getFillColor()->setRGB($fillColor);
945
946
                                                // Parse style
947 3
                                                $styleArray = explode(';', str_replace(' ', '', $style));
948 3
                                                foreach ($styleArray as $stylePair) {
949 3
                                                    $stylePair = explode(':', $stylePair);
950
951 3
                                                    if ($stylePair[0] == 'margin-left') {
952 3
                                                        $comment->setMarginLeft($stylePair[1]);
953
                                                    }
954 3
                                                    if ($stylePair[0] == 'margin-top') {
955 3
                                                        $comment->setMarginTop($stylePair[1]);
956
                                                    }
957 3
                                                    if ($stylePair[0] == 'width') {
958 3
                                                        $comment->setWidth($stylePair[1]);
959
                                                    }
960 3
                                                    if ($stylePair[0] == 'height') {
961 3
                                                        $comment->setHeight($stylePair[1]);
962
                                                    }
963 3
                                                    if ($stylePair[0] == 'visibility') {
964 3
                                                        $comment->setVisible($stylePair[1] == 'visible');
965
                                                    }
966
                                                }
967
968 3
                                                unset($unparsedVmlDrawings[$relName]);
969
                                            }
970
                                        }
971
                                    }
972
                                }
973
974
                                // unparsed vmlDrawing
975 48
                                if ($unparsedVmlDrawings) {
976 1
                                    foreach ($unparsedVmlDrawings as $rId => $relPath) {
977 1
                                        $rId = substr($rId, 3); // rIdXXX
978 1
                                        $unparsedVmlDrawing = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['vmlDrawings'];
979 1
                                        $unparsedVmlDrawing[$rId] = [];
980 1
                                        $unparsedVmlDrawing[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $relPath);
981 1
                                        $unparsedVmlDrawing[$rId]['relFilePath'] = $relPath;
982 1
                                        $unparsedVmlDrawing[$rId]['content'] = $this->securityScanner->scan($this->getFromZipArchive($zip, $unparsedVmlDrawing[$rId]['filePath']));
983 1
                                        unset($unparsedVmlDrawing);
984
                                    }
985
                                }
986
987
                                // Header/footer images
988 48
                                if ($xmlSheet && $xmlSheet->legacyDrawingHF && !$this->readDataOnly) {
989
                                    if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
990
                                        //~ http://schemas.openxmlformats.org/package/2006/relationships"
991
                                        $relsWorksheet = simplexml_load_string(
992
                                            $this->securityScanner->scan(
993
                                                $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
994
                                            ),
995
                                            'SimpleXMLElement',
996
                                            Settings::getLibXmlLoaderOptions()
997
                                        );
998
                                        $vmlRelationship = '';
999
1000
                                        foreach ($relsWorksheet->Relationship as $ele) {
1001
                                            if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing') {
1002
                                                $vmlRelationship = self::dirAdd("$dir/$fileWorksheet", $ele['Target']);
1003
                                            }
1004
                                        }
1005
1006
                                        if ($vmlRelationship != '') {
1007
                                            // Fetch linked images
1008
                                            //~ http://schemas.openxmlformats.org/package/2006/relationships"
1009
                                            $relsVML = simplexml_load_string(
1010
                                                $this->securityScanner->scan(
1011
                                                    $this->getFromZipArchive($zip, dirname($vmlRelationship) . '/_rels/' . basename($vmlRelationship) . '.rels')
1012
                                                ),
1013
                                                'SimpleXMLElement',
1014
                                                Settings::getLibXmlLoaderOptions()
1015
                                            );
1016
                                            $drawings = [];
1017
                                            foreach ($relsVML->Relationship as $ele) {
1018
                                                if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') {
1019
                                                    $drawings[(string) $ele['Id']] = self::dirAdd($vmlRelationship, $ele['Target']);
1020
                                                }
1021
                                            }
1022
1023
                                            // Fetch VML document
1024
                                            $vmlDrawing = simplexml_load_string(
1025
                                                $this->securityScanner->scan($this->getFromZipArchive($zip, $vmlRelationship)),
1026
                                                'SimpleXMLElement',
1027
                                                Settings::getLibXmlLoaderOptions()
1028
                                            );
1029
                                            $vmlDrawing->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
1030
1031
                                            $hfImages = [];
1032
1033
                                            $shapes = $vmlDrawing->xpath('//v:shape');
1034
                                            foreach ($shapes as $idx => $shape) {
1035
                                                $shape->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
1036
                                                $imageData = $shape->xpath('//v:imagedata');
1037
1038
                                                if (!$imageData) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $imageData of type SimpleXMLElement[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1039
                                                    continue;
1040
                                                }
1041
1042
                                                $imageData = $imageData[$idx];
1043
1044
                                                $imageData = $imageData->attributes('urn:schemas-microsoft-com:office:office');
1045
                                                $style = self::toCSSArray((string) $shape['style']);
1046
1047
                                                $hfImages[(string) $shape['id']] = new HeaderFooterDrawing();
1048
                                                if (isset($imageData['title'])) {
1049
                                                    $hfImages[(string) $shape['id']]->setName((string) $imageData['title']);
1050
                                                }
1051
1052
                                                $hfImages[(string) $shape['id']]->setPath('zip://' . File::realpath($pFilename) . '#' . $drawings[(string) $imageData['relid']], false);
1053
                                                $hfImages[(string) $shape['id']]->setResizeProportional(false);
1054
                                                $hfImages[(string) $shape['id']]->setWidth($style['width']);
1055
                                                $hfImages[(string) $shape['id']]->setHeight($style['height']);
1056
                                                if (isset($style['margin-left'])) {
1057
                                                    $hfImages[(string) $shape['id']]->setOffsetX($style['margin-left']);
1058
                                                }
1059
                                                $hfImages[(string) $shape['id']]->setOffsetY($style['margin-top']);
1060
                                                $hfImages[(string) $shape['id']]->setResizeProportional(true);
1061
                                            }
1062
1063
                                            $docSheet->getHeaderFooter()->setImages($hfImages);
1064
                                        }
1065
                                    }
1066
                                }
1067
                            }
1068
1069
                            // TODO: Autoshapes from twoCellAnchors!
1070 48
                            if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
1071
                                //~ http://schemas.openxmlformats.org/package/2006/relationships"
1072 43
                                $relsWorksheet = simplexml_load_string(
1073 43
                                    $this->securityScanner->scan(
1074 43
                                        $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
1075
                                    ),
1076 43
                                    'SimpleXMLElement',
1077 43
                                    Settings::getLibXmlLoaderOptions()
1078
                                );
1079 43
                                $drawings = [];
1080 43
                                foreach ($relsWorksheet->Relationship as $ele) {
1081 16
                                    if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing') {
1082 8
                                        $drawings[(string) $ele['Id']] = self::dirAdd("$dir/$fileWorksheet", $ele['Target']);
1083
                                    }
1084
                                }
1085 43
                                if ($xmlSheet->drawing && !$this->readDataOnly) {
1086 8
                                    $unparsedDrawings = [];
1087 8
                                    foreach ($xmlSheet->drawing as $drawing) {
1088 8
                                        $drawingRelId = (string) self::getArrayItem($drawing->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id');
1089 8
                                        $fileDrawing = $drawings[$drawingRelId];
1090
                                        //~ http://schemas.openxmlformats.org/package/2006/relationships"
1091 8
                                        $relsDrawing = simplexml_load_string(
1092 8
                                            $this->securityScanner->scan(
1093 8
                                                $this->getFromZipArchive($zip, dirname($fileDrawing) . '/_rels/' . basename($fileDrawing) . '.rels')
1094
                                            ),
1095 8
                                            'SimpleXMLElement',
1096 8
                                            Settings::getLibXmlLoaderOptions()
1097
                                        );
1098 8
                                        $images = [];
1099 8
                                        $hyperlinks = [];
1100 8
                                        if ($relsDrawing && $relsDrawing->Relationship) {
1101 6
                                            foreach ($relsDrawing->Relationship as $ele) {
1102 6
                                                if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink') {
1103 2
                                                    $hyperlinks[(string) $ele['Id']] = (string) $ele['Target'];
1104
                                                }
1105 6
                                                if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') {
1106 6
                                                    $images[(string) $ele['Id']] = self::dirAdd($fileDrawing, $ele['Target']);
1107 4
                                                } elseif ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart') {
1108 2
                                                    if ($this->includeCharts) {
1109 2
                                                        $charts[self::dirAdd($fileDrawing, $ele['Target'])] = [
1110 2
                                                            'id' => (string) $ele['Id'],
1111 2
                                                            'sheet' => $docSheet->getTitle(),
1112
                                                        ];
1113
                                                    }
1114
                                                }
1115
                                            }
1116
                                        }
1117 8
                                        $xmlDrawing = simplexml_load_string(
1118 8
                                            $this->securityScanner->scan($this->getFromZipArchive($zip, $fileDrawing)),
1119 8
                                            'SimpleXMLElement',
1120 8
                                            Settings::getLibXmlLoaderOptions()
1121
                                        );
1122 8
                                        $xmlDrawingChildren = $xmlDrawing->children('http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing');
1123
1124 8
                                        if ($xmlDrawingChildren->oneCellAnchor) {
1125 4
                                            foreach ($xmlDrawingChildren->oneCellAnchor as $oneCellAnchor) {
1126 4
                                                if ($oneCellAnchor->pic->blipFill) {
1127
                                                    /** @var SimpleXMLElement $blip */
1128 4
                                                    $blip = $oneCellAnchor->pic->blipFill->children('http://schemas.openxmlformats.org/drawingml/2006/main')->blip;
1129
                                                    /** @var SimpleXMLElement $xfrm */
1130 4
                                                    $xfrm = $oneCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->xfrm;
1131
                                                    /** @var SimpleXMLElement $outerShdw */
1132 4
                                                    $outerShdw = $oneCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->effectLst->outerShdw;
1133
                                                    /** @var \SimpleXMLElement $hlinkClick */
1134 4
                                                    $hlinkClick = $oneCellAnchor->pic->nvPicPr->cNvPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->hlinkClick;
0 ignored issues
show
Unused Code introduced by
The assignment to $hlinkClick is dead and can be removed.
Loading history...
1135
1136 4
                                                    $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
1137 4
                                                    $objDrawing->setName((string) self::getArrayItem($oneCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'name'));
1138 4
                                                    $objDrawing->setDescription((string) self::getArrayItem($oneCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'descr'));
1139 4
                                                    $objDrawing->setPath(
1140 4
                                                        'zip://' . File::realpath($pFilename) . '#' .
1141 4
                                                        $images[(string) self::getArrayItem(
1142 4
                                                            $blip->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'),
1143 4
                                                            'embed'
1144
                                                        )],
1145 4
                                                        false
1146
                                                    );
1147 4
                                                    $objDrawing->setCoordinates(Coordinate::stringFromColumnIndex(((string) $oneCellAnchor->from->col) + 1) . ($oneCellAnchor->from->row + 1));
1148 4
                                                    $objDrawing->setOffsetX(Drawing::EMUToPixels($oneCellAnchor->from->colOff));
1149 4
                                                    $objDrawing->setOffsetY(Drawing::EMUToPixels($oneCellAnchor->from->rowOff));
1150 4
                                                    $objDrawing->setResizeProportional(false);
1151 4
                                                    $objDrawing->setWidth(Drawing::EMUToPixels(self::getArrayItem($oneCellAnchor->ext->attributes(), 'cx')));
1152 4
                                                    $objDrawing->setHeight(Drawing::EMUToPixels(self::getArrayItem($oneCellAnchor->ext->attributes(), 'cy')));
1153 4
                                                    if ($xfrm) {
1154 4
                                                        $objDrawing->setRotation(Drawing::angleToDegrees(self::getArrayItem($xfrm->attributes(), 'rot')));
1 ignored issue
show
Bug introduced by
It seems like self::getArrayItem($xfrm->attributes(), 'rot') can also be of type SimpleXMLElement; however, parameter $pValue of PhpOffice\PhpSpreadsheet...awing::angleToDegrees() does only seem to accept integer, 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

1154
                                                        $objDrawing->setRotation(Drawing::angleToDegrees(/** @scrutinizer ignore-type */ self::getArrayItem($xfrm->attributes(), 'rot')));
Loading history...
1155
                                                    }
1156 4
                                                    if ($outerShdw) {
1157 2
                                                        $shadow = $objDrawing->getShadow();
1158 2
                                                        $shadow->setVisible(true);
1159 2
                                                        $shadow->setBlurRadius(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
1 ignored issue
show
Bug introduced by
It seems like self::getArrayItem($oute...ttributes(), 'blurRad') can also be of type SimpleXMLElement; however, parameter $pValue of PhpOffice\PhpSpreadsheet...\Drawing::EMUToPixels() does only seem to accept integer, 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

1159
                                                        $shadow->setBlurRadius(Drawing::EMUToPixels(/** @scrutinizer ignore-type */ self::getArrayItem($outerShdw->attributes(), 'blurRad')));
Loading history...
1160 2
                                                        $shadow->setDistance(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
1161 2
                                                        $shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem($outerShdw->attributes(), 'dir')));
1162 2
                                                        $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

1162
                                                        $shadow->setAlignment(/** @scrutinizer ignore-type */ (string) self::getArrayItem($outerShdw->attributes(), 'algn'));
Loading history...
1163 2
                                                        $clr = isset($outerShdw->srgbClr) ? $outerShdw->srgbClr : $outerShdw->prstClr;
1164 2
                                                        $shadow->getColor()->setRGB(self::getArrayItem($clr->attributes(), 'val'));
1165 2
                                                        $shadow->setAlpha(self::getArrayItem($clr->alpha->attributes(), 'val') / 1000);
1166
                                                    }
1167
1168 4
                                                    $this->readHyperLinkDrawing($objDrawing, $oneCellAnchor, $hyperlinks);
1169
1170 4
                                                    $objDrawing->setWorksheet($docSheet);
1171
                                                } else {
1172
                                                    //    ? Can charts be positioned with a oneCellAnchor ?
1173
                                                    $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...
1174
                                                    $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...
1175
                                                    $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...
1176
                                                    $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...
1177
                                                    $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...
1178
                                                }
1179
                                            }
1180
                                        }
1181 8
                                        if ($xmlDrawingChildren->twoCellAnchor) {
1182 2
                                            foreach ($xmlDrawingChildren->twoCellAnchor as $twoCellAnchor) {
1183 2
                                                if ($twoCellAnchor->pic->blipFill) {
1184 2
                                                    $blip = $twoCellAnchor->pic->blipFill->children('http://schemas.openxmlformats.org/drawingml/2006/main')->blip;
1185 2
                                                    $xfrm = $twoCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->xfrm;
1186 2
                                                    $outerShdw = $twoCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->effectLst->outerShdw;
1187 2
                                                    $hlinkClick = $twoCellAnchor->pic->nvPicPr->cNvPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->hlinkClick;
1188 2
                                                    $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
1189 2
                                                    $objDrawing->setName((string) self::getArrayItem($twoCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'name'));
1190 2
                                                    $objDrawing->setDescription((string) self::getArrayItem($twoCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'descr'));
1191 2
                                                    $objDrawing->setPath(
1192 2
                                                        'zip://' . File::realpath($pFilename) . '#' .
1193 2
                                                        $images[(string) self::getArrayItem(
1194 2
                                                            $blip->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'),
1195 2
                                                            'embed'
1196
                                                        )],
1197 2
                                                        false
1198
                                                    );
1199 2
                                                    $objDrawing->setCoordinates(Coordinate::stringFromColumnIndex(((string) $twoCellAnchor->from->col) + 1) . ($twoCellAnchor->from->row + 1));
1200 2
                                                    $objDrawing->setOffsetX(Drawing::EMUToPixels($twoCellAnchor->from->colOff));
1201 2
                                                    $objDrawing->setOffsetY(Drawing::EMUToPixels($twoCellAnchor->from->rowOff));
1202 2
                                                    $objDrawing->setResizeProportional(false);
1203
1204 2
                                                    if ($xfrm) {
1205 2
                                                        $objDrawing->setWidth(Drawing::EMUToPixels(self::getArrayItem($xfrm->ext->attributes(), 'cx')));
1206 2
                                                        $objDrawing->setHeight(Drawing::EMUToPixels(self::getArrayItem($xfrm->ext->attributes(), 'cy')));
1207 2
                                                        $objDrawing->setRotation(Drawing::angleToDegrees(self::getArrayItem($xfrm->attributes(), 'rot')));
1208
                                                    }
1209 2
                                                    if ($outerShdw) {
1210
                                                        $shadow = $objDrawing->getShadow();
1211
                                                        $shadow->setVisible(true);
1212
                                                        $shadow->setBlurRadius(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
1213
                                                        $shadow->setDistance(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
1214
                                                        $shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem($outerShdw->attributes(), 'dir')));
1215
                                                        $shadow->setAlignment((string) self::getArrayItem($outerShdw->attributes(), 'algn'));
1216
                                                        $clr = isset($outerShdw->srgbClr) ? $outerShdw->srgbClr : $outerShdw->prstClr;
1217
                                                        $shadow->getColor()->setRGB(self::getArrayItem($clr->attributes(), 'val'));
1218
                                                        $shadow->setAlpha(self::getArrayItem($clr->alpha->attributes(), 'val') / 1000);
1219
                                                    }
1220
1221 2
                                                    $this->readHyperLinkDrawing($objDrawing, $twoCellAnchor, $hyperlinks);
1222
1223 2
                                                    $objDrawing->setWorksheet($docSheet);
1224 2
                                                } elseif (($this->includeCharts) && ($twoCellAnchor->graphicFrame)) {
1225 2
                                                    $fromCoordinate = Coordinate::stringFromColumnIndex(((string) $twoCellAnchor->from->col) + 1) . ($twoCellAnchor->from->row + 1);
1226 2
                                                    $fromOffsetX = Drawing::EMUToPixels($twoCellAnchor->from->colOff);
1227 2
                                                    $fromOffsetY = Drawing::EMUToPixels($twoCellAnchor->from->rowOff);
1228 2
                                                    $toCoordinate = Coordinate::stringFromColumnIndex(((string) $twoCellAnchor->to->col) + 1) . ($twoCellAnchor->to->row + 1);
1229 2
                                                    $toOffsetX = Drawing::EMUToPixels($twoCellAnchor->to->colOff);
1230 2
                                                    $toOffsetY = Drawing::EMUToPixels($twoCellAnchor->to->rowOff);
1231 2
                                                    $graphic = $twoCellAnchor->graphicFrame->children('http://schemas.openxmlformats.org/drawingml/2006/main')->graphic;
1232
                                                    /** @var SimpleXMLElement $chartRef */
1233 2
                                                    $chartRef = $graphic->graphicData->children('http://schemas.openxmlformats.org/drawingml/2006/chart')->chart;
1234 2
                                                    $thisChart = (string) $chartRef->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
1235
1236 2
                                                    $chartDetails[$docSheet->getTitle() . '!' . $thisChart] = [
1237 2
                                                        'fromCoordinate' => $fromCoordinate,
1238 2
                                                        'fromOffsetX' => $fromOffsetX,
1239 2
                                                        'fromOffsetY' => $fromOffsetY,
1240 2
                                                        'toCoordinate' => $toCoordinate,
1241 2
                                                        'toOffsetX' => $toOffsetX,
1242 2
                                                        'toOffsetY' => $toOffsetY,
1243 2
                                                        'worksheetTitle' => $docSheet->getTitle(),
1244
                                                    ];
1245
                                                }
1246
                                            }
1247
                                        }
1248 8
                                        if ($relsDrawing === false && $xmlDrawing->count() == 0) {
1249
                                            // Save Drawing without rels and children as unparsed
1250 2
                                            $unparsedDrawings[$drawingRelId] = $xmlDrawing->asXML();
1251
                                        }
1252
                                    }
1253
1254
                                    // store original rId of drawing files
1255 8
                                    $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'] = [];
1256 8
                                    foreach ($relsWorksheet->Relationship as $ele) {
1257 8
                                        if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing') {
1258 8
                                            $drawingRelId = (string) $ele['Id'];
1259 8
                                            $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'][(string) $ele['Target']] = $drawingRelId;
1260 8
                                            if (isset($unparsedDrawings[$drawingRelId])) {
1261 2
                                                $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['Drawings'][$drawingRelId] = $unparsedDrawings[$drawingRelId];
1262
                                            }
1263
                                        }
1264
                                    }
1265
1266
                                    // unparsed drawing AlternateContent
1267 8
                                    $xmlAltDrawing = simplexml_load_string(
1268 8
                                        $this->securityScanner->scan($this->getFromZipArchive($zip, $fileDrawing)),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $fileDrawing does not seem to be defined for all execution paths leading up to this point.
Loading history...
1269 8
                                        'SimpleXMLElement',
1270 8
                                        Settings::getLibXmlLoaderOptions()
1271 8
                                    )->children('http://schemas.openxmlformats.org/markup-compatibility/2006');
1272
1273 8
                                    if ($xmlAltDrawing->AlternateContent) {
1274 1
                                        foreach ($xmlAltDrawing->AlternateContent as $alternateContent) {
1275 1
                                            $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingAlternateContents'][] = $alternateContent->asXML();
1276
                                        }
1277
                                    }
1278
                                }
1279
                            }
1280
1281 48
                            $this->readFormControlProperties($excel, $zip, $dir, $fileWorksheet, $docSheet, $unparsedLoadedData);
1282 48
                            $this->readPrinterSettings($excel, $zip, $dir, $fileWorksheet, $docSheet, $unparsedLoadedData);
1283
1284
                            // Loop through definedNames
1285 48
                            if ($xmlWorkbook->definedNames) {
1286 34
                                foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
1287
                                    // Extract range
1288 3
                                    $extractedRange = (string) $definedName;
1289 3
                                    if (($spos = strpos($extractedRange, '!')) !== false) {
1290 3
                                        $extractedRange = substr($extractedRange, 0, $spos) . str_replace('$', '', substr($extractedRange, $spos));
1291
                                    } else {
1292
                                        $extractedRange = str_replace('$', '', $extractedRange);
1293
                                    }
1294
1295
                                    // Valid range?
1296 3
                                    if (stripos((string) $definedName, '#REF!') !== false || $extractedRange == '') {
1297
                                        continue;
1298
                                    }
1299
1300
                                    // Some definedNames are only applicable if we are on the same sheet...
1301 3
                                    if ((string) $definedName['localSheetId'] != '' && (string) $definedName['localSheetId'] == $oldSheetId) {
1302
                                        // Switch on type
1303 3
                                        switch ((string) $definedName['name']) {
1304 3
                                            case '_xlnm._FilterDatabase':
1305 1
                                                if ((string) $definedName['hidden'] !== '1') {
1306
                                                    $extractedRange = explode(',', $extractedRange);
1307
                                                    foreach ($extractedRange as $range) {
1308
                                                        $autoFilterRange = $range;
1309
                                                        if (strpos($autoFilterRange, ':') !== false) {
1310
                                                            $docSheet->getAutoFilter()->setRange($autoFilterRange);
1311
                                                        }
1312
                                                    }
1313
                                                }
1314
1315 1
                                                break;
1316 2
                                            case '_xlnm.Print_Titles':
1317
                                                // Split $extractedRange
1318 1
                                                $extractedRange = explode(',', $extractedRange);
1319
1320
                                                // Set print titles
1321 1
                                                foreach ($extractedRange as $range) {
1322 1
                                                    $matches = [];
1323 1
                                                    $range = str_replace('$', '', $range);
1324
1325
                                                    // check for repeating columns, e g. 'A:A' or 'A:D'
1326 1
                                                    if (preg_match('/!?([A-Z]+)\:([A-Z]+)$/', $range, $matches)) {
1327
                                                        $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$matches[1], $matches[2]]);
1328 1
                                                    } elseif (preg_match('/!?(\d+)\:(\d+)$/', $range, $matches)) {
1329
                                                        // check for repeating rows, e.g. '1:1' or '1:5'
1330 1
                                                        $docSheet->getPageSetup()->setRowsToRepeatAtTop([$matches[1], $matches[2]]);
1331
                                                    }
1332
                                                }
1333
1334 1
                                                break;
1335 1
                                            case '_xlnm.Print_Area':
1336 1
                                                $rangeSets = preg_split("/('?(?:.*?)'?(?:![A-Z0-9]+:[A-Z0-9]+)),?/", $extractedRange, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
1337 1
                                                $newRangeSets = [];
1338 1
                                                foreach ($rangeSets as $rangeSet) {
1339 1
                                                    [$sheetName, $rangeSet] = Worksheet::extractSheetTitle($rangeSet, true);
1340 1
                                                    if (strpos($rangeSet, ':') === false) {
1341
                                                        $rangeSet = $rangeSet . ':' . $rangeSet;
1342
                                                    }
1343 1
                                                    $newRangeSets[] = str_replace('$', '', $rangeSet);
1344
                                                }
1345 1
                                                $docSheet->getPageSetup()->setPrintArea(implode(',', $newRangeSets));
1346
1347 1
                                                break;
1348
                                            default:
1349
                                                break;
1350
                                        }
1351
                                    }
1352
                                }
1353
                            }
1354
1355
                            // Next sheet id
1356 48
                            ++$sheetId;
1357
                        }
1358
1359
                        // Loop through definedNames
1360 48
                        if ($xmlWorkbook->definedNames) {
1361 34
                            foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
1362
                                // Extract range
1363 3
                                $extractedRange = (string) $definedName;
1364 3
                                if (($spos = strpos($extractedRange, '!')) !== false) {
1365 3
                                    $extractedRange = substr($extractedRange, 0, $spos) . str_replace('$', '', substr($extractedRange, $spos));
1366
                                } else {
1367
                                    $extractedRange = str_replace('$', '', $extractedRange);
1368
                                }
1369
1370
                                // Valid range?
1371 3
                                if (stripos((string) $definedName, '#REF!') !== false || $extractedRange == '') {
1372
                                    continue;
1373
                                }
1374
1375
                                // Some definedNames are only applicable if we are on the same sheet...
1376 3
                                if ((string) $definedName['localSheetId'] != '') {
1377
                                    // Local defined name
1378
                                    // Switch on type
1379 3
                                    switch ((string) $definedName['name']) {
1380 3
                                        case '_xlnm._FilterDatabase':
1381 2
                                        case '_xlnm.Print_Titles':
1382 1
                                        case '_xlnm.Print_Area':
1383 3
                                            break;
1384
                                        default:
1385
                                            if ($mapSheetId[(int) $definedName['localSheetId']] !== null) {
1386
                                                if (strpos((string) $definedName, '!') !== false) {
1387
                                                    $range = Worksheet::extractSheetTitle((string) $definedName, true);
1388
                                                    $range[0] = str_replace("''", "'", $range[0]);
1389
                                                    $range[0] = str_replace("'", '', $range[0]);
1390
                                                    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...
1391
                                                        $extractedRange = str_replace('$', '', $range[1]);
1392
                                                        $scope = $docSheet->getParent()->getSheet($mapSheetId[(int) $definedName['localSheetId']]);
1393
                                                        $excel->addNamedRange(new NamedRange((string) $definedName['name'], $worksheet, $extractedRange, true, $scope));
1394
                                                    }
1395
                                                }
1396
                                            }
1397
1398 3
                                            break;
1399
                                    }
1400
                                } elseif (!isset($definedName['localSheetId'])) {
1401
                                    // "Global" definedNames
1402
                                    $locatedSheet = null;
1403
                                    $extractedSheetName = '';
1404
                                    if (strpos((string) $definedName, '!') !== false) {
1405
                                        // Extract sheet name
1406
                                        $extractedSheetName = Worksheet::extractSheetTitle((string) $definedName, true);
1407
                                        $extractedSheetName = trim($extractedSheetName[0], "'");
1408
1409
                                        // Locate sheet
1410
                                        $locatedSheet = $excel->getSheetByName($extractedSheetName);
1411
1412
                                        // Modify range
1413
                                        [$worksheetName, $extractedRange] = Worksheet::extractSheetTitle($extractedRange, true);
1414
                                    }
1415
1416
                                    if ($locatedSheet !== null) {
1417
                                        $excel->addNamedRange(new NamedRange((string) $definedName['name'], $locatedSheet, $extractedRange, false));
1418
                                    }
1419
                                }
1420
                            }
1421
                        }
1422
                    }
1423
1424 48
                    if ((!$this->readDataOnly) || (!empty($this->loadSheetsOnly))) {
1425 48
                        $workbookView = $xmlWorkbook->bookViews->workbookView;
1426
1427
                        // active sheet index
1428 48
                        $activeTab = (int) ($workbookView['activeTab']); // refers to old sheet index
1429
1430
                        // keep active sheet index if sheet is still loaded, else first sheet is set as the active
1431 48
                        if (isset($mapSheetId[$activeTab]) && $mapSheetId[$activeTab] !== null) {
1432 48
                            $excel->setActiveSheetIndex($mapSheetId[$activeTab]);
1433
                        } else {
1434
                            if ($excel->getSheetCount() == 0) {
1435
                                $excel->createSheet();
1436
                            }
1437
                            $excel->setActiveSheetIndex(0);
1438
                        }
1439
1440 48
                        if (isset($workbookView['showHorizontalScroll'])) {
1441 33
                            $showHorizontalScroll = (string) $workbookView['showHorizontalScroll'];
1442 33
                            $excel->setShowHorizontalScroll($this->castXsdBooleanToBool($showHorizontalScroll));
1443
                        }
1444
1445 48
                        if (isset($workbookView['showVerticalScroll'])) {
1446 33
                            $showVerticalScroll = (string) $workbookView['showVerticalScroll'];
1447 33
                            $excel->setShowVerticalScroll($this->castXsdBooleanToBool($showVerticalScroll));
1448
                        }
1449
1450 48
                        if (isset($workbookView['showSheetTabs'])) {
1451 33
                            $showSheetTabs = (string) $workbookView['showSheetTabs'];
1452 33
                            $excel->setShowSheetTabs($this->castXsdBooleanToBool($showSheetTabs));
1453
                        }
1454
1455 48
                        if (isset($workbookView['minimized'])) {
1456 33
                            $minimized = (string) $workbookView['minimized'];
1457 33
                            $excel->setMinimized($this->castXsdBooleanToBool($minimized));
1458
                        }
1459
1460 48
                        if (isset($workbookView['autoFilterDateGrouping'])) {
1461 33
                            $autoFilterDateGrouping = (string) $workbookView['autoFilterDateGrouping'];
1462 33
                            $excel->setAutoFilterDateGrouping($this->castXsdBooleanToBool($autoFilterDateGrouping));
1463
                        }
1464
1465 48
                        if (isset($workbookView['firstSheet'])) {
1466 33
                            $firstSheet = (string) $workbookView['firstSheet'];
1467 33
                            $excel->setFirstSheetIndex((int) $firstSheet);
1468
                        }
1469
1470 48
                        if (isset($workbookView['visibility'])) {
1471 33
                            $visibility = (string) $workbookView['visibility'];
1472 33
                            $excel->setVisibility($visibility);
1473
                        }
1474
1475 48
                        if (isset($workbookView['tabRatio'])) {
1476 33
                            $tabRatio = (string) $workbookView['tabRatio'];
1477 33
                            $excel->setTabRatio((int) $tabRatio);
1478
                        }
1479
                    }
1480
1481 48
                    break;
1482
            }
1483
        }
1484
1485 48
        if (!$this->readDataOnly) {
1486 48
            $contentTypes = simplexml_load_string(
1487 48
                $this->securityScanner->scan(
1488 48
                    $this->getFromZipArchive($zip, '[Content_Types].xml')
1489
                ),
1490 48
                'SimpleXMLElement',
1491 48
                Settings::getLibXmlLoaderOptions()
1492
            );
1493
1494
            // Default content types
1495 48
            foreach ($contentTypes->Default as $contentType) {
1496 48
                switch ($contentType['ContentType']) {
1497 48
                    case 'application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings':
1498 11
                        $unparsedLoadedData['default_content_types'][(string) $contentType['Extension']] = (string) $contentType['ContentType'];
1499
1500 11
                        break;
1501
                }
1502
            }
1503
1504
            // Override content types
1505 48
            foreach ($contentTypes->Override as $contentType) {
1506 48
                switch ($contentType['ContentType']) {
1507 48
                    case 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml':
1508 2
                        if ($this->includeCharts) {
1509 2
                            $chartEntryRef = ltrim($contentType['PartName'], '/');
1510 2
                            $chartElements = simplexml_load_string(
1511 2
                                $this->securityScanner->scan(
1512 2
                                    $this->getFromZipArchive($zip, $chartEntryRef)
1513
                                ),
1514 2
                                'SimpleXMLElement',
1515 2
                                Settings::getLibXmlLoaderOptions()
1516
                            );
1517 2
                            $objChart = Chart::readChart($chartElements, basename($chartEntryRef, '.xml'));
1 ignored issue
show
Bug introduced by
It seems like $chartElements can also be of type false; however, parameter $chartElements of PhpOffice\PhpSpreadsheet...Xlsx\Chart::readChart() does only seem to accept SimpleXMLElement, 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

1517
                            $objChart = Chart::readChart(/** @scrutinizer ignore-type */ $chartElements, basename($chartEntryRef, '.xml'));
Loading history...
1518
1519 2
                            if (isset($charts[$chartEntryRef])) {
1520 2
                                $chartPositionRef = $charts[$chartEntryRef]['sheet'] . '!' . $charts[$chartEntryRef]['id'];
1521 2
                                if (isset($chartDetails[$chartPositionRef])) {
1522 2
                                    $excel->getSheetByName($charts[$chartEntryRef]['sheet'])->addChart($objChart);
1523 2
                                    $objChart->setWorksheet($excel->getSheetByName($charts[$chartEntryRef]['sheet']));
1524 2
                                    $objChart->setTopLeftPosition($chartDetails[$chartPositionRef]['fromCoordinate'], $chartDetails[$chartPositionRef]['fromOffsetX'], $chartDetails[$chartPositionRef]['fromOffsetY']);
1525 2
                                    $objChart->setBottomRightPosition($chartDetails[$chartPositionRef]['toCoordinate'], $chartDetails[$chartPositionRef]['toOffsetX'], $chartDetails[$chartPositionRef]['toOffsetY']);
1526
                                }
1527
                            }
1528
                        }
1529
1530 2
                        break;
1531
1532
                    // unparsed
1533 48
                    case 'application/vnd.ms-excel.controlproperties+xml':
1534 1
                        $unparsedLoadedData['override_content_types'][(string) $contentType['PartName']] = (string) $contentType['ContentType'];
1535
1536 1
                        break;
1537
                }
1538
            }
1539
        }
1540
1541 48
        $excel->setUnparsedLoadedData($unparsedLoadedData);
1542
1543 48
        $zip->close();
1544
1545 48
        return $excel;
1546
    }
1547
1548 48
    private static function readColor($color, $background = false)
1549
    {
1550 48
        if (isset($color['rgb'])) {
1551 41
            return (string) $color['rgb'];
1552 14
        } elseif (isset($color['indexed'])) {
1553 9
            return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
1554 11
        } elseif (isset($color['theme'])) {
1555 10
            if (self::$theme !== null) {
1556 10
                $returnColour = self::$theme->getColourByIndex((int) $color['theme']);
1557 10
                if (isset($color['tint'])) {
1558 1
                    $tintAdjust = (float) $color['tint'];
1559 1
                    $returnColour = Color::changeBrightness($returnColour, $tintAdjust);
1560
                }
1561
1562 10
                return 'FF' . $returnColour;
1563
            }
1564
        }
1565
1566 2
        if ($background) {
1567
            return 'FFFFFFFF';
1568
        }
1569
1570 2
        return 'FF000000';
1571
    }
1572
1573
    /**
1574
     * @param Style $docStyle
1575
     * @param SimpleXMLElement|\stdClass $style
1576
     */
1577 48
    private static function readStyle(Style $docStyle, $style)
1578
    {
1579 48
        $docStyle->getNumberFormat()->setFormatCode($style->numFmt);
1580
1581
        // font
1582 48
        if (isset($style->font)) {
1583 48
            $docStyle->getFont()->setName((string) $style->font->name['val']);
1584 48
            $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

1584
            $docStyle->getFont()->setSize(/** @scrutinizer ignore-type */ (string) $style->font->sz['val']);
Loading history...
1585 48
            if (isset($style->font->b)) {
1586 41
                $docStyle->getFont()->setBold(!isset($style->font->b['val']) || self::boolean((string) $style->font->b['val']));
1587
            }
1588 48
            if (isset($style->font->i)) {
1589 35
                $docStyle->getFont()->setItalic(!isset($style->font->i['val']) || self::boolean((string) $style->font->i['val']));
1590
            }
1591 48
            if (isset($style->font->strike)) {
1592 34
                $docStyle->getFont()->setStrikethrough(!isset($style->font->strike['val']) || self::boolean((string) $style->font->strike['val']));
1593
            }
1594 48
            $docStyle->getFont()->getColor()->setARGB(self::readColor($style->font->color));
1595
1596 48
            if (isset($style->font->u) && !isset($style->font->u['val'])) {
1597 1
                $docStyle->getFont()->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
1598 48
            } elseif (isset($style->font->u, $style->font->u['val'])) {
1599 33
                $docStyle->getFont()->setUnderline((string) $style->font->u['val']);
1600
            }
1601
1602 48
            if (isset($style->font->vertAlign, $style->font->vertAlign['val'])) {
1603 1
                $vertAlign = strtolower((string) $style->font->vertAlign['val']);
1604 1
                if ($vertAlign == 'superscript') {
1605 1
                    $docStyle->getFont()->setSuperscript(true);
1606
                }
1607 1
                if ($vertAlign == 'subscript') {
1608 1
                    $docStyle->getFont()->setSubscript(true);
1609
                }
1610
            }
1611
        }
1612
1613
        // fill
1614 48
        if (isset($style->fill)) {
1615 48
            if ($style->fill->gradientFill) {
1616
                /** @var SimpleXMLElement $gradientFill */
1617 2
                $gradientFill = $style->fill->gradientFill[0];
1618 2
                if (!empty($gradientFill['type'])) {
1619 2
                    $docStyle->getFill()->setFillType((string) $gradientFill['type']);
1620
                }
1621 2
                $docStyle->getFill()->setRotation((float) ($gradientFill['degree']));
1622 2
                $gradientFill->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
1623 2
                $docStyle->getFill()->getStartColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
1624 2
                $docStyle->getFill()->getEndColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
1625 48
            } elseif ($style->fill->patternFill) {
1626 48
                $patternType = (string) $style->fill->patternFill['patternType'] != '' ? (string) $style->fill->patternFill['patternType'] : 'solid';
1627 48
                $docStyle->getFill()->setFillType($patternType);
1628 48
                if ($style->fill->patternFill->fgColor) {
1629 6
                    $docStyle->getFill()->getStartColor()->setARGB(self::readColor($style->fill->patternFill->fgColor, true));
1630
                } else {
1631 48
                    $docStyle->getFill()->getStartColor()->setARGB('FF000000');
1632
                }
1633 48
                if ($style->fill->patternFill->bgColor) {
1634 6
                    $docStyle->getFill()->getEndColor()->setARGB(self::readColor($style->fill->patternFill->bgColor, true));
1635
                }
1636
            }
1637
        }
1638
1639
        // border
1640 48
        if (isset($style->border)) {
1641 48
            $diagonalUp = self::boolean((string) $style->border['diagonalUp']);
1642 48
            $diagonalDown = self::boolean((string) $style->border['diagonalDown']);
1643 48
            if (!$diagonalUp && !$diagonalDown) {
1644 48
                $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_NONE);
1645 1
            } elseif ($diagonalUp && !$diagonalDown) {
1646 1
                $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_UP);
1647 1
            } elseif (!$diagonalUp && $diagonalDown) {
1648 1
                $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_DOWN);
1649
            } else {
1650
                $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_BOTH);
1651
            }
1652 48
            self::readBorder($docStyle->getBorders()->getLeft(), $style->border->left);
1653 48
            self::readBorder($docStyle->getBorders()->getRight(), $style->border->right);
1654 48
            self::readBorder($docStyle->getBorders()->getTop(), $style->border->top);
1655 48
            self::readBorder($docStyle->getBorders()->getBottom(), $style->border->bottom);
1656 48
            self::readBorder($docStyle->getBorders()->getDiagonal(), $style->border->diagonal);
1657
        }
1658
1659
        // alignment
1660 48
        if (isset($style->alignment)) {
1661 48
            $docStyle->getAlignment()->setHorizontal((string) $style->alignment['horizontal']);
1662 48
            $docStyle->getAlignment()->setVertical((string) $style->alignment['vertical']);
1663
1664 48
            $textRotation = 0;
1665 48
            if ((int) $style->alignment['textRotation'] <= 90) {
1666 48
                $textRotation = (int) $style->alignment['textRotation'];
1667
            } elseif ((int) $style->alignment['textRotation'] > 90) {
1668
                $textRotation = 90 - (int) $style->alignment['textRotation'];
1669
            }
1670
1671 48
            $docStyle->getAlignment()->setTextRotation((int) $textRotation);
1672 48
            $docStyle->getAlignment()->setWrapText(self::boolean((string) $style->alignment['wrapText']));
1673 48
            $docStyle->getAlignment()->setShrinkToFit(self::boolean((string) $style->alignment['shrinkToFit']));
1674 48
            $docStyle->getAlignment()->setIndent((int) ((string) $style->alignment['indent']) > 0 ? (int) ((string) $style->alignment['indent']) : 0);
1675 48
            $docStyle->getAlignment()->setReadOrder((int) ((string) $style->alignment['readingOrder']) > 0 ? (int) ((string) $style->alignment['readingOrder']) : 0);
1676
        }
1677
1678
        // protection
1679 48
        if (isset($style->protection)) {
1680 48
            if (isset($style->protection['locked'])) {
1681 2
                if (self::boolean((string) $style->protection['locked'])) {
1682
                    $docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
1683
                } else {
1684 2
                    $docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
1685
                }
1686
            }
1687
1688 48
            if (isset($style->protection['hidden'])) {
1689
                if (self::boolean((string) $style->protection['hidden'])) {
1690
                    $docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
1691
                } else {
1692
                    $docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
1693
                }
1694
            }
1695
        }
1696
1697
        // top-level style settings
1698 48
        if (isset($style->quotePrefix)) {
1699 48
            $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

1699
            $docStyle->setQuotePrefix(/** @scrutinizer ignore-type */ $style->quotePrefix);
Loading history...
1700
        }
1701 48
    }
1702
1703
    /**
1704
     * @param Border $docBorder
1705
     * @param SimpleXMLElement $eleBorder
1706
     */
1707 48
    private static function readBorder(Border $docBorder, $eleBorder)
1708
    {
1709 48
        if (isset($eleBorder['style'])) {
1710 4
            $docBorder->setBorderStyle((string) $eleBorder['style']);
1711
        }
1712 48
        if (isset($eleBorder->color)) {
1713 4
            $docBorder->getColor()->setARGB(self::readColor($eleBorder->color));
1714
        }
1715 48
    }
1716
1717
    /**
1718
     * @param SimpleXMLElement | null $is
1719
     *
1720
     * @return RichText
1721
     */
1722 5
    private function parseRichText($is)
1723
    {
1724 5
        $value = new RichText();
1725
1726 5
        if (isset($is->t)) {
1727
            $value->createText(StringHelper::controlCharacterOOXML2PHP((string) $is->t));
1728
        } else {
1729 5
            if (is_object($is->r)) {
1730 5
                foreach ($is->r as $run) {
1731 5
                    if (!isset($run->rPr)) {
1732 5
                        $value->createText(StringHelper::controlCharacterOOXML2PHP((string) $run->t));
1733
                    } else {
1734 4
                        $objText = $value->createTextRun(StringHelper::controlCharacterOOXML2PHP((string) $run->t));
1735
1736 4
                        if (isset($run->rPr->rFont['val'])) {
1737 4
                            $objText->getFont()->setName((string) $run->rPr->rFont['val']);
1738
                        }
1739 4
                        if (isset($run->rPr->sz['val'])) {
1740 4
                            $objText->getFont()->setSize((float) $run->rPr->sz['val']);
1741
                        }
1742 4
                        if (isset($run->rPr->color)) {
1743 4
                            $objText->getFont()->setColor(new Color(self::readColor($run->rPr->color)));
1744
                        }
1745 4
                        if ((isset($run->rPr->b['val']) && self::boolean((string) $run->rPr->b['val'])) ||
1746 4
                            (isset($run->rPr->b) && !isset($run->rPr->b['val']))) {
1747 4
                            $objText->getFont()->setBold(true);
1748
                        }
1749 4
                        if ((isset($run->rPr->i['val']) && self::boolean((string) $run->rPr->i['val'])) ||
1750 4
                            (isset($run->rPr->i) && !isset($run->rPr->i['val']))) {
1751 2
                            $objText->getFont()->setItalic(true);
1752
                        }
1753 4
                        if (isset($run->rPr->vertAlign, $run->rPr->vertAlign['val'])) {
1754
                            $vertAlign = strtolower((string) $run->rPr->vertAlign['val']);
1755
                            if ($vertAlign == 'superscript') {
1756
                                $objText->getFont()->setSuperscript(true);
1757
                            }
1758
                            if ($vertAlign == 'subscript') {
1759
                                $objText->getFont()->setSubscript(true);
1760
                            }
1761
                        }
1762 4
                        if (isset($run->rPr->u) && !isset($run->rPr->u['val'])) {
1763
                            $objText->getFont()->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
1764 4
                        } elseif (isset($run->rPr->u, $run->rPr->u['val'])) {
1765 2
                            $objText->getFont()->setUnderline((string) $run->rPr->u['val']);
1766
                        }
1767 4
                        if ((isset($run->rPr->strike['val']) && self::boolean((string) $run->rPr->strike['val'])) ||
1768 4
                            (isset($run->rPr->strike) && !isset($run->rPr->strike['val']))) {
1769
                            $objText->getFont()->setStrikethrough(true);
1770
                        }
1771
                    }
1772
                }
1773
            }
1774
        }
1775
1776 5
        return $value;
1777
    }
1778
1779
    /**
1780
     * @param Spreadsheet $excel
1781
     * @param mixed $customUITarget
1782
     * @param mixed $zip
1783
     */
1784
    private function readRibbon(Spreadsheet $excel, $customUITarget, $zip)
1785
    {
1786
        $baseDir = dirname($customUITarget);
1787
        $nameCustomUI = basename($customUITarget);
1788
        // get the xml file (ribbon)
1789
        $localRibbon = $this->getFromZipArchive($zip, $customUITarget);
1790
        $customUIImagesNames = [];
1791
        $customUIImagesBinaries = [];
1792
        // something like customUI/_rels/customUI.xml.rels
1793
        $pathRels = $baseDir . '/_rels/' . $nameCustomUI . '.rels';
1794
        $dataRels = $this->getFromZipArchive($zip, $pathRels);
1795
        if ($dataRels) {
1796
            // exists and not empty if the ribbon have some pictures (other than internal MSO)
1797
            $UIRels = simplexml_load_string(
1798
                $this->securityScanner->scan($dataRels),
1799
                'SimpleXMLElement',
1800
                Settings::getLibXmlLoaderOptions()
1801
            );
1802
            if (false !== $UIRels) {
1803
                // we need to save id and target to avoid parsing customUI.xml and "guess" if it's a pseudo callback who load the image
1804
                foreach ($UIRels->Relationship as $ele) {
1805
                    if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') {
1806
                        // an image ?
1807
                        $customUIImagesNames[(string) $ele['Id']] = (string) $ele['Target'];
1808
                        $customUIImagesBinaries[(string) $ele['Target']] = $this->getFromZipArchive($zip, $baseDir . '/' . (string) $ele['Target']);
1809
                    }
1810
                }
1811
            }
1812
        }
1813
        if ($localRibbon) {
1814
            $excel->setRibbonXMLData($customUITarget, $localRibbon);
1815
            if (count($customUIImagesNames) > 0 && count($customUIImagesBinaries) > 0) {
1816
                $excel->setRibbonBinObjects($customUIImagesNames, $customUIImagesBinaries);
1817
            } else {
1818
                $excel->setRibbonBinObjects(null, null);
1819
            }
1820
        } else {
1821
            $excel->setRibbonXMLData(null, null);
1822
            $excel->setRibbonBinObjects(null, null);
1823
        }
1824
    }
1825
1826 49
    private static function getArrayItem($array, $key = 0)
1827
    {
1828 49
        return $array[$key] ?? null;
1829
    }
1830
1831 15
    private static function dirAdd($base, $add)
1832
    {
1833 15
        return preg_replace('~[^/]+/\.\./~', '', dirname($base) . "/$add");
1834
    }
1835
1836
    private static function toCSSArray($style)
1837
    {
1838
        $style = trim(str_replace(["\r", "\n"], '', $style), ';');
1839
1840
        $temp = explode(';', $style);
1841
        $style = [];
1842
        foreach ($temp as $item) {
1843
            $item = explode(':', $item);
1844
1845
            if (strpos($item[1], 'px') !== false) {
1846
                $item[1] = str_replace('px', '', $item[1]);
1847
            }
1848
            if (strpos($item[1], 'pt') !== false) {
1849
                $item[1] = str_replace('pt', '', $item[1]);
1850
                $item[1] = Font::fontSizeToPixels($item[1]);
1851
            }
1852
            if (strpos($item[1], 'in') !== false) {
1853
                $item[1] = str_replace('in', '', $item[1]);
1854
                $item[1] = Font::inchSizeToPixels($item[1]);
1855
            }
1856
            if (strpos($item[1], 'cm') !== false) {
1857
                $item[1] = str_replace('cm', '', $item[1]);
1858
                $item[1] = Font::centimeterSizeToPixels($item[1]);
1859
            }
1860
1861
            $style[$item[0]] = $item[1];
1862
        }
1863
1864
        return $style;
1865
    }
1866
1867 48
    private static function boolean($value)
1868
    {
1869 48
        if (is_object($value)) {
1870
            $value = (string) $value;
1871
        }
1872 48
        if (is_numeric($value)) {
1873 42
            return (bool) $value;
1874
        }
1875
1876 48
        return $value === 'true' || $value === 'TRUE';
1877
    }
1878
1879
    /**
1880
     * @param \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $objDrawing
1881
     * @param \SimpleXMLElement $cellAnchor
1882
     * @param array $hyperlinks
1883
     */
1884 6
    private function readHyperLinkDrawing($objDrawing, $cellAnchor, $hyperlinks)
1885
    {
1886 6
        $hlinkClick = $cellAnchor->pic->nvPicPr->cNvPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->hlinkClick;
1887
1888 6
        if ($hlinkClick->count() === 0) {
1889 4
            return;
1890
        }
1891
1892 2
        $hlinkId = (string) $hlinkClick->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships')['id'];
1893 2
        $hyperlink = new Hyperlink(
1894 2
            $hyperlinks[$hlinkId],
1895 2
            (string) self::getArrayItem($cellAnchor->pic->nvPicPr->cNvPr->attributes(), 'name')
1896
        );
1897 2
        $objDrawing->setHyperlink($hyperlink);
1898 2
    }
1899
1900 48
    private function readProtection(Spreadsheet $excel, SimpleXMLElement $xmlWorkbook)
1901
    {
1902 48
        if (!$xmlWorkbook->workbookProtection) {
1903 47
            return;
1904
        }
1905
1906 1
        if ($xmlWorkbook->workbookProtection['lockRevision']) {
1907
            $excel->getSecurity()->setLockRevision((bool) $xmlWorkbook->workbookProtection['lockRevision']);
1908
        }
1909
1910 1
        if ($xmlWorkbook->workbookProtection['lockStructure']) {
1911 1
            $excel->getSecurity()->setLockStructure((bool) $xmlWorkbook->workbookProtection['lockStructure']);
1912
        }
1913
1914 1
        if ($xmlWorkbook->workbookProtection['lockWindows']) {
1915
            $excel->getSecurity()->setLockWindows((bool) $xmlWorkbook->workbookProtection['lockWindows']);
1916
        }
1917
1918 1
        if ($xmlWorkbook->workbookProtection['revisionsPassword']) {
1919
            $excel->getSecurity()->setRevisionsPassword((string) $xmlWorkbook->workbookProtection['revisionsPassword'], true);
1920
        }
1921
1922 1
        if ($xmlWorkbook->workbookProtection['workbookPassword']) {
1923 1
            $excel->getSecurity()->setWorkbookPassword((string) $xmlWorkbook->workbookProtection['workbookPassword'], true);
1924
        }
1925 1
    }
1926
1927 48
    private function readFormControlProperties(Spreadsheet $excel, ZipArchive $zip, $dir, $fileWorksheet, $docSheet, array &$unparsedLoadedData)
1928
    {
1929 48
        if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
1930 8
            return;
1931
        }
1932
1933
        //~ http://schemas.openxmlformats.org/package/2006/relationships"
1934 43
        $relsWorksheet = simplexml_load_string(
1935 43
            $this->securityScanner->scan(
1936 43
                $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
1937
            ),
1938 43
            'SimpleXMLElement',
1939 43
            Settings::getLibXmlLoaderOptions()
1940
        );
1941 43
        $ctrlProps = [];
1942 43
        foreach ($relsWorksheet->Relationship as $ele) {
1943 16
            if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp') {
1944 1
                $ctrlProps[(string) $ele['Id']] = $ele;
1945
            }
1946
        }
1947
1948 43
        $unparsedCtrlProps = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['ctrlProps'];
1949 43
        foreach ($ctrlProps as $rId => $ctrlProp) {
1950 1
            $rId = substr($rId, 3); // rIdXXX
1951 1
            $unparsedCtrlProps[$rId] = [];
1952 1
            $unparsedCtrlProps[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $ctrlProp['Target']);
1953 1
            $unparsedCtrlProps[$rId]['relFilePath'] = (string) $ctrlProp['Target'];
1954 1
            $unparsedCtrlProps[$rId]['content'] = $this->securityScanner->scan($this->getFromZipArchive($zip, $unparsedCtrlProps[$rId]['filePath']));
1955
        }
1956 43
        unset($unparsedCtrlProps);
1957 43
    }
1958
1959 48
    private function readPrinterSettings(Spreadsheet $excel, ZipArchive $zip, $dir, $fileWorksheet, $docSheet, array &$unparsedLoadedData)
1960
    {
1961 48
        if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
1962 8
            return;
1963
        }
1964
1965
        //~ http://schemas.openxmlformats.org/package/2006/relationships"
1966 43
        $relsWorksheet = simplexml_load_string(
1967 43
            $this->securityScanner->scan(
1968 43
                $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
1969
            ),
1970 43
            'SimpleXMLElement',
1971 43
            Settings::getLibXmlLoaderOptions()
1972
        );
1973 43
        $sheetPrinterSettings = [];
1974 43
        foreach ($relsWorksheet->Relationship as $ele) {
1975 16
            if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings') {
1976 10
                $sheetPrinterSettings[(string) $ele['Id']] = $ele;
1977
            }
1978
        }
1979
1980 43
        $unparsedPrinterSettings = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['printerSettings'];
1981 43
        foreach ($sheetPrinterSettings as $rId => $printerSettings) {
1982 10
            $rId = substr($rId, 3); // rIdXXX
1983 10
            $unparsedPrinterSettings[$rId] = [];
1984 10
            $unparsedPrinterSettings[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $printerSettings['Target']);
1985 10
            $unparsedPrinterSettings[$rId]['relFilePath'] = (string) $printerSettings['Target'];
1986 10
            $unparsedPrinterSettings[$rId]['content'] = $this->securityScanner->scan($this->getFromZipArchive($zip, $unparsedPrinterSettings[$rId]['filePath']));
1987
        }
1988 43
        unset($unparsedPrinterSettings);
1989 43
    }
1990
1991
    /**
1992
     * Convert an 'xsd:boolean' XML value to a PHP boolean value.
1993
     * A valid 'xsd:boolean' XML value can be one of the following
1994
     * four values: 'true', 'false', '1', '0'.  It is case sensitive.
1995
     *
1996
     * Note that just doing '(bool) $xsdBoolean' is not safe,
1997
     * since '(bool) "false"' returns true.
1998
     *
1999
     * @see https://www.w3.org/TR/xmlschema11-2/#boolean
2000
     *
2001
     * @param string $xsdBoolean An XML string value of type 'xsd:boolean'
2002
     *
2003
     * @return bool  Boolean value
2004
     */
2005 33
    private function castXsdBooleanToBool($xsdBoolean)
2006
    {
2007 33
        if ($xsdBoolean === 'false') {
2008 33
            return false;
2009
        }
2010
2011 33
        return (bool) $xsdBoolean;
2012
    }
2013
2014
    /**
2015
     * @param ZipArchive $zip Opened zip archive
2016
     *
2017
     * @return string basename of the used excel workbook
2018
     */
2019 51
    private function getWorkbookBaseName(ZipArchive $zip)
2020
    {
2021 51
        $workbookBasename = '';
2022
2023
        // check if it is an OOXML archive
2024 51
        $rels = simplexml_load_string(
2025 51
            $this->securityScanner->scan(
2026 51
                $this->getFromZipArchive($zip, '_rels/.rels')
2027
            ),
2028 51
            'SimpleXMLElement',
2029 51
            Settings::getLibXmlLoaderOptions()
2030
        );
2031 51
        if ($rels !== false) {
2032 51
            foreach ($rels->Relationship as $rel) {
2033 51
                switch ($rel['Type']) {
2034 51
                    case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
2035 51
                        $basename = basename($rel['Target']);
2036 51
                        if (preg_match('/workbook.*\.xml/', $basename)) {
2037 51
                            $workbookBasename = $basename;
2038
                        }
2039
2040 51
                        break;
2041
                }
2042
            }
2043
        }
2044
2045 51
        return $workbookBasename;
2046
    }
2047
}
2048