LoadSpreadsheet::loadSpreadsheetFromFile2()   F
last analyzed

Complexity

Conditions 122
Paths > 20000

Size

Total Lines 662
Code Lines 408

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 372
CRAP Score 128.7685

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 408
c 1
b 0
f 0
dl 0
loc 662
ccs 372
cts 403
cp 0.9231
rs 0
cc 122
nc 25283664
nop 2
crap 128.7685

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