Passed
Push — master ( b504ba...c2a964 )
by
unknown
21:09 queued 09:01
created

LoadSpreadsheet   F

Complexity

Total Complexity 122

Size/Duplication

Total Lines 654
Duplicated Lines 0 %

Test Coverage

Coverage 92.17%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 122
eloc 403
c 1
b 0
f 0
dl 0
loc 654
ccs 365
cts 396
cp 0.9217
rs 2

1 Method

Rating   Name   Duplication   Size   Complexity  
F loadSpreadsheetFromFile2() 0 649 122

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