Failed Conditions
Pull Request — master (#4118)
by Owen
24:41 queued 14:40
created

LoadSpreadsheet   F

Complexity

Total Complexity 124

Size/Duplication

Total Lines 651
Duplicated Lines 0 %

Test Coverage

Coverage 92.86%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 124
eloc 400
c 2
b 0
f 0
dl 0
loc 651
ccs 364
cts 392
cp 0.9286
rs 2

1 Method

Rating   Name   Duplication   Size   Complexity  
F loadSpreadsheetFromFile2() 0 646 124

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
4
5
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
6
use PhpOffice\PhpSpreadsheet\Cell\DataType;
7
use PhpOffice\PhpSpreadsheet\NamedRange;
8
use PhpOffice\PhpSpreadsheet\Reader\Xls;
9
use PhpOffice\PhpSpreadsheet\Shared\CodePage;
10
use PhpOffice\PhpSpreadsheet\Shared\Escher as SharedEscher;
11
use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer\SpContainer;
12
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE;
13
use PhpOffice\PhpSpreadsheet\Shared\Xls as SharedXls;
14
use PhpOffice\PhpSpreadsheet\Spreadsheet;
15
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
16
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
17
18
class LoadSpreadsheet extends Xls
19
{
20
    /**
21
     * Loads PhpSpreadsheet from file.
22
     */
23 113
    protected function loadSpreadsheetFromFile2(string $filename, Xls $xls): Spreadsheet
24
    {
25
        // Read the OLE file
26 113
        $xls->loadOLE($filename);
27
28
        // Initialisations
29 113
        $xls->spreadsheet = new Spreadsheet();
30 113
        $xls->spreadsheet->removeSheetByIndex(0); // remove 1st sheet
31 113
        if (!$xls->readDataOnly) {
32 111
            $xls->spreadsheet->removeCellStyleXfByIndex(0); // remove the default style
33 111
            $xls->spreadsheet->removeCellXfByIndex(0); // remove the default style
34
        }
35
36
        // Read the summary information stream (containing meta data)
37 113
        $xls->readSummaryInformation();
38
39
        // Read the Additional document summary information stream (containing application-specific meta data)
40 113
        $xls->readDocumentSummaryInformation();
41
42
        // total byte size of Excel data (workbook global substream + sheet substreams)
43 113
        $xls->dataSize = strlen($xls->data);
44
45
        // initialize
46 113
        $xls->pos = 0;
47 113
        $xls->codepage = $xls->codepage ?: CodePage::DEFAULT_CODE_PAGE;
48 113
        $xls->formats = [];
49 113
        $xls->objFonts = [];
50 113
        $xls->palette = [];
51 113
        $xls->sheets = [];
52 113
        $xls->externalBooks = [];
53 113
        $xls->ref = [];
54 113
        $xls->definedname = [];
55 113
        $xls->sst = [];
56 113
        $xls->drawingGroupData = '';
57 113
        $xls->xfIndex = 0;
58 113
        $xls->mapCellXfIndex = [];
59 113
        $xls->mapCellStyleXfIndex = [];
60
61
        // Parse Workbook Global Substream
62 113
        while ($xls->pos < $xls->dataSize) {
63 113
            $code = self::getUInt2d($xls->data, $xls->pos);
64
65
            match ($code) {
66 113
                self::XLS_TYPE_BOF => $xls->readBof(),
67 113
                self::XLS_TYPE_FILEPASS => $xls->readFilepass(),
68 113
                self::XLS_TYPE_CODEPAGE => $xls->readCodepage(),
69 113
                self::XLS_TYPE_DATEMODE => $xls->readDateMode(),
70 113
                self::XLS_TYPE_FONT => $xls->readFont(),
71 113
                self::XLS_TYPE_FORMAT => $xls->readFormat(),
72 113
                self::XLS_TYPE_XF => $xls->readXf(),
73 113
                self::XLS_TYPE_XFEXT => $xls->readXfExt(),
74 113
                self::XLS_TYPE_STYLE => $xls->readStyle(),
75 113
                self::XLS_TYPE_PALETTE => $xls->readPalette(),
76 113
                self::XLS_TYPE_SHEET => $xls->readSheet(),
77 113
                self::XLS_TYPE_EXTERNALBOOK => $xls->readExternalBook(),
78 113
                self::XLS_TYPE_EXTERNNAME => $xls->readExternName(),
79 113
                self::XLS_TYPE_EXTERNSHEET => $xls->readExternSheet(),
80 113
                self::XLS_TYPE_DEFINEDNAME => $xls->readDefinedName(),
81 113
                self::XLS_TYPE_MSODRAWINGGROUP => $xls->readMsoDrawingGroup(),
82 113
                self::XLS_TYPE_SST => $xls->readSst(),
83 113
                self::XLS_TYPE_EOF => $xls->readDefault(),
84 113
                default => $xls->readDefault(),
85
            };
86
87 113
            if ($code === self::XLS_TYPE_EOF) {
88 113
                break;
89
            }
90
        }
91
92
        // Resolve indexed colors for font, fill, and border colors
93
        // Cannot be resolved already in XF record, because PALETTE record comes afterwards
94 113
        if (!$xls->readDataOnly) {
95 111
            foreach ($xls->objFonts as $objFont) {
96 110
                if (isset($objFont->colorIndex)) {
97 110
                    $color = Color::map($objFont->colorIndex, $xls->palette, $xls->version);
98 110
                    $objFont->getColor()->setRGB($color['rgb']);
99
                }
100
            }
101
102 111
            foreach ($xls->spreadsheet->getCellXfCollection() as $objStyle) {
103
                // fill start and end color
104 111
                $fill = $objStyle->getFill();
105
106 111
                if (isset($fill->startcolorIndex)) {
107 111
                    $startColor = Color::map($fill->startcolorIndex, $xls->palette, $xls->version);
108 111
                    $fill->getStartColor()->setRGB($startColor['rgb']);
109
                }
110 111
                if (isset($fill->endcolorIndex)) {
111 111
                    $endColor = Color::map($fill->endcolorIndex, $xls->palette, $xls->version);
112 111
                    $fill->getEndColor()->setRGB($endColor['rgb']);
113
                }
114
115
                // border colors
116 111
                $top = $objStyle->getBorders()->getTop();
117 111
                $right = $objStyle->getBorders()->getRight();
118 111
                $bottom = $objStyle->getBorders()->getBottom();
119 111
                $left = $objStyle->getBorders()->getLeft();
120 111
                $diagonal = $objStyle->getBorders()->getDiagonal();
121
122 111
                if (isset($top->colorIndex)) {
123 111
                    $borderTopColor = Color::map($top->colorIndex, $xls->palette, $xls->version);
124 111
                    $top->getColor()->setRGB($borderTopColor['rgb']);
125
                }
126 111
                if (isset($right->colorIndex)) {
127 111
                    $borderRightColor = Color::map($right->colorIndex, $xls->palette, $xls->version);
128 111
                    $right->getColor()->setRGB($borderRightColor['rgb']);
129
                }
130 111
                if (isset($bottom->colorIndex)) {
131 111
                    $borderBottomColor = Color::map($bottom->colorIndex, $xls->palette, $xls->version);
132 111
                    $bottom->getColor()->setRGB($borderBottomColor['rgb']);
133
                }
134 111
                if (isset($left->colorIndex)) {
135 111
                    $borderLeftColor = Color::map($left->colorIndex, $xls->palette, $xls->version);
136 111
                    $left->getColor()->setRGB($borderLeftColor['rgb']);
137
                }
138 111
                if (isset($diagonal->colorIndex)) {
139 109
                    $borderDiagonalColor = Color::map($diagonal->colorIndex, $xls->palette, $xls->version);
140 109
                    $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
141
                }
142
            }
143
        }
144
145
        // treat MSODRAWINGGROUP records, workbook-level Escher
146 113
        $escherWorkbook = null;
147 113
        if (!$xls->readDataOnly && $xls->drawingGroupData) {
148 18
            $escher = new SharedEscher();
149 18
            $reader = new Escher($escher);
150 18
            $escherWorkbook = $reader->load($xls->drawingGroupData);
151
        }
152
153
        // Parse the individual sheets
154 113
        $xls->activeSheetSet = false;
155 113
        foreach ($xls->sheets as $sheet) {
156 113
            $selectedCells = '';
157 113
            if ($sheet['sheetType'] != 0x00) {
158
                // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
159
                continue;
160
            }
161
162
            // check if sheet should be skipped
163 113
            if (isset($xls->loadSheetsOnly) && !in_array($sheet['name'], $xls->loadSheetsOnly)) {
164 8
                continue;
165
            }
166
167
            // add sheet to PhpSpreadsheet object
168 112
            $xls->phpSheet = $xls->spreadsheet->createSheet();
169
            //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
170
            //        cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
171
            //        name in line with the formula, not the reverse
172 112
            $xls->phpSheet->setTitle($sheet['name'], false, false);
173 112
            $xls->phpSheet->setSheetState($sheet['sheetState']);
174
175 112
            $xls->pos = $sheet['offset'];
176
177
            // Initialize isFitToPages. May change after reading SHEETPR record.
178 112
            $xls->isFitToPages = false;
179
180
            // Initialize drawingData
181 112
            $xls->drawingData = '';
182
183
            // Initialize objs
184 112
            $xls->objs = [];
185
186
            // Initialize shared formula parts
187 112
            $xls->sharedFormulaParts = [];
188
189
            // Initialize shared formulas
190 112
            $xls->sharedFormulas = [];
191
192
            // Initialize text objs
193 112
            $xls->textObjects = [];
194
195
            // Initialize cell annotations
196 112
            $xls->cellNotes = [];
197 112
            $xls->textObjRef = -1;
198
199 112
            while ($xls->pos <= $xls->dataSize - 4) {
200 112
                $code = self::getUInt2d($xls->data, $xls->pos);
201
202
                switch ($code) {
203 112
                    case self::XLS_TYPE_BOF:
204 112
                        $xls->readBof();
205
206 112
                        break;
207 112
                    case self::XLS_TYPE_PRINTGRIDLINES:
208 109
                        $xls->readPrintGridlines();
209
210 109
                        break;
211 112
                    case self::XLS_TYPE_DEFAULTROWHEIGHT:
212 61
                        $xls->readDefaultRowHeight();
213
214 61
                        break;
215 112
                    case self::XLS_TYPE_SHEETPR:
216 111
                        $xls->readSheetPr();
217
218 111
                        break;
219 112
                    case self::XLS_TYPE_HORIZONTALPAGEBREAKS:
220 5
                        $xls->readHorizontalPageBreaks();
221
222 5
                        break;
223 112
                    case self::XLS_TYPE_VERTICALPAGEBREAKS:
224 5
                        $xls->readVerticalPageBreaks();
225
226 5
                        break;
227 112
                    case self::XLS_TYPE_HEADER:
228 109
                        $xls->readHeader();
229
230 109
                        break;
231 112
                    case self::XLS_TYPE_FOOTER:
232 109
                        $xls->readFooter();
233
234 109
                        break;
235 112
                    case self::XLS_TYPE_HCENTER:
236 109
                        $xls->readHcenter();
237
238 109
                        break;
239 112
                    case self::XLS_TYPE_VCENTER:
240 109
                        $xls->readVcenter();
241
242 109
                        break;
243 112
                    case self::XLS_TYPE_LEFTMARGIN:
244 104
                        $xls->readLeftMargin();
245
246 104
                        break;
247 112
                    case self::XLS_TYPE_RIGHTMARGIN:
248 104
                        $xls->readRightMargin();
249
250 104
                        break;
251 112
                    case self::XLS_TYPE_TOPMARGIN:
252 104
                        $xls->readTopMargin();
253
254 104
                        break;
255 112
                    case self::XLS_TYPE_BOTTOMMARGIN:
256 104
                        $xls->readBottomMargin();
257
258 104
                        break;
259 112
                    case self::XLS_TYPE_PAGESETUP:
260 111
                        $xls->readPageSetup();
261
262 111
                        break;
263 112
                    case self::XLS_TYPE_PROTECT:
264 7
                        $xls->readProtect();
265
266 7
                        break;
267 112
                    case self::XLS_TYPE_SCENPROTECT:
268
                        $xls->readScenProtect();
269
270
                        break;
271 112
                    case self::XLS_TYPE_OBJECTPROTECT:
272 2
                        $xls->readObjectProtect();
273
274 2
                        break;
275 112
                    case self::XLS_TYPE_PASSWORD:
276 3
                        $xls->readPassword();
277
278 3
                        break;
279 112
                    case self::XLS_TYPE_DEFCOLWIDTH:
280 110
                        $xls->readDefColWidth();
281
282 110
                        break;
283 112
                    case self::XLS_TYPE_COLINFO:
284 100
                        $xls->readColInfo();
285
286 100
                        break;
287 112
                    case self::XLS_TYPE_DIMENSION:
288 112
                        $xls->readDefault();
289
290 112
                        break;
291 112
                    case self::XLS_TYPE_ROW:
292 69
                        $xls->readRow();
293
294 69
                        break;
295 112
                    case self::XLS_TYPE_DBCELL:
296 55
                        $xls->readDefault();
297
298 55
                        break;
299 112
                    case self::XLS_TYPE_RK:
300 35
                        $xls->readRk();
301
302 35
                        break;
303 112
                    case self::XLS_TYPE_LABELSST:
304 61
                        $xls->readLabelSst();
305
306 61
                        break;
307 112
                    case self::XLS_TYPE_MULRK:
308 22
                        $xls->readMulRk();
309
310 22
                        break;
311 112
                    case self::XLS_TYPE_NUMBER:
312 54
                        $xls->readNumber();
313
314 54
                        break;
315 112
                    case self::XLS_TYPE_FORMULA:
316 37
                        $xls->readFormula();
317
318 37
                        break;
319 112
                    case self::XLS_TYPE_SHAREDFMLA:
320 1
                        $xls->readSharedFmla();
321
322 1
                        break;
323 112
                    case self::XLS_TYPE_BOOLERR:
324 11
                        $xls->readBoolErr();
325
326 11
                        break;
327 112
                    case self::XLS_TYPE_MULBLANK:
328 26
                        $xls->readMulBlank();
329
330 26
                        break;
331 112
                    case self::XLS_TYPE_LABEL:
332 4
                        $xls->readLabel();
333
334 4
                        break;
335 112
                    case self::XLS_TYPE_BLANK:
336 25
                        $xls->readBlank();
337
338 25
                        break;
339 112
                    case self::XLS_TYPE_MSODRAWING:
340 17
                        $xls->readMsoDrawing();
341
342 17
                        break;
343 112
                    case self::XLS_TYPE_OBJ:
344 13
                        $xls->readObj();
345
346 13
                        break;
347 112
                    case self::XLS_TYPE_WINDOW2:
348 112
                        $xls->readWindow2();
349
350 112
                        break;
351 112
                    case self::XLS_TYPE_PAGELAYOUTVIEW:
352 99
                        $xls->readPageLayoutView();
353
354 99
                        break;
355 112
                    case self::XLS_TYPE_SCL:
356 6
                        $xls->readScl();
357
358 6
                        break;
359 112
                    case self::XLS_TYPE_PANE:
360 8
                        $xls->readPane();
361
362 8
                        break;
363 112
                    case self::XLS_TYPE_SELECTION:
364 109
                        $selectedCells = $xls->readSelection();
365
366 109
                        break;
367 112
                    case self::XLS_TYPE_MERGEDCELLS:
368 19
                        $xls->readMergedCells();
369
370 19
                        break;
371 112
                    case self::XLS_TYPE_HYPERLINK:
372 6
                        $xls->readHyperLink();
373
374 6
                        break;
375 112
                    case self::XLS_TYPE_DATAVALIDATIONS:
376 3
                        $xls->readDataValidations();
377
378 3
                        break;
379 112
                    case self::XLS_TYPE_DATAVALIDATION:
380 3
                        $xls->readDataValidation();
381
382 3
                        break;
383 112
                    case self::XLS_TYPE_CFHEADER:
384 23
                        $cellRangeAddresses = $xls->readCFHeader();
385
386 23
                        break;
387 112
                    case self::XLS_TYPE_CFRULE:
388 23
                        $xls->readCFRule($cellRangeAddresses ?? []);
389
390 23
                        break;
391 112
                    case self::XLS_TYPE_SHEETLAYOUT:
392 5
                        $xls->readSheetLayout();
393
394 5
                        break;
395 112
                    case self::XLS_TYPE_SHEETPROTECTION:
396 103
                        $xls->readSheetProtection();
397
398 103
                        break;
399 112
                    case self::XLS_TYPE_RANGEPROTECTION:
400 2
                        $xls->readRangeProtection();
401
402 2
                        break;
403 112
                    case self::XLS_TYPE_NOTE:
404 3
                        $xls->readNote();
405
406 3
                        break;
407 112
                    case self::XLS_TYPE_TXO:
408 2
                        $xls->readTextObject();
409
410 2
                        break;
411 112
                    case self::XLS_TYPE_CONTINUE:
412 1
                        $xls->readContinue();
413
414 1
                        break;
415 112
                    case self::XLS_TYPE_EOF:
416 112
                        $xls->readDefault();
417
418 112
                        break 2;
419
                    default:
420 111
                        $xls->readDefault();
421
422 111
                        break;
423
                }
424
            }
425
426
            // treat MSODRAWING records, sheet-level Escher
427 112
            if (!$xls->readDataOnly && $xls->drawingData) {
428 17
                $escherWorksheet = new SharedEscher();
429 17
                $reader = new Escher($escherWorksheet);
430 17
                $escherWorksheet = $reader->load($xls->drawingData);
431
432
                // get all spContainers in one long array, so they can be mapped to OBJ records
433
                /** @var SpContainer[] $allSpContainers */
434 17
                $allSpContainers = method_exists($escherWorksheet, 'getDgContainer') ? $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers() : [];
435
            }
436
437
            // treat OBJ records
438 112
            foreach ($xls->objs as $n => $obj) {
439
                // the first shape container never has a corresponding OBJ record, hence $n + 1
440 12
                if (isset($allSpContainers[$n + 1])) {
441 12
                    $spContainer = $allSpContainers[$n + 1];
442
443
                    // we skip all spContainers that are a part of a group shape since we cannot yet handle those
444 12
                    if ($spContainer->getNestingLevel() > 1) {
445
                        continue;
446
                    }
447
448
                    // calculate the width and height of the shape
449
                    /** @var int $startRow */
450 12
                    [$startColumn, $startRow] = Coordinate::coordinateFromString($spContainer->getStartCoordinates());
451
                    /** @var int $endRow */
452 12
                    [$endColumn, $endRow] = Coordinate::coordinateFromString($spContainer->getEndCoordinates());
453
454 12
                    $startOffsetX = $spContainer->getStartOffsetX();
455 12
                    $startOffsetY = $spContainer->getStartOffsetY();
456 12
                    $endOffsetX = $spContainer->getEndOffsetX();
457 12
                    $endOffsetY = $spContainer->getEndOffsetY();
458
459 12
                    $width = SharedXls::getDistanceX($xls->phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
460 12
                    $height = SharedXls::getDistanceY($xls->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
461
462
                    // calculate offsetX and offsetY of the shape
463 12
                    $offsetX = (int) ($startOffsetX * SharedXls::sizeCol($xls->phpSheet, $startColumn) / 1024);
464 12
                    $offsetY = (int) ($startOffsetY * SharedXls::sizeRow($xls->phpSheet, $startRow) / 256);
465
466 12
                    switch ($obj['otObjType']) {
467 12
                        case 0x19:
468
                            // Note
469 2
                            if (isset($xls->cellNotes[$obj['idObjID']])) {
470
                                //$cellNote = $xls->cellNotes[$obj['idObjID']];
471
472 2
                                if (isset($xls->textObjects[$obj['idObjID']])) {
473 2
                                    $textObject = $xls->textObjects[$obj['idObjID']];
474 2
                                    $xls->cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
475
                                }
476
                            }
477
478 2
                            break;
479 12
                        case 0x08:
480
                            // picture
481
                            // get index to BSE entry (1-based)
482 12
                            $BSEindex = $spContainer->getOPT(0x0104);
483
484
                            // If there is no BSE Index, we will fail here and other fields are not read.
485
                            // Fix by checking here.
486
                            // TODO: Why is there no BSE Index? Is this a new Office Version? Password protected field?
487
                            // More likely : a uncompatible picture
488 12
                            if (!$BSEindex) {
489
                                continue 2;
490
                            }
491
492 12
                            if ($escherWorkbook) {
493 12
                                $BSECollection = method_exists($escherWorkbook, 'getDggContainer') ? $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection() : [];
494 12
                                $BSE = $BSECollection[$BSEindex - 1];
495 12
                                $blipType = $BSE->getBlipType();
496
497
                                // need check because some blip types are not supported by Escher reader such as EMF
498 12
                                if ($blip = $BSE->getBlip()) {
499 12
                                    $ih = imagecreatefromstring($blip->getData());
500 12
                                    if ($ih !== false) {
501 12
                                        $drawing = new MemoryDrawing();
502 12
                                        $drawing->setImageResource($ih);
503
504
                                        // width, height, offsetX, offsetY
505 12
                                        $drawing->setResizeProportional(false);
506 12
                                        $drawing->setWidth($width);
507 12
                                        $drawing->setHeight($height);
508 12
                                        $drawing->setOffsetX($offsetX);
509 12
                                        $drawing->setOffsetY($offsetY);
510
511
                                        switch ($blipType) {
512 11
                                            case BSE::BLIPTYPE_JPEG:
513 9
                                                $drawing->setRenderingFunction(MemoryDrawing::RENDERING_JPEG);
514 9
                                                $drawing->setMimeType(MemoryDrawing::MIMETYPE_JPEG);
515
516 9
                                                break;
517 11
                                            case BSE::BLIPTYPE_PNG:
518 12
                                                imagealphablending($ih, false);
519 12
                                                imagesavealpha($ih, true);
520 12
                                                $drawing->setRenderingFunction(MemoryDrawing::RENDERING_PNG);
521 12
                                                $drawing->setMimeType(MemoryDrawing::MIMETYPE_PNG);
522
523 12
                                                break;
524
                                        }
525
526 12
                                        $drawing->setWorksheet($xls->phpSheet);
527 12
                                        $drawing->setCoordinates($spContainer->getStartCoordinates());
528
                                    }
529
                                }
530
                            }
531
532 12
                            break;
533
                        default:
534
                            // other object type
535
                            break;
536
                    }
537
                }
538
            }
539
540
            // treat SHAREDFMLA records
541 112
            if ($xls->version == self::XLS_BIFF8) {
542 110
                foreach ($xls->sharedFormulaParts as $cell => $baseCell) {
543
                    /** @var int $row */
544 1
                    [$column, $row] = Coordinate::coordinateFromString($cell);
545 1
                    if (($xls->getReadFilter() !== null) && $xls->getReadFilter()->readCell($column, $row, $xls->phpSheet->getTitle())) {
546 1
                        $formula = $xls->getFormulaFromStructure($xls->sharedFormulas[$baseCell], $cell);
547 1
                        $xls->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA);
548
                    }
549
                }
550
            }
551
552 112
            if (!empty($xls->cellNotes)) {
553 2
                foreach ($xls->cellNotes as $note => $noteDetails) {
554 2
                    if (!isset($noteDetails['objTextData'])) {
555
                        if (isset($xls->textObjects[$note])) {
556
                            $textObject = $xls->textObjects[$note];
557
                            $noteDetails['objTextData'] = $textObject;
558
                        } else {
559
                            $noteDetails['objTextData']['text'] = '';
560
                        }
561
                    }
562 2
                    $cellAddress = str_replace('$', '', $noteDetails['cellRef']);
563 2
                    $xls->phpSheet->getComment($cellAddress)->setAuthor($noteDetails['author'])->setText($xls->parseRichText($noteDetails['objTextData']['text']));
564
                }
565
            }
566 112
            if ($selectedCells !== '') {
567 107
                $xls->phpSheet->setSelectedCells($selectedCells);
568
            }
569
        }
570 113
        if ($xls->activeSheetSet === false) {
571 5
            $xls->spreadsheet->setActiveSheetIndex(0);
572
        }
573
574
        // add the named ranges (defined names)
575 112
        foreach ($xls->definedname as $definedName) {
576 15
            if ($definedName['isBuiltInName']) {
577 5
                switch ($definedName['name']) {
578 5
                    case pack('C', 0x06):
579
                        // print area
580
                        //    in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
581 5
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
582
583 5
                        $extractedRanges = [];
584 5
                        $sheetName = '';
585
                        /** @var non-empty-string $range */
586 5
                        foreach ($ranges as $range) {
587
                            // $range should look like one of these
588
                            //        Foo!$C$7:$J$66
589
                            //        Bar!$A$1:$IV$2
590 5
                            $explodes = Worksheet::extractSheetTitle($range, true);
591 5
                            $sheetName = trim($explodes[0], "'");
592 5
                            if (!str_contains($explodes[1], ':')) {
593
                                $explodes[1] = $explodes[1] . ':' . $explodes[1];
594
                            }
595 5
                            $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
596
                        }
597 5
                        if ($docSheet = $xls->spreadsheet->getSheetByName($sheetName)) {
598 5
                            $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
599
                        }
600
601 5
                        break;
602
                    case pack('C', 0x07):
603
                        // print titles (repeating rows)
604
                        // Assuming BIFF8, there are 3 cases
605
                        // 1. repeating rows
606
                        //        formula looks like this: Sheet!$A$1:$IV$2
607
                        //        rows 1-2 repeat
608
                        // 2. repeating columns
609
                        //        formula looks like this: Sheet!$A$1:$B$65536
610
                        //        columns A-B repeat
611
                        // 3. both repeating rows and repeating columns
612
                        //        formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
613
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
614
                        foreach ($ranges as $range) {
615
                            // $range should look like this one of these
616
                            //        Sheet!$A$1:$B$65536
617
                            //        Sheet!$A$1:$IV$2
618
                            if (str_contains($range, '!')) {
619
                                $explodes = Worksheet::extractSheetTitle($range, true);
620
                                if ($docSheet = $xls->spreadsheet->getSheetByName($explodes[0])) {
621
                                    $extractedRange = $explodes[1];
622
                                    $extractedRange = str_replace('$', '', $extractedRange);
623
624
                                    $coordinateStrings = explode(':', $extractedRange);
625
                                    if (count($coordinateStrings) == 2) {
626
                                        [$firstColumn, $firstRow] = Coordinate::coordinateFromString($coordinateStrings[0]);
627
                                        [$lastColumn, $lastRow] = Coordinate::coordinateFromString($coordinateStrings[1]);
628
629
                                        if ($firstColumn == 'A' && $lastColumn == 'IV') {
630
                                            // then we have repeating rows
631
                                            $docSheet->getPageSetup()->setRowsToRepeatAtTop([$firstRow, $lastRow]);
632
                                        } elseif ($firstRow == 1 && $lastRow == 65536) {
633
                                            // then we have repeating columns
634
                                            $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$firstColumn, $lastColumn]);
635
                                        }
636
                                    }
637
                                }
638
                            }
639
                        }
640
641
                        break;
642
                }
643
            } else {
644
                // Extract range
645
                /** @var non-empty-string $formula */
646 10
                $formula = $definedName['formula'];
647 10
                if (str_contains($formula, '!')) {
648 5
                    $explodes = Worksheet::extractSheetTitle($formula, true);
649
                    if (
650 5
                        ($docSheet = $xls->spreadsheet->getSheetByName($explodes[0]))
651 5
                        || ($docSheet = $xls->spreadsheet->getSheetByName(trim($explodes[0], "'")))
652
                    ) {
653 5
                        $extractedRange = $explodes[1];
654
655 5
                        $localOnly = ($definedName['scope'] === 0) ? false : true;
656
657 5
                        $scope = ($definedName['scope'] === 0) ? null : $xls->spreadsheet->getSheetByName($xls->sheets[$definedName['scope'] - 1]['name']);
658
659 5
                        $xls->spreadsheet->addNamedRange(new NamedRange((string) $definedName['name'], $docSheet, $extractedRange, $localOnly, $scope));
660
                    }
661
                }
662
                //    Named Value
663
                //    TODO Provide support for named values
664
            }
665
        }
666 112
        $xls->data = '';
667
668 112
        return $xls->spreadsheet;
669
    }
670
}
671