• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPOffice / PhpSpreadsheet / 17663639262

12 Sep 2025 03:26AM UTC coverage: 95.297% (-0.001%) from 95.298%
17663639262

Pull #4641

github

web-flow
Merge dc11b8ccc into 9b28b9e9c
Pull Request #4641: Proper Output for BASE Function

40307 of 42296 relevant lines covered (95.3%)

348.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.36
/src/PhpSpreadsheet/Reader/Xls/LoadSpreadsheet.php
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
    protected function loadSpreadsheetFromFile2(string $filename, Xls $xls): Spreadsheet
24
    {
25
        // Read the OLE file
26
        $xls->loadOLE($filename);
133✔
27

28
        // Initialisations
29
        $xls->spreadsheet = $this->newSpreadsheet();
133✔
30
        $xls->spreadsheet->setValueBinder($xls->valueBinder);
133✔
31
        $xls->spreadsheet->removeSheetByIndex(0); // remove 1st sheet
133✔
32
        if (!$xls->readDataOnly) {
133✔
33
            $xls->spreadsheet->removeCellStyleXfByIndex(0); // remove the default style
131✔
34
            $xls->spreadsheet->removeCellXfByIndex(0); // remove the default style
131✔
35
        }
36

37
        // Read the summary information stream (containing meta data)
38
        $xls->readSummaryInformation();
133✔
39

40
        // Read the Additional document summary information stream (containing application-specific meta data)
41
        $xls->readDocumentSummaryInformation();
133✔
42

43
        // total byte size of Excel data (workbook global substream + sheet substreams)
44
        $xls->dataSize = strlen($xls->data);
133✔
45

46
        // initialize
47
        $xls->pos = 0;
133✔
48
        $xls->codepage = $xls->codepage ?: CodePage::DEFAULT_CODE_PAGE;
133✔
49
        $xls->formats = [];
133✔
50
        $xls->objFonts = [];
133✔
51
        $xls->palette = [];
133✔
52
        $xls->sheets = [];
133✔
53
        $xls->externalBooks = [];
133✔
54
        $xls->ref = [];
133✔
55
        $xls->definedname = []; //* @phpstan-ignore-line
133✔
56
        $xls->sst = [];
133✔
57
        $xls->drawingGroupData = '';
133✔
58
        $xls->xfIndex = 0;
133✔
59
        $xls->mapCellXfIndex = [];
133✔
60
        $xls->mapCellStyleXfIndex = [];
133✔
61

62
        // Parse Workbook Global Substream
63
        while ($xls->pos < $xls->dataSize) {
133✔
64
            $code = self::getUInt2d($xls->data, $xls->pos);
133✔
65

66
            match ($code) {
67
                self::XLS_TYPE_BOF => $xls->readBof(),
133✔
68
                self::XLS_TYPE_FILEPASS => $xls->readFilepass(),
133✔
69
                self::XLS_TYPE_CODEPAGE => $xls->readCodepage(),
131✔
70
                self::XLS_TYPE_DATEMODE => $xls->readDateMode(),
131✔
71
                self::XLS_TYPE_FONT => $xls->readFont(),
131✔
72
                self::XLS_TYPE_FORMAT => $xls->readFormat(),
131✔
73
                self::XLS_TYPE_XF => $xls->readXf(),
131✔
74
                self::XLS_TYPE_XFEXT => $xls->readXfExt(),
131✔
75
                self::XLS_TYPE_STYLE => $xls->readStyle(),
131✔
76
                self::XLS_TYPE_PALETTE => $xls->readPalette(),
131✔
77
                self::XLS_TYPE_SHEET => $xls->readSheet(),
131✔
78
                self::XLS_TYPE_EXTERNALBOOK => $xls->readExternalBook(),
131✔
79
                self::XLS_TYPE_EXTERNNAME => $xls->readExternName(),
131✔
80
                self::XLS_TYPE_EXTERNSHEET => $xls->readExternSheet(),
131✔
81
                self::XLS_TYPE_DEFINEDNAME => $xls->readDefinedName(),
131✔
82
                self::XLS_TYPE_MSODRAWINGGROUP => $xls->readMsoDrawingGroup(),
131✔
83
                self::XLS_TYPE_SST => $xls->readSst(),
131✔
84
                self::XLS_TYPE_EOF => $xls->readDefault(),
131✔
85
                default => $xls->readDefault(),
131✔
86
            };
87

88
            if ($code === self::XLS_TYPE_EOF) {
133✔
89
                break;
131✔
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
        if (!$xls->readDataOnly) {
131✔
96
            foreach ($xls->objFonts as $objFont) {
129✔
97
                if (isset($objFont->colorIndex)) {
128✔
98
                    $color = Color::map($objFont->colorIndex, $xls->palette, $xls->version);
128✔
99
                    $objFont->getColor()->setRGB($color['rgb']);
128✔
100
                }
101
            }
102

103
            foreach ($xls->spreadsheet->getCellXfCollection() as $objStyle) {
129✔
104
                // fill start and end color
105
                $fill = $objStyle->getFill();
129✔
106

107
                if (isset($fill->startcolorIndex)) {
129✔
108
                    $startColor = Color::map($fill->startcolorIndex, $xls->palette, $xls->version);
129✔
109
                    $fill->getStartColor()->setRGB($startColor['rgb']);
129✔
110
                }
111
                if (isset($fill->endcolorIndex)) {
129✔
112
                    $endColor = Color::map($fill->endcolorIndex, $xls->palette, $xls->version);
129✔
113
                    $fill->getEndColor()->setRGB($endColor['rgb']);
129✔
114
                }
115

116
                // border colors
117
                $top = $objStyle->getBorders()->getTop();
129✔
118
                $right = $objStyle->getBorders()->getRight();
129✔
119
                $bottom = $objStyle->getBorders()->getBottom();
129✔
120
                $left = $objStyle->getBorders()->getLeft();
129✔
121
                $diagonal = $objStyle->getBorders()->getDiagonal();
129✔
122

123
                if (isset($top->colorIndex)) {
129✔
124
                    $borderTopColor = Color::map($top->colorIndex, $xls->palette, $xls->version);
129✔
125
                    $top->getColor()->setRGB($borderTopColor['rgb']);
129✔
126
                }
127
                if (isset($right->colorIndex)) {
129✔
128
                    $borderRightColor = Color::map($right->colorIndex, $xls->palette, $xls->version);
129✔
129
                    $right->getColor()->setRGB($borderRightColor['rgb']);
129✔
130
                }
131
                if (isset($bottom->colorIndex)) {
129✔
132
                    $borderBottomColor = Color::map($bottom->colorIndex, $xls->palette, $xls->version);
129✔
133
                    $bottom->getColor()->setRGB($borderBottomColor['rgb']);
129✔
134
                }
135
                if (isset($left->colorIndex)) {
129✔
136
                    $borderLeftColor = Color::map($left->colorIndex, $xls->palette, $xls->version);
129✔
137
                    $left->getColor()->setRGB($borderLeftColor['rgb']);
129✔
138
                }
139
                if (isset($diagonal->colorIndex)) {
129✔
140
                    $borderDiagonalColor = Color::map($diagonal->colorIndex, $xls->palette, $xls->version);
127✔
141
                    $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
127✔
142
                }
143
            }
144
        }
145

146
        // treat MSODRAWINGGROUP records, workbook-level Escher
147
        $escherWorkbook = null;
131✔
148
        if (!$xls->readDataOnly && $xls->drawingGroupData) {
131✔
149
            $escher = new SharedEscher();
20✔
150
            $reader = new Escher($escher);
20✔
151
            $escherWorkbook = $reader->load($xls->drawingGroupData);
20✔
152
        }
153

154
        // Parse the individual sheets
155
        $xls->activeSheetSet = false;
131✔
156
        $sheetCreated = false;
131✔
157
        foreach ($xls->sheets as $sheet) {
131✔
158
            $selectedCells = '';
131✔
159
            if ($sheet['sheetType'] != 0x00) {
131✔
160
                // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
161
                continue;
×
162
            }
163

164
            // check if sheet should be skipped
165
            if (isset($xls->loadSheetsOnly) && !in_array($sheet['name'], $xls->loadSheetsOnly)) {
131✔
166
                continue;
10✔
167
            }
168

169
            // add sheet to PhpSpreadsheet object
170
            $xls->phpSheet = $xls->spreadsheet->createSheet();
128✔
171
            $sheetCreated = true;
128✔
172
            //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
173
            //        cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
174
            //        name in line with the formula, not the reverse
175
            $xls->phpSheet->setTitle($sheet['name'], false, false);
128✔
176
            $xls->phpSheet->setSheetState($sheet['sheetState']);
128✔
177

178
            $xls->pos = $sheet['offset'];
128✔
179

180
            // Initialize isFitToPages. May change after reading SHEETPR record.
181
            $xls->isFitToPages = false;
128✔
182

183
            // Initialize drawingData
184
            $xls->drawingData = '';
128✔
185

186
            // Initialize objs
187
            $xls->objs = [];
128✔
188

189
            // Initialize shared formula parts
190
            $xls->sharedFormulaParts = [];
128✔
191

192
            // Initialize shared formulas
193
            $xls->sharedFormulas = [];
128✔
194

195
            // Initialize text objs
196
            $xls->textObjects = [];
128✔
197

198
            // Initialize cell annotations
199
            $xls->cellNotes = [];
128✔
200
            $xls->textObjRef = -1;
128✔
201

202
            while ($xls->pos <= $xls->dataSize - 4) {
128✔
203
                $code = self::getUInt2d($xls->data, $xls->pos);
128✔
204

205
                switch ($code) {
206
                    case self::XLS_TYPE_BOF:
128✔
207
                        $xls->readBof();
128✔
208

209
                        break;
128✔
210
                    case self::XLS_TYPE_PRINTGRIDLINES:
128✔
211
                        $xls->readPrintGridlines();
125✔
212

213
                        break;
125✔
214
                    case self::XLS_TYPE_DEFAULTROWHEIGHT:
128✔
215
                        $xls->readDefaultRowHeight();
64✔
216

217
                        break;
64✔
218
                    case self::XLS_TYPE_SHEETPR:
128✔
219
                        $xls->readSheetPr();
127✔
220

221
                        break;
127✔
222
                    case self::XLS_TYPE_HORIZONTALPAGEBREAKS:
128✔
223
                        $xls->readHorizontalPageBreaks();
5✔
224

225
                        break;
5✔
226
                    case self::XLS_TYPE_VERTICALPAGEBREAKS:
128✔
227
                        $xls->readVerticalPageBreaks();
5✔
228

229
                        break;
5✔
230
                    case self::XLS_TYPE_HEADER:
128✔
231
                        $xls->readHeader();
125✔
232

233
                        break;
125✔
234
                    case self::XLS_TYPE_FOOTER:
128✔
235
                        $xls->readFooter();
125✔
236

237
                        break;
125✔
238
                    case self::XLS_TYPE_HCENTER:
128✔
239
                        $xls->readHcenter();
125✔
240

241
                        break;
125✔
242
                    case self::XLS_TYPE_VCENTER:
128✔
243
                        $xls->readVcenter();
125✔
244

245
                        break;
125✔
246
                    case self::XLS_TYPE_LEFTMARGIN:
128✔
247
                        $xls->readLeftMargin();
118✔
248

249
                        break;
118✔
250
                    case self::XLS_TYPE_RIGHTMARGIN:
128✔
251
                        $xls->readRightMargin();
118✔
252

253
                        break;
118✔
254
                    case self::XLS_TYPE_TOPMARGIN:
128✔
255
                        $xls->readTopMargin();
118✔
256

257
                        break;
118✔
258
                    case self::XLS_TYPE_BOTTOMMARGIN:
128✔
259
                        $xls->readBottomMargin();
118✔
260

261
                        break;
118✔
262
                    case self::XLS_TYPE_PAGESETUP:
128✔
263
                        $xls->readPageSetup();
127✔
264

265
                        break;
127✔
266
                    case self::XLS_TYPE_PROTECT:
128✔
267
                        $xls->readProtect();
7✔
268

269
                        break;
7✔
270
                    case self::XLS_TYPE_SCENPROTECT:
128✔
271
                        $xls->readScenProtect();
×
272

273
                        break;
×
274
                    case self::XLS_TYPE_OBJECTPROTECT:
128✔
275
                        $xls->readObjectProtect();
2✔
276

277
                        break;
2✔
278
                    case self::XLS_TYPE_PASSWORD:
128✔
279
                        $xls->readPassword();
3✔
280

281
                        break;
3✔
282
                    case self::XLS_TYPE_DEFCOLWIDTH:
128✔
283
                        $xls->readDefColWidth();
126✔
284

285
                        break;
126✔
286
                    case self::XLS_TYPE_COLINFO:
128✔
287
                        $xls->readColInfo();
113✔
288

289
                        break;
113✔
290
                    case self::XLS_TYPE_DIMENSION:
128✔
291
                        $xls->readDefault();
128✔
292

293
                        break;
128✔
294
                    case self::XLS_TYPE_ROW:
128✔
295
                        $xls->readRow();
72✔
296

297
                        break;
72✔
298
                    case self::XLS_TYPE_DBCELL:
128✔
299
                        $xls->readDefault();
58✔
300

301
                        break;
58✔
302
                    case self::XLS_TYPE_RK:
128✔
303
                        $xls->readRk();
36✔
304

305
                        break;
36✔
306
                    case self::XLS_TYPE_LABELSST:
128✔
307
                        $xls->readLabelSst();
73✔
308

309
                        break;
73✔
310
                    case self::XLS_TYPE_MULRK:
128✔
311
                        $xls->readMulRk();
22✔
312

313
                        break;
22✔
314
                    case self::XLS_TYPE_NUMBER:
128✔
315
                        $xls->readNumber();
59✔
316

317
                        break;
59✔
318
                    case self::XLS_TYPE_FORMULA:
128✔
319
                        $xls->readFormula();
43✔
320

321
                        break;
43✔
322
                    case self::XLS_TYPE_SHAREDFMLA:
128✔
323
                        $xls->readSharedFmla();
1✔
324

325
                        break;
1✔
326
                    case self::XLS_TYPE_BOOLERR:
128✔
327
                        $xls->readBoolErr();
11✔
328

329
                        break;
11✔
330
                    case self::XLS_TYPE_MULBLANK:
128✔
331
                        $xls->readMulBlank();
26✔
332

333
                        break;
26✔
334
                    case self::XLS_TYPE_LABEL:
128✔
335
                        $xls->readLabel();
4✔
336

337
                        break;
4✔
338
                    case self::XLS_TYPE_BLANK:
128✔
339
                        $xls->readBlank();
25✔
340

341
                        break;
25✔
342
                    case self::XLS_TYPE_MSODRAWING:
128✔
343
                        $xls->readMsoDrawing();
17✔
344

345
                        break;
17✔
346
                    case self::XLS_TYPE_OBJ:
128✔
347
                        $xls->readObj();
13✔
348

349
                        break;
13✔
350
                    case self::XLS_TYPE_WINDOW2:
128✔
351
                        $xls->readWindow2();
128✔
352

353
                        break;
128✔
354
                    case self::XLS_TYPE_PAGELAYOUTVIEW:
128✔
355
                        $xls->readPageLayoutView();
113✔
356

357
                        break;
113✔
358
                    case self::XLS_TYPE_SCL:
128✔
359
                        $xls->readScl();
6✔
360

361
                        break;
6✔
362
                    case self::XLS_TYPE_PANE:
128✔
363
                        $xls->readPane();
8✔
364

365
                        break;
8✔
366
                    case self::XLS_TYPE_SELECTION:
128✔
367
                        $selectedCells = $xls->readSelection();
125✔
368

369
                        break;
125✔
370
                    case self::XLS_TYPE_MERGEDCELLS:
128✔
371
                        $xls->readMergedCells();
19✔
372

373
                        break;
19✔
374
                    case self::XLS_TYPE_HYPERLINK:
128✔
375
                        $xls->readHyperLink();
7✔
376

377
                        break;
7✔
378
                    case self::XLS_TYPE_DATAVALIDATIONS:
128✔
379
                        $xls->readDataValidations();
5✔
380

381
                        break;
5✔
382
                    case self::XLS_TYPE_DATAVALIDATION:
128✔
383
                        $xls->readDataValidation();
5✔
384

385
                        break;
5✔
386
                    case self::XLS_TYPE_CFHEADER:
128✔
387
                        /** @var string[] */
388
                        $cellRangeAddresses = $xls->readCFHeader();
24✔
389

390
                        break;
24✔
391
                    case self::XLS_TYPE_CFRULE:
128✔
392
                        $xls->readCFRule($cellRangeAddresses ?? []);
24✔
393

394
                        break;
24✔
395
                    case self::XLS_TYPE_SHEETLAYOUT:
128✔
396
                        $xls->readSheetLayout();
5✔
397

398
                        break;
5✔
399
                    case self::XLS_TYPE_SHEETPROTECTION:
128✔
400
                        $xls->readSheetProtection();
117✔
401

402
                        break;
117✔
403
                    case self::XLS_TYPE_RANGEPROTECTION:
128✔
404
                        $xls->readRangeProtection();
2✔
405

406
                        break;
2✔
407
                    case self::XLS_TYPE_NOTE:
128✔
408
                        $xls->readNote();
3✔
409

410
                        break;
3✔
411
                    case self::XLS_TYPE_TXO:
128✔
412
                        $xls->readTextObject();
2✔
413

414
                        break;
2✔
415
                    case self::XLS_TYPE_CONTINUE:
128✔
416
                        $xls->readContinue();
1✔
417

418
                        break;
1✔
419
                    case self::XLS_TYPE_EOF:
128✔
420
                        $xls->readDefault();
128✔
421

422
                        break 2;
128✔
423
                    default:
424
                        $xls->readDefault();
127✔
425

426
                        break;
127✔
427
                }
428
            }
429

430
            // treat MSODRAWING records, sheet-level Escher
431
            if (!$xls->readDataOnly && $xls->drawingData) {
128✔
432
                $escherWorksheet = new SharedEscher();
17✔
433
                $reader = new Escher($escherWorksheet);
17✔
434
                $escherWorksheet = $reader->load($xls->drawingData);
17✔
435

436
                // get all spContainers in one long array, so they can be mapped to OBJ records
437
                /** @var SpContainer[] $allSpContainers */
438
                $allSpContainers = method_exists($escherWorksheet, 'getDgContainer') ? $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers() : [];
17✔
439
            }
440

441
            // treat OBJ records
442
            foreach ($xls->objs as $n => $obj) {
128✔
443
                // the first shape container never has a corresponding OBJ record, hence $n + 1
444
                if (isset($allSpContainers[$n + 1])) {
12✔
445
                    $spContainer = $allSpContainers[$n + 1];
12✔
446

447
                    // we skip all spContainers that are a part of a group shape since we cannot yet handle those
448
                    if ($spContainer->getNestingLevel() > 1) {
12✔
449
                        continue;
×
450
                    }
451

452
                    // calculate the width and height of the shape
453
                    /** @var int $startRow */
454
                    [$startColumn, $startRow] = Coordinate::coordinateFromString($spContainer->getStartCoordinates());
12✔
455
                    /** @var int $endRow */
456
                    [$endColumn, $endRow] = Coordinate::coordinateFromString($spContainer->getEndCoordinates());
12✔
457

458
                    $startOffsetX = $spContainer->getStartOffsetX();
12✔
459
                    $startOffsetY = $spContainer->getStartOffsetY();
12✔
460
                    $endOffsetX = $spContainer->getEndOffsetX();
12✔
461
                    $endOffsetY = $spContainer->getEndOffsetY();
12✔
462

463
                    $width = SharedXls::getDistanceX($xls->phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
12✔
464
                    $height = SharedXls::getDistanceY($xls->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
12✔
465

466
                    // calculate offsetX and offsetY of the shape
467
                    $offsetX = (int) ($startOffsetX * SharedXls::sizeCol($xls->phpSheet, $startColumn) / 1024);
12✔
468
                    $offsetY = (int) ($startOffsetY * SharedXls::sizeRow($xls->phpSheet, $startRow) / 256);
12✔
469

470
                    /** @var int[] $obj */
471
                    switch ($obj['otObjType']) {
12✔
472
                        case 0x19:
12✔
473
                            // Note
474
                            if (isset($xls->cellNotes[$obj['idObjID']])) {
2✔
475
                                //$cellNote = $xls->cellNotes[$obj['idObjID']];
476

477
                                if (isset($xls->textObjects[$obj['idObjID']])) {
2✔
478
                                    $textObject = $xls->textObjects[$obj['idObjID']];
2✔
479
                                    $xls->cellNotes[$obj['idObjID']]['objTextData'] = $textObject; //* @phpstan-ignore-line
2✔
480
                                }
481
                            }
482

483
                            break;
2✔
484
                        case 0x08:
12✔
485
                            // picture
486
                            // get index to BSE entry (1-based)
487
                            /** @var int */
488
                            $BSEindex = $spContainer->getOPT(0x0104);
12✔
489

490
                            // If there is no BSE Index, we will fail here and other fields are not read.
491
                            // Fix by checking here.
492
                            // TODO: Why is there no BSE Index? Is this a new Office Version? Password protected field?
493
                            // More likely : a uncompatible picture
494
                            if (!$BSEindex) {
12✔
495
                                continue 2;
×
496
                            }
497

498
                            if ($escherWorkbook) {
12✔
499
                                /** @var BSE[] */
500
                                $BSECollection = method_exists($escherWorkbook, 'getDggContainer') ? $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection() : [];
12✔
501
                                $BSE = $BSECollection[$BSEindex - 1];
12✔
502
                                $blipType = $BSE->getBlipType();
12✔
503

504
                                // need check because some blip types are not supported by Escher reader such as EMF
505
                                if ($blip = $BSE->getBlip()) {
12✔
506
                                    $ih = imagecreatefromstring($blip->getData());
12✔
507
                                    if ($ih !== false) {
12✔
508
                                        $drawing = new MemoryDrawing();
12✔
509
                                        $drawing->setImageResource($ih);
12✔
510

511
                                        // width, height, offsetX, offsetY
512
                                        $drawing->setResizeProportional(false);
12✔
513
                                        $drawing->setWidth($width);
12✔
514
                                        $drawing->setHeight($height);
12✔
515
                                        $drawing->setOffsetX($offsetX);
12✔
516
                                        $drawing->setOffsetY($offsetY);
12✔
517

518
                                        switch ($blipType) {
519
                                            case BSE::BLIPTYPE_JPEG:
11✔
520
                                                $drawing->setRenderingFunction(MemoryDrawing::RENDERING_JPEG);
9✔
521
                                                $drawing->setMimeType(MemoryDrawing::MIMETYPE_JPEG);
9✔
522

523
                                                break;
9✔
524
                                            case BSE::BLIPTYPE_PNG:
11✔
525
                                                imagealphablending($ih, false);
12✔
526
                                                imagesavealpha($ih, true);
12✔
527
                                                $drawing->setRenderingFunction(MemoryDrawing::RENDERING_PNG);
12✔
528
                                                $drawing->setMimeType(MemoryDrawing::MIMETYPE_PNG);
12✔
529

530
                                                break;
12✔
531
                                        }
532

533
                                        $drawing->setWorksheet($xls->phpSheet);
12✔
534
                                        $drawing->setCoordinates($spContainer->getStartCoordinates());
12✔
535
                                    }
536
                                }
537
                            }
538

539
                            break;
12✔
540
                        default:
541
                            // other object type
542
                            break;
×
543
                    }
544
                }
545
            }
546

547
            // treat SHAREDFMLA records
548
            if ($xls->version == self::XLS_BIFF8) {
128✔
549
                foreach ($xls->sharedFormulaParts as $cell => $baseCell) {
126✔
550
                    /** @var int $row */
551
                    [$column, $row] = Coordinate::coordinateFromString($cell);
1✔
552
                    /** @var string $baseCell */
553
                    if ($xls->getReadFilter()->readCell($column, $row, $xls->phpSheet->getTitle())) {
1✔
554
                        /** @var string */
555
                        $temp = $xls->sharedFormulas[$baseCell];
1✔
556
                        $formula = $xls->getFormulaFromStructure($temp, $cell);
1✔
557
                        $xls->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA);
1✔
558
                    }
559
                }
560
            }
561

562
            if (!empty($xls->cellNotes)) {
128✔
563
                foreach ($xls->cellNotes as $note => $noteDetails) {
2✔
564
                    /** @var array{author: string, cellRef: string, objTextData?: mixed[]} $noteDetails */
565
                    if (!isset($noteDetails['objTextData'])) {
2✔
566
                        if (isset($xls->textObjects[$note])) {
×
567
                            $textObject = $xls->textObjects[$note];
×
568
                            $noteDetails['objTextData'] = $textObject;
×
569
                        } else {
570
                            $noteDetails['objTextData']['text'] = '';
×
571
                        }
572
                    }
573
                    $cellAddress = str_replace('$', '', $noteDetails['cellRef']);
2✔
574
                    /** @var string */
575
                    $tempDetails = $noteDetails['objTextData']['text'];
2✔
576
                    $xls->phpSheet
2✔
577
                        ->getComment($cellAddress)
2✔
578
                        ->setAuthor($noteDetails['author'])
2✔
579
                        ->setText(
2✔
580
                            $xls->parseRichText($tempDetails)
2✔
581
                        );
2✔
582
                }
583
            }
584
            if ($selectedCells !== '') {
128✔
585
                $xls->phpSheet->setSelectedCells($selectedCells);
123✔
586
            }
587
        }
588
        if ($xls->createBlankSheetIfNoneRead && !$sheetCreated) {
131✔
589
            $xls->spreadsheet->createSheet();
1✔
590
        }
591
        if ($xls->activeSheetSet === false) {
131✔
592
            $xls->spreadsheet->setActiveSheetIndex(0);
7✔
593
        }
594

595
        // add the named ranges (defined names)
596
        foreach ($xls->definedname as $definedName) {
129✔
597
            /** @var array{isBuiltInName: int, name: string, formula: string, scope: int} $definedName */
598
            if ($definedName['isBuiltInName']) {
18✔
599
                switch ($definedName['name']) {
5✔
600
                    case pack('C', 0x06):
5✔
601
                        // print area
602
                        //    in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
603
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
5✔
604

605
                        $extractedRanges = [];
5✔
606
                        $sheetName = '';
5✔
607
                        /** @var non-empty-string $range */
608
                        foreach ($ranges as $range) {
5✔
609
                            // $range should look like one of these
610
                            //        Foo!$C$7:$J$66
611
                            //        Bar!$A$1:$IV$2
612
                            $explodes = Worksheet::extractSheetTitle($range, true, true);
5✔
613
                            $sheetName = (string) $explodes[0];
5✔
614
                            if (!str_contains($explodes[1], ':')) {
5✔
615
                                $explodes[1] = $explodes[1] . ':' . $explodes[1];
×
616
                            }
617
                            $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
5✔
618
                        }
619
                        if ($docSheet = $xls->spreadsheet->getSheetByName($sheetName)) {
5✔
620
                            $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
5✔
621
                        }
622

623
                        break;
5✔
624
                    case pack('C', 0x07):
×
625
                        // print titles (repeating rows)
626
                        // Assuming BIFF8, there are 3 cases
627
                        // 1. repeating rows
628
                        //        formula looks like this: Sheet!$A$1:$IV$2
629
                        //        rows 1-2 repeat
630
                        // 2. repeating columns
631
                        //        formula looks like this: Sheet!$A$1:$B$65536
632
                        //        columns A-B repeat
633
                        // 3. both repeating rows and repeating columns
634
                        //        formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
635
                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
×
636
                        foreach ($ranges as $range) {
×
637
                            // $range should look like this one of these
638
                            //        Sheet!$A$1:$B$65536
639
                            //        Sheet!$A$1:$IV$2
640
                            if (str_contains($range, '!')) {
×
641
                                $explodes = Worksheet::extractSheetTitle($range, true, true);
×
642
                                $docSheet = $xls->spreadsheet->getSheetByName($explodes[0]);
×
643
                                if ($docSheet) {
×
644
                                    $extractedRange = $explodes[1];
×
645
                                    $extractedRange = str_replace('$', '', $extractedRange);
×
646

647
                                    $coordinateStrings = explode(':', $extractedRange);
×
648
                                    if (count($coordinateStrings) == 2) {
×
649
                                        [$firstColumn, $firstRow] = Coordinate::coordinateFromString($coordinateStrings[0]);
×
650
                                        [$lastColumn, $lastRow] = Coordinate::coordinateFromString($coordinateStrings[1]);
×
651
                                        $firstRow = (int) $firstRow;
×
652
                                        $lastRow = (int) $lastRow;
×
653

654
                                        if ($firstColumn == 'A' && $lastColumn == 'IV') {
×
655
                                            // then we have repeating rows
656
                                            $docSheet->getPageSetup()->setRowsToRepeatAtTop([$firstRow, $lastRow]);
×
657
                                        } elseif ($firstRow == 1 && $lastRow == 65536) {
×
658
                                            // then we have repeating columns
659
                                            $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$firstColumn, $lastColumn]);
×
660
                                        }
661
                                    }
662
                                }
663
                            }
664
                        }
665

666
                        break;
×
667
                }
668
            } else {
669
                // Extract range
670
                $formula = $definedName['formula'];
13✔
671
                if (str_contains($formula, '!')) {
13✔
672
                    $explodes = Worksheet::extractSheetTitle($formula, true, true);
8✔
673
                    $docSheet = $xls->spreadsheet->getSheetByName($explodes[0]);
8✔
674
                    if ($docSheet) {
8✔
675
                        $extractedRange = $explodes[1];
7✔
676

677
                        $localOnly = ($definedName['scope'] === 0) ? false : true;
7✔
678

679
                        $scope = ($definedName['scope'] === 0) ? null : $xls->spreadsheet->getSheetByName($xls->sheets[$definedName['scope'] - 1]['name']);
7✔
680

681
                        $xls->spreadsheet->addNamedRange(new NamedRange((string) $definedName['name'], $docSheet, $extractedRange, $localOnly, $scope));
7✔
682
                    }
683
                }
684
                //    Named Value
685
                //    TODO Provide support for named values
686
            }
687
        }
688
        $xls->data = '';
129✔
689

690
        return $xls->spreadsheet;
129✔
691
    }
692
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc