Completed
Push — develop ( 08525a...2922a1 )
by Adrien
22:42
created

HTML::extendRowsForChartsAndImages()   C

Complexity

Conditions 13
Paths 56

Size

Total Lines 50
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 34
c 1
b 0
f 0
nc 56
nop 2
dl 0
loc 50
rs 5.3808
ccs 0
cts 48
cp 0
crap 182

How to fix   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 PhpSpreadsheet\Writer;
4
5
use PhpSpreadsheet\Calculation;
6
use PhpSpreadsheet\Shared\Font;
7
use PhpSpreadsheet\Shared\StringHelper;
8
use PhpSpreadsheet\Spreadsheet;
9
10
/**
11
 * Copyright (c) 2006 - 2015 Spreadsheet
12
 *
13
 * This library is free software; you can redistribute it and/or
14
 * modify it under the terms of the GNU Lesser General Public
15
 * License as published by the Free Software Foundation; either
16
 * version 2.1 of the License, or (at your option) any later version.
17
 *
18
 * This library is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21
 * Lesser General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Lesser General Public
24
 * License along with this library; if not, write to the Free Software
25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26
 *
27
 * @category   Spreadsheet
28
 * @copyright  Copyright (c) 2006 - 2015 Spreadsheet (https://github.com/PHPOffice/Spreadsheet)
29
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
30
 * @version    ##VERSION##, ##DATE##
31
 */
32
class HTML extends BaseWriter implements IWriter
33
{
34
    /**
35
     * Spreadsheet object
36
     *
37
     * @var Spreadsheet
38
     */
39
    protected $spreadsheet;
40
41
    /**
42
     * Sheet index to write
43
     *
44
     * @var int
45
     */
46
    private $sheetIndex = 0;
47
48
    /**
49
     * Images root
50
     *
51
     * @var string
52
     */
53
    private $imagesRoot = '.';
54
55
    /**
56
     * embed images, or link to images
57
     *
58
     * @var bool
59
     */
60
    private $embedImages = false;
61
62
    /**
63
     * Use inline CSS?
64
     *
65
     * @var bool
66
     */
67
    private $useInlineCss = false;
68
69
    /**
70
     * Array of CSS styles
71
     *
72
     * @var array
73
     */
74
    private $cssStyles;
75
76
    /**
77
     * Array of column widths in points
78
     *
79
     * @var array
80
     */
81
    private $columnWidths;
82
83
    /**
84
     * Default font
85
     *
86
     * @var \PhpSpreadsheet\Style\Font
87
     */
88
    private $defaultFont;
89
90
    /**
91
     * Flag whether spans have been calculated
92
     *
93
     * @var bool
94
     */
95
    private $spansAreCalculated = false;
96
97
    /**
98
     * Excel cells that should not be written as HTML cells
99
     *
100
     * @var array
101
     */
102
    private $isSpannedCell = [];
103
104
    /**
105
     * Excel cells that are upper-left corner in a cell merge
106
     *
107
     * @var array
108
     */
109
    private $isBaseCell = [];
110
111
    /**
112
     * Excel rows that should not be written as HTML rows
113
     *
114
     * @var array
115
     */
116
    private $isSpannedRow = [];
117
118
    /**
119
     * Is the current writer creating PDF?
120
     *
121
     * @var bool
122
     */
123
    protected $isPdf = false;
124
125
    /**
126
     * Generate the Navigation block
127
     *
128
     * @var bool
129
     */
130
    private $generateSheetNavigationBlock = true;
131
132
    /**
133
     * Create a new HTML
134
     *
135
     * @param    \PhpSpreadsheet\Spreadsheet    $spreadsheet
136
     */
137
    public function __construct(Spreadsheet $spreadsheet)
138
    {
139
        $this->spreadsheet = $spreadsheet;
140
        $this->defaultFont = $this->spreadsheet->getDefaultStyle()->getFont();
141
    }
142
143
    /**
144
     * Save Spreadsheet to file
145
     *
146
     * @param    string        $pFilename
147
     * @throws    \PhpSpreadsheet\Writer\Exception
148
     */
149
    public function save($pFilename = null)
150
    {
151
        // garbage collect
152
        $this->spreadsheet->garbageCollect();
153
154
        $saveDebugLog = Calculation::getInstance($this->spreadsheet)->getDebugLog()->getWriteDebugLog();
155
        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog(false);
156
        $saveArrayReturnType = Calculation::getArrayReturnType();
157
        Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE);
158
159
        // Build CSS
160
        $this->buildCSS(!$this->useInlineCss);
161
162
        // Open file
163
        $fileHandle = fopen($pFilename, 'wb+');
164
        if ($fileHandle === false) {
165
            throw new \PhpSpreadsheet\Writer\Exception("Could not open file $pFilename for writing.");
166
        }
167
168
        // Write headers
169
        fwrite($fileHandle, $this->generateHTMLHeader(!$this->useInlineCss));
170
171
        // Write navigation (tabs)
172
        if ((!$this->isPdf) && ($this->generateSheetNavigationBlock)) {
173
            fwrite($fileHandle, $this->generateNavigation());
174
        }
175
176
        // Write data
177
        fwrite($fileHandle, $this->generateSheetData());
178
179
        // Write footer
180
        fwrite($fileHandle, $this->generateHTMLFooter());
181
182
        // Close file
183
        fclose($fileHandle);
184
185
        Calculation::setArrayReturnType($saveArrayReturnType);
186
        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
187
    }
188
189
    /**
190
     * Map VAlign
191
     *
192
     * @param    string        $vAlign        Vertical alignment
193
     * @return string
194
     */
195
    private function mapVAlign($vAlign)
196
    {
197
        switch ($vAlign) {
198
            case \PhpSpreadsheet\Style\Alignment::VERTICAL_BOTTOM:
199
                return 'bottom';
200
            case \PhpSpreadsheet\Style\Alignment::VERTICAL_TOP:
201
                return 'top';
202
            case \PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER:
203
            case \PhpSpreadsheet\Style\Alignment::VERTICAL_JUSTIFY:
204
                return 'middle';
205
            default:
206
                return 'baseline';
207
        }
208
    }
209
210
    /**
211
     * Map HAlign
212
     *
213
     * @param    string        $hAlign        Horizontal alignment
214
     * @return string|false
215
     */
216
    private function mapHAlign($hAlign)
217
    {
218
        switch ($hAlign) {
219
            case \PhpSpreadsheet\Style\Alignment::HORIZONTAL_GENERAL:
220
                return false;
221
            case \PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT:
222
                return 'left';
223
            case \PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT:
224
                return 'right';
225
            case \PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER:
226
            case \PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER_CONTINUOUS:
227
                return 'center';
228
            case \PhpSpreadsheet\Style\Alignment::HORIZONTAL_JUSTIFY:
229
                return 'justify';
230
            default:
231
                return false;
232
        }
233
    }
234
235
    /**
236
     * Map border style
237
     *
238
     * @param    int        $borderStyle        Sheet index
239
     * @return    string
240
     */
241
    private function mapBorderStyle($borderStyle)
242
    {
243
        switch ($borderStyle) {
244
            case \PhpSpreadsheet\Style\Border::BORDER_NONE:
245
                return 'none';
246
            case \PhpSpreadsheet\Style\Border::BORDER_DASHDOT:
247
                return '1px dashed';
248
            case \PhpSpreadsheet\Style\Border::BORDER_DASHDOTDOT:
249
                return '1px dotted';
250
            case \PhpSpreadsheet\Style\Border::BORDER_DASHED:
251
                return '1px dashed';
252
            case \PhpSpreadsheet\Style\Border::BORDER_DOTTED:
253
                return '1px dotted';
254
            case \PhpSpreadsheet\Style\Border::BORDER_DOUBLE:
255
                return '3px double';
256
            case \PhpSpreadsheet\Style\Border::BORDER_HAIR:
257
                return '1px solid';
258
            case \PhpSpreadsheet\Style\Border::BORDER_MEDIUM:
259
                return '2px solid';
260
            case \PhpSpreadsheet\Style\Border::BORDER_MEDIUMDASHDOT:
261
                return '2px dashed';
262
            case \PhpSpreadsheet\Style\Border::BORDER_MEDIUMDASHDOTDOT:
263
                return '2px dotted';
264
            case \PhpSpreadsheet\Style\Border::BORDER_MEDIUMDASHED:
265
                return '2px dashed';
266
            case \PhpSpreadsheet\Style\Border::BORDER_SLANTDASHDOT:
267
                return '2px dashed';
268
            case \PhpSpreadsheet\Style\Border::BORDER_THICK:
269
                return '3px solid';
270
            case \PhpSpreadsheet\Style\Border::BORDER_THIN:
271
                return '1px solid';
272
            default:
273
                // map others to thin
274
                return '1px solid';
275
        }
276
    }
277
278
    /**
279
     * Get sheet index
280
     *
281
     * @return int
282
     */
283
    public function getSheetIndex()
284
    {
285
        return $this->sheetIndex;
286
    }
287
288
    /**
289
     * Set sheet index
290
     *
291
     * @param    int        $pValue        Sheet index
292
     * @return HTML
293
     */
294
    public function setSheetIndex($pValue = 0)
295
    {
296
        $this->sheetIndex = $pValue;
297
298
        return $this;
299
    }
300
301
    /**
302
     * Get sheet index
303
     *
304
     * @return bool
305
     */
306
    public function getGenerateSheetNavigationBlock()
307
    {
308
        return $this->generateSheetNavigationBlock;
309
    }
310
311
    /**
312
     * Set sheet index
313
     *
314
     * @param    bool        $pValue        Flag indicating whether the sheet navigation block should be generated or not
315
     * @return HTML
316
     */
317
    public function setGenerateSheetNavigationBlock($pValue = true)
318
    {
319
        $this->generateSheetNavigationBlock = (bool) $pValue;
320
321
        return $this;
322
    }
323
324
    /**
325
     * Write all sheets (resets sheetIndex to NULL)
326
     */
327
    public function writeAllSheets()
328
    {
329
        $this->sheetIndex = null;
330
331
        return $this;
332
    }
333
334
    /**
335
     * Generate HTML header
336
     *
337
     * @param    bool        $pIncludeStyles        Include styles?
338
     * @throws \PhpSpreadsheet\Writer\Exception
339
     * @return    string
340
     */
341
    public function generateHTMLHeader($pIncludeStyles = false)
342
    {
343
        // Spreadsheet object known?
344
        if (is_null($this->spreadsheet)) {
345
            throw new \PhpSpreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
346
        }
347
348
        // Construct HTML
349
        $properties = $this->spreadsheet->getProperties();
350
        $html = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' . PHP_EOL;
351
        $html .= '<!-- Generated by Spreadsheet - https://github.com/PHPOffice/Spreadsheet -->' . PHP_EOL;
352
        $html .= '<html>' . PHP_EOL;
353
        $html .= '  <head>' . PHP_EOL;
354
        $html .= '      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . PHP_EOL;
355
        if ($properties->getTitle() > '') {
356
            $html .= '      <title>' . htmlspecialchars($properties->getTitle()) . '</title>' . PHP_EOL;
357
        }
358
        if ($properties->getCreator() > '') {
359
            $html .= '      <meta name="author" content="' . htmlspecialchars($properties->getCreator()) . '" />' . PHP_EOL;
360
        }
361
        if ($properties->getTitle() > '') {
362
            $html .= '      <meta name="title" content="' . htmlspecialchars($properties->getTitle()) . '" />' . PHP_EOL;
363
        }
364 View Code Duplication
        if ($properties->getDescription() > '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
365
            $html .= '      <meta name="description" content="' . htmlspecialchars($properties->getDescription()) . '" />' . PHP_EOL;
366
        }
367
        if ($properties->getSubject() > '') {
368
            $html .= '      <meta name="subject" content="' . htmlspecialchars($properties->getSubject()) . '" />' . PHP_EOL;
369
        }
370
        if ($properties->getKeywords() > '') {
371
            $html .= '      <meta name="keywords" content="' . htmlspecialchars($properties->getKeywords()) . '" />' . PHP_EOL;
372
        }
373
        if ($properties->getCategory() > '') {
374
            $html .= '      <meta name="category" content="' . htmlspecialchars($properties->getCategory()) . '" />' . PHP_EOL;
375
        }
376 View Code Duplication
        if ($properties->getCompany() > '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
377
            $html .= '      <meta name="company" content="' . htmlspecialchars($properties->getCompany()) . '" />' . PHP_EOL;
378
        }
379 View Code Duplication
        if ($properties->getManager() > '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
380
            $html .= '      <meta name="manager" content="' . htmlspecialchars($properties->getManager()) . '" />' . PHP_EOL;
381
        }
382
383
        if ($pIncludeStyles) {
384
            $html .= $this->generateStyles(true);
385
        }
386
387
        $html .= '  </head>' . PHP_EOL;
388
        $html .= '' . PHP_EOL;
389
        $html .= '  <body>' . PHP_EOL;
390
391
        return $html;
392
    }
393
394
    /**
395
     * Generate sheet data
396
     *
397
     * @throws \PhpSpreadsheet\Writer\Exception
398
     * @return    string
399
     */
400
    public function generateSheetData()
401
    {
402
        // Spreadsheet object known?
403
        if (is_null($this->spreadsheet)) {
404
            throw new \PhpSpreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
405
        }
406
407
        // Ensure that Spans have been calculated?
408
        if ($this->sheetIndex !== null || !$this->spansAreCalculated) {
409
            $this->calculateSpans();
410
        }
411
412
        // Fetch sheets
413
        $sheets = [];
414 View Code Duplication
        if (is_null($this->sheetIndex)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
415
            $sheets = $this->spreadsheet->getAllSheets();
416
        } else {
417
            $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex);
418
        }
419
420
        // Construct HTML
421
        $html = '';
422
423
        // Loop all sheets
424
        $sheetId = 0;
425
        foreach ($sheets as $sheet) {
426
            // Write table header
427
            $html .= $this->generateTableHeader($sheet);
428
429
            // Get worksheet dimension
430
            $dimension = explode(':', $sheet->calculateWorksheetDimension());
431
            $dimension[0] = \PhpSpreadsheet\Cell::coordinateFromString($dimension[0]);
432
            $dimension[0][0] = \PhpSpreadsheet\Cell::columnIndexFromString($dimension[0][0]) - 1;
433
            $dimension[1] = \PhpSpreadsheet\Cell::coordinateFromString($dimension[1]);
434
            $dimension[1][0] = \PhpSpreadsheet\Cell::columnIndexFromString($dimension[1][0]) - 1;
435
436
            // row min,max
437
            $rowMin = $dimension[0][1];
438
            $rowMax = $dimension[1][1];
439
440
            // calculate start of <tbody>, <thead>
441
            $tbodyStart = $rowMin;
442
            $theadStart = $theadEnd = 0; // default: no <thead>    no </thead>
443
            if ($sheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
444
                $rowsToRepeatAtTop = $sheet->getPageSetup()->getRowsToRepeatAtTop();
445
446
                // we can only support repeating rows that start at top row
447
                if ($rowsToRepeatAtTop[0] == 1) {
448
                    $theadStart = $rowsToRepeatAtTop[0];
449
                    $theadEnd = $rowsToRepeatAtTop[1];
450
                    $tbodyStart = $rowsToRepeatAtTop[1] + 1;
451
                }
452
            }
453
454
            // Loop through cells
455
            $row = $rowMin - 1;
456
            while ($row++ < $rowMax) {
457
                // <thead> ?
458
                if ($row == $theadStart) {
459
                    $html .= '        <thead>' . PHP_EOL;
460
                    $cellType = 'th';
461
                }
462
463
                // <tbody> ?
464
                if ($row == $tbodyStart) {
465
                    $html .= '        <tbody>' . PHP_EOL;
466
                    $cellType = 'td';
467
                }
468
469
                // Write row if there are HTML table cells in it
470
                if (!isset($this->isSpannedRow[$sheet->getParent()->getIndex($sheet)][$row])) {
471
                    // Start a new rowData
472
                    $rowData = [];
473
                    // Loop through columns
474
                    $column = $dimension[0][0] - 1;
475
                    while ($column++ < $dimension[1][0]) {
476
                        // Cell exists?
477
                        if ($sheet->cellExistsByColumnAndRow($column, $row)) {
478
                            $rowData[$column] = \PhpSpreadsheet\Cell::stringFromColumnIndex($column) . $row;
479
                        } else {
480
                            $rowData[$column] = '';
481
                        }
482
                    }
483
                    $html .= $this->generateRow($sheet, $rowData, $row - 1, $cellType);
0 ignored issues
show
Bug introduced by
The variable $cellType does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
484
                }
485
486
                // </thead> ?
487
                if ($row == $theadEnd) {
488
                    $html .= '        </thead>' . PHP_EOL;
489
                }
490
            }
491
            $html .= $this->extendRowsForChartsAndImages($sheet, $row);
492
493
            // Close table body.
494
            $html .= '        </tbody>' . PHP_EOL;
495
496
            // Write table footer
497
            $html .= $this->generateTableFooter();
498
499
            // Writing PDF?
500
            if ($this->isPdf) {
501
                if (is_null($this->sheetIndex) && $sheetId + 1 < $this->spreadsheet->getSheetCount()) {
502
                    $html .= '<div style="page-break-before:always" />';
503
                }
504
            }
505
506
            // Next sheet
507
            ++$sheetId;
508
        }
509
510
        return $html;
511
    }
512
513
    /**
514
     * Generate sheet tabs
515
     *
516
     * @throws \PhpSpreadsheet\Writer\Exception
517
     * @return    string
518
     */
519
    public function generateNavigation()
520
    {
521
        // Spreadsheet object known?
522
        if (is_null($this->spreadsheet)) {
523
            throw new \PhpSpreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
524
        }
525
526
        // Fetch sheets
527
        $sheets = [];
528 View Code Duplication
        if (is_null($this->sheetIndex)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
529
            $sheets = $this->spreadsheet->getAllSheets();
530
        } else {
531
            $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex);
532
        }
533
534
        // Construct HTML
535
        $html = '';
536
537
        // Only if there are more than 1 sheets
538
        if (count($sheets) > 1) {
539
            // Loop all sheets
540
            $sheetId = 0;
541
542
            $html .= '<ul class="navigation">' . PHP_EOL;
543
544
            foreach ($sheets as $sheet) {
545
                $html .= '  <li class="sheet' . $sheetId . '"><a href="#sheet' . $sheetId . '">' . $sheet->getTitle() . '</a></li>' . PHP_EOL;
546
                ++$sheetId;
547
            }
548
549
            $html .= '</ul>' . PHP_EOL;
550
        }
551
552
        return $html;
553
    }
554
555
    private function extendRowsForChartsAndImages(\PhpSpreadsheet\Worksheet $pSheet, $row)
556
    {
557
        $rowMax = $row;
558
        $colMax = 'A';
559
        if ($this->includeCharts) {
560
            foreach ($pSheet->getChartCollection() as $chart) {
561
                if ($chart instanceof \PhpSpreadsheet\Chart) {
562
                    $chartCoordinates = $chart->getTopLeftPosition();
563
                    $chartTL = \PhpSpreadsheet\Cell::coordinateFromString($chartCoordinates['cell']);
564
                    $chartCol = \PhpSpreadsheet\Cell::columnIndexFromString($chartTL[0]);
565
                    if ($chartTL[1] > $rowMax) {
566
                        $rowMax = $chartTL[1];
567
                        if ($chartCol > \PhpSpreadsheet\Cell::columnIndexFromString($colMax)) {
568
                            $colMax = $chartTL[0];
569
                        }
570
                    }
571
                }
572
            }
573
        }
574
575
        foreach ($pSheet->getDrawingCollection() as $drawing) {
576
            if ($drawing instanceof \PhpSpreadsheet\Worksheet\Drawing) {
577
                $imageTL = \PhpSpreadsheet\Cell::coordinateFromString($drawing->getCoordinates());
578
                $imageCol = \PhpSpreadsheet\Cell::columnIndexFromString($imageTL[0]);
579
                if ($imageTL[1] > $rowMax) {
580
                    $rowMax = $imageTL[1];
581
                    if ($imageCol > \PhpSpreadsheet\Cell::columnIndexFromString($colMax)) {
582
                        $colMax = $imageTL[0];
583
                    }
584
                }
585
            }
586
        }
587
        $html = '';
588
        ++$colMax;
589
        while ($row <= $rowMax) {
590
            $html .= '<tr>';
591
            for ($col = 'A'; $col != $colMax; ++$col) {
592
                $html .= '<td>';
593
                $html .= $this->writeImageInCell($pSheet, $col . $row);
594
                if ($this->includeCharts) {
595
                    $html .= $this->writeChartInCell($pSheet, $col . $row);
596
                }
597
                $html .= '</td>';
598
            }
599
            ++$row;
600
            $html .= '</tr>';
601
        }
602
603
        return $html;
604
    }
605
606
    /**
607
     * Generate image tag in cell
608
     *
609
     * @param    \PhpSpreadsheet\Worksheet    $pSheet            \PhpSpreadsheet\Worksheet
610
     * @param    string                $coordinates    Cell coordinates
611
     * @throws    \PhpSpreadsheet\Writer\Exception
612
     * @return    string
613
     */
614
    private function writeImageInCell(\PhpSpreadsheet\Worksheet $pSheet, $coordinates)
615
    {
616
        // Construct HTML
617
        $html = '';
618
619
        // Write images
620
        foreach ($pSheet->getDrawingCollection() as $drawing) {
621
            if ($drawing instanceof \PhpSpreadsheet\Worksheet\Drawing) {
622
                if ($drawing->getCoordinates() == $coordinates) {
623
                    $filename = $drawing->getPath();
624
625
                    // Strip off eventual '.'
626
                    if (substr($filename, 0, 1) == '.') {
627
                        $filename = substr($filename, 1);
628
                    }
629
630
                    // Prepend images root
631
                    $filename = $this->getImagesRoot() . $filename;
632
633
                    // Strip off eventual '.'
634
                    if (substr($filename, 0, 1) == '.' && substr($filename, 0, 2) != './') {
635
                        $filename = substr($filename, 1);
636
                    }
637
638
                    // Convert UTF8 data to PCDATA
639
                    $filename = htmlspecialchars($filename);
640
641
                    $html .= PHP_EOL;
642
                    if ((!$this->embedImages) || ($this->isPdf)) {
643
                        $imageData = $filename;
644
                    } else {
645
                        $imageDetails = getimagesize($filename);
646
                        if ($fp = fopen($filename, 'rb', 0)) {
647
                            $picture = fread($fp, filesize($filename));
648
                            fclose($fp);
649
                            // base64 encode the binary data, then break it
650
                            // into chunks according to RFC 2045 semantics
651
                            $base64 = chunk_split(base64_encode($picture));
652
                            $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
653
                        } else {
654
                            $imageData = $filename;
655
                        }
656
                    }
657
658
                    $html .= '<div style="position: relative;">';
659
                    $html .= '<img style="position: absolute; z-index: 1; left: ' .
660
                        $drawing->getOffsetX() . 'px; top: ' . $drawing->getOffsetY() . 'px; width: ' .
661
                        $drawing->getWidth() . 'px; height: ' . $drawing->getHeight() . 'px;" src="' .
662
                        $imageData . '" border="0" />';
663
                    $html .= '</div>';
664
                }
665
            } elseif ($drawing instanceof \PhpSpreadsheet\Worksheet\MemoryDrawing) {
666
                if ($drawing->getCoordinates() != $coordinates) {
667
                    continue;
668
                }
669
                ob_start(); //  Let's start output buffering.
670
                imagepng($drawing->getImageResource()); //  This will normally output the image, but because of ob_start(), it won't.
671
                $contents = ob_get_contents(); //  Instead, output above is saved to $contents
672
                ob_end_clean(); //  End the output buffer.
673
674
                $dataUri = 'data:image/jpeg;base64,' . base64_encode($contents);
675
676
                //  Because of the nature of tables, width is more important than height.
677
                //  max-width: 100% ensures that image doesnt overflow containing cell
678
                //  width: X sets width of supplied image.
679
                //  As a result, images bigger than cell will be contained and images smaller will not get stretched
680
                $html .= '<img src="' . $dataUri . '" style="max-width:100%;width:' . $drawing->getWidth() . 'px;" />';
681
            }
682
        }
683
684
        return $html;
685
    }
686
687
    /**
688
     * Generate chart tag in cell
689
     *
690
     * @param    \PhpSpreadsheet\Worksheet    $pSheet            \PhpSpreadsheet\Worksheet
691
     * @param    string                $coordinates    Cell coordinates
692
     * @throws    \PhpSpreadsheet\Writer\Exception
693
     * @return    string
694
     */
695
    private function writeChartInCell(\PhpSpreadsheet\Worksheet $pSheet, $coordinates)
696
    {
697
        // Construct HTML
698
        $html = '';
699
700
        // Write charts
701
        foreach ($pSheet->getChartCollection() as $chart) {
702
            if ($chart instanceof \PhpSpreadsheet\Chart) {
703
                $chartCoordinates = $chart->getTopLeftPosition();
704
                if ($chartCoordinates['cell'] == $coordinates) {
705
                    $chartFileName = \PhpSpreadsheet\Shared\File::sysGetTempDir() . '/' . uniqid() . '.png';
706
                    if (!$chart->render($chartFileName)) {
707
                        return;
708
                    }
709
710
                    $html .= PHP_EOL;
711
                    $imageDetails = getimagesize($chartFileName);
712
                    if ($fp = fopen($chartFileName, 'rb', 0)) {
713
                        $picture = fread($fp, filesize($chartFileName));
714
                        fclose($fp);
715
                        // base64 encode the binary data, then break it
716
                        // into chunks according to RFC 2045 semantics
717
                        $base64 = chunk_split(base64_encode($picture));
718
                        $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
719
720
                        $html .= '<div style="position: relative;">';
721
                        $html .= '<img style="position: absolute; z-index: 1; left: ' . $chartCoordinates['xOffset'] . 'px; top: ' . $chartCoordinates['yOffset'] . 'px; width: ' . $imageDetails[0] . 'px; height: ' . $imageDetails[1] . 'px;" src="' . $imageData . '" border="0" />' . PHP_EOL;
722
                        $html .= '</div>';
723
724
                        unlink($chartFileName);
725
                    }
726
                }
727
            }
728
        }
729
730
        // Return
731
        return $html;
732
    }
733
734
    /**
735
     * Generate CSS styles
736
     *
737
     * @param    bool    $generateSurroundingHTML    Generate surrounding HTML tags? (&lt;style&gt; and &lt;/style&gt;)
738
     * @throws    \PhpSpreadsheet\Writer\Exception
739
     * @return    string
740
     */
741
    public function generateStyles($generateSurroundingHTML = true)
742
    {
743
        // Spreadsheet object known?
744
        if (is_null($this->spreadsheet)) {
745
            throw new \PhpSpreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
746
        }
747
748
        // Build CSS
749
        $css = $this->buildCSS($generateSurroundingHTML);
750
751
        // Construct HTML
752
        $html = '';
753
754
        // Start styles
755
        if ($generateSurroundingHTML) {
756
            $html .= '    <style type="text/css">' . PHP_EOL;
757
            $html .= '      html { ' . $this->assembleCSS($css['html']) . ' }' . PHP_EOL;
758
        }
759
760
        // Write all other styles
761
        foreach ($css as $styleName => $styleDefinition) {
762
            if ($styleName != 'html') {
763
                $html .= '      ' . $styleName . ' { ' . $this->assembleCSS($styleDefinition) . ' }' . PHP_EOL;
764
            }
765
        }
766
767
        // End styles
768
        if ($generateSurroundingHTML) {
769
            $html .= '    </style>' . PHP_EOL;
770
        }
771
772
        // Return
773
        return $html;
774
    }
775
776
    /**
777
     * Build CSS styles
778
     *
779
     * @param    bool    $generateSurroundingHTML    Generate surrounding HTML style? (html { })
780
     * @throws    \PhpSpreadsheet\Writer\Exception
781
     * @return    array
782
     */
783
    public function buildCSS($generateSurroundingHTML = true)
784
    {
785
        // Spreadsheet object known?
786
        if (is_null($this->spreadsheet)) {
787
            throw new \PhpSpreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
788
        }
789
790
        // Cached?
791
        if (!is_null($this->cssStyles)) {
792
            return $this->cssStyles;
793
        }
794
795
        // Ensure that spans have been calculated
796
        if (!$this->spansAreCalculated) {
797
            $this->calculateSpans();
798
        }
799
800
        // Construct CSS
801
        $css = [];
802
803
        // Start styles
804
        if ($generateSurroundingHTML) {
805
            // html { }
806
            $css['html']['font-family'] = 'Calibri, Arial, Helvetica, sans-serif';
807
            $css['html']['font-size'] = '11pt';
808
            $css['html']['background-color'] = 'white';
809
        }
810
811
        // table { }
812
        $css['table']['border-collapse'] = 'collapse';
813
        if (!$this->isPdf) {
814
            $css['table']['page-break-after'] = 'always';
815
        }
816
817
        // .gridlines td { }
818
        $css['.gridlines td']['border'] = '1px dotted black';
819
        $css['.gridlines th']['border'] = '1px dotted black';
820
821
        // .b {}
822
        $css['.b']['text-align'] = 'center'; // BOOL
823
824
        // .e {}
825
        $css['.e']['text-align'] = 'center'; // ERROR
826
827
        // .f {}
828
        $css['.f']['text-align'] = 'right'; // FORMULA
829
830
        // .inlineStr {}
831
        $css['.inlineStr']['text-align'] = 'left'; // INLINE
832
833
        // .n {}
834
        $css['.n']['text-align'] = 'right'; // NUMERIC
835
836
        // .s {}
837
        $css['.s']['text-align'] = 'left'; // STRING
838
839
        // Calculate cell style hashes
840
        foreach ($this->spreadsheet->getCellXfCollection() as $index => $style) {
841
            $css['td.style' . $index] = $this->createCSSStyle($style);
842
            $css['th.style' . $index] = $this->createCSSStyle($style);
843
        }
844
845
        // Fetch sheets
846
        $sheets = [];
847 View Code Duplication
        if (is_null($this->sheetIndex)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
848
            $sheets = $this->spreadsheet->getAllSheets();
849
        } else {
850
            $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex);
851
        }
852
853
        // Build styles per sheet
854
        foreach ($sheets as $sheet) {
855
            // Calculate hash code
856
            $sheetIndex = $sheet->getParent()->getIndex($sheet);
857
858
            // Build styles
859
            // Calculate column widths
860
            $sheet->calculateColumnWidths();
861
862
            // col elements, initialize
863
            $highestColumnIndex = \PhpSpreadsheet\Cell::columnIndexFromString($sheet->getHighestColumn()) - 1;
864
            $column = -1;
865
            while ($column++ < $highestColumnIndex) {
866
                $this->columnWidths[$sheetIndex][$column] = 42; // approximation
867
                $css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = '42pt';
868
            }
869
870
            // col elements, loop through columnDimensions and set width
871
            foreach ($sheet->getColumnDimensions() as $columnDimension) {
872
                if (($width = \PhpSpreadsheet\Shared\Drawing::cellDimensionToPixels($columnDimension->getWidth(), $this->defaultFont)) >= 0) {
873
                    $width = \PhpSpreadsheet\Shared\Drawing::pixelsToPoints($width);
874
                    $column = \PhpSpreadsheet\Cell::columnIndexFromString($columnDimension->getColumnIndex()) - 1;
875
                    $this->columnWidths[$sheetIndex][$column] = $width;
876
                    $css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = $width . 'pt';
877
878 View Code Duplication
                    if ($columnDimension->getVisible() === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
879
                        $css['table.sheet' . $sheetIndex . ' col.col' . $column]['visibility'] = 'collapse';
880
                        $css['table.sheet' . $sheetIndex . ' col.col' . $column]['*display'] = 'none'; // target IE6+7
881
                    }
882
                }
883
            }
884
885
            // Default row height
886
            $rowDimension = $sheet->getDefaultRowDimension();
887
888
            // table.sheetN tr { }
889
            $css['table.sheet' . $sheetIndex . ' tr'] = [];
890
891 View Code Duplication
            if ($rowDimension->getRowHeight() == -1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
892
                $pt_height = Font::getDefaultRowHeightByFont($this->spreadsheet->getDefaultStyle()->getFont());
893
            } else {
894
                $pt_height = $rowDimension->getRowHeight();
895
            }
896
            $css['table.sheet' . $sheetIndex . ' tr']['height'] = $pt_height . 'pt';
897
            if ($rowDimension->getVisible() === false) {
898
                $css['table.sheet' . $sheetIndex . ' tr']['display'] = 'none';
899
                $css['table.sheet' . $sheetIndex . ' tr']['visibility'] = 'hidden';
900
            }
901
902
            // Calculate row heights
903
            foreach ($sheet->getRowDimensions() as $rowDimension) {
904
                $row = $rowDimension->getRowIndex() - 1;
905
906
                // table.sheetN tr.rowYYYYYY { }
907
                $css['table.sheet' . $sheetIndex . ' tr.row' . $row] = [];
908
909 View Code Duplication
                if ($rowDimension->getRowHeight() == -1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
910
                    $pt_height = Font::getDefaultRowHeightByFont($this->spreadsheet->getDefaultStyle()->getFont());
911
                } else {
912
                    $pt_height = $rowDimension->getRowHeight();
913
                }
914
                $css['table.sheet' . $sheetIndex . ' tr.row' . $row]['height'] = $pt_height . 'pt';
915 View Code Duplication
                if ($rowDimension->getVisible() === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
916
                    $css['table.sheet' . $sheetIndex . ' tr.row' . $row]['display'] = 'none';
917
                    $css['table.sheet' . $sheetIndex . ' tr.row' . $row]['visibility'] = 'hidden';
918
                }
919
            }
920
        }
921
922
        // Cache
923
        if (is_null($this->cssStyles)) {
924
            $this->cssStyles = $css;
925
        }
926
927
        // Return
928
        return $css;
929
    }
930
931
    /**
932
     * Create CSS style
933
     *
934
     * @param    \PhpSpreadsheet\Style        $pStyle
935
     * @return    array
936
     */
937
    private function createCSSStyle(\PhpSpreadsheet\Style $pStyle)
938
    {
939
        // Construct CSS
940
        $css = '';
0 ignored issues
show
Unused Code introduced by
$css is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
941
942
        // Create CSS
943
        $css = array_merge(
944
            $this->createCSSStyleAlignment($pStyle->getAlignment()),
945
            $this->createCSSStyleBorders($pStyle->getBorders()),
946
            $this->createCSSStyleFont($pStyle->getFont()),
947
            $this->createCSSStyleFill($pStyle->getFill())
948
        );
949
950
        // Return
951
        return $css;
952
    }
953
954
    /**
955
     * Create CSS style (\PhpSpreadsheet\Style\Alignment)
956
     *
957
     * @param    \PhpSpreadsheet\Style\Alignment        $pStyle            \PhpSpreadsheet\Style\Alignment
958
     * @return    array
959
     */
960
    private function createCSSStyleAlignment(\PhpSpreadsheet\Style\Alignment $pStyle)
961
    {
962
        // Construct CSS
963
        $css = [];
964
965
        // Create CSS
966
        $css['vertical-align'] = $this->mapVAlign($pStyle->getVertical());
967
        if ($textAlign = $this->mapHAlign($pStyle->getHorizontal())) {
968
            $css['text-align'] = $textAlign;
969
            if (in_array($textAlign, ['left', 'right'])) {
970
                $css['padding-' . $textAlign] = (string) ((int) $pStyle->getIndent() * 9) . 'px';
971
            }
972
        }
973
974
        return $css;
975
    }
976
977
    /**
978
     * Create CSS style (\PhpSpreadsheet\Style\Font)
979
     *
980
     * @param    \PhpSpreadsheet\Style\Font        $pStyle            \PhpSpreadsheet\Style\Font
981
     * @return    array
982
     */
983
    private function createCSSStyleFont(\PhpSpreadsheet\Style\Font $pStyle)
984
    {
985
        // Construct CSS
986
        $css = [];
987
988
        // Create CSS
989
        if ($pStyle->getBold()) {
990
            $css['font-weight'] = 'bold';
991
        }
992
        if ($pStyle->getUnderline() != \PhpSpreadsheet\Style\Font::UNDERLINE_NONE && $pStyle->getStrikethrough()) {
993
            $css['text-decoration'] = 'underline line-through';
994
        } elseif ($pStyle->getUnderline() != \PhpSpreadsheet\Style\Font::UNDERLINE_NONE) {
995
            $css['text-decoration'] = 'underline';
996
        } elseif ($pStyle->getStrikethrough()) {
997
            $css['text-decoration'] = 'line-through';
998
        }
999
        if ($pStyle->getItalic()) {
1000
            $css['font-style'] = 'italic';
1001
        }
1002
1003
        $css['color'] = '#' . $pStyle->getColor()->getRGB();
1004
        $css['font-family'] = '\'' . $pStyle->getName() . '\'';
1005
        $css['font-size'] = $pStyle->getSize() . 'pt';
1006
1007
        return $css;
1008
    }
1009
1010
    /**
1011
     * Create CSS style (\PhpSpreadsheet\Style\Borders)
1012
     *
1013
     * @param    \PhpSpreadsheet\Style\Borders        $pStyle            \PhpSpreadsheet\Style\Borders
1014
     * @return    array
1015
     */
1016
    private function createCSSStyleBorders(\PhpSpreadsheet\Style\Borders $pStyle)
1017
    {
1018
        // Construct CSS
1019
        $css = [];
1020
1021
        // Create CSS
1022
        $css['border-bottom'] = $this->createCSSStyleBorder($pStyle->getBottom());
1023
        $css['border-top'] = $this->createCSSStyleBorder($pStyle->getTop());
1024
        $css['border-left'] = $this->createCSSStyleBorder($pStyle->getLeft());
1025
        $css['border-right'] = $this->createCSSStyleBorder($pStyle->getRight());
1026
1027
        return $css;
1028
    }
1029
1030
    /**
1031
     * Create CSS style (\PhpSpreadsheet\Style\Border)
1032
     *
1033
     * @param    \PhpSpreadsheet\Style\Border        $pStyle            \PhpSpreadsheet\Style\Border
1034
     * @return    string
1035
     */
1036
    private function createCSSStyleBorder(\PhpSpreadsheet\Style\Border $pStyle)
1037
    {
1038
        //    Create CSS - add !important to non-none border styles for merged cells
1039
        $borderStyle = $this->mapBorderStyle($pStyle->getBorderStyle());
1040
        $css = $borderStyle . ' #' . $pStyle->getColor()->getRGB() . (($borderStyle == 'none') ? '' : ' !important');
1041
1042
        return $css;
1043
    }
1044
1045
    /**
1046
     * Create CSS style (\PhpSpreadsheet\Style\Fill)
1047
     *
1048
     * @param    \PhpSpreadsheet\Style\Fill        $pStyle            \PhpSpreadsheet\Style\Fill
1049
     * @return    array
1050
     */
1051
    private function createCSSStyleFill(\PhpSpreadsheet\Style\Fill $pStyle)
1052
    {
1053
        // Construct HTML
1054
        $css = [];
1055
1056
        // Create CSS
1057
        $value = $pStyle->getFillType() == \PhpSpreadsheet\Style\Fill::FILL_NONE ?
1058
            'white' : '#' . $pStyle->getStartColor()->getRGB();
1059
        $css['background-color'] = $value;
1060
1061
        return $css;
1062
    }
1063
1064
    /**
1065
     * Generate HTML footer
1066
     */
1067
    public function generateHTMLFooter()
1068
    {
1069
        // Construct HTML
1070
        $html = '';
1071
        $html .= '  </body>' . PHP_EOL;
1072
        $html .= '</html>' . PHP_EOL;
1073
1074
        return $html;
1075
    }
1076
1077
    /**
1078
     * Generate table header
1079
     *
1080
     * @param    \PhpSpreadsheet\Worksheet    $pSheet        The worksheet for the table we are writing
1081
     * @throws    \PhpSpreadsheet\Writer\Exception
1082
     * @return    string
1083
     */
1084
    private function generateTableHeader($pSheet)
1085
    {
1086
        $sheetIndex = $pSheet->getParent()->getIndex($pSheet);
1087
1088
        // Construct HTML
1089
        $html = '';
1090
        $html .= $this->setMargins($pSheet);
1091
1092
        if (!$this->useInlineCss) {
1093
            $gridlines = $pSheet->getShowGridlines() ? ' gridlines' : '';
1094
            $html .= '    <table border="0" cellpadding="0" cellspacing="0" id="sheet' . $sheetIndex . '" class="sheet' . $sheetIndex . $gridlines . '">' . PHP_EOL;
1095
        } else {
1096
            $style = isset($this->cssStyles['table']) ?
1097
                $this->assembleCSS($this->cssStyles['table']) : '';
1098
1099
            if ($this->isPdf && $pSheet->getShowGridlines()) {
1100
                $html .= '    <table border="1" cellpadding="1" id="sheet' . $sheetIndex . '" cellspacing="1" style="' . $style . '">' . PHP_EOL;
1101
            } else {
1102
                $html .= '    <table border="0" cellpadding="1" id="sheet' . $sheetIndex . '" cellspacing="0" style="' . $style . '">' . PHP_EOL;
1103
            }
1104
        }
1105
1106
        // Write <col> elements
1107
        $highestColumnIndex = \PhpSpreadsheet\Cell::columnIndexFromString($pSheet->getHighestColumn()) - 1;
1108
        $i = -1;
1109
        while ($i++ < $highestColumnIndex) {
1110 View Code Duplication
            if (!$this->isPdf) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1111
                if (!$this->useInlineCss) {
1112
                    $html .= '        <col class="col' . $i . '">' . PHP_EOL;
1113
                } else {
1114
                    $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i]) ?
1115
                        $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i]) : '';
1116
                    $html .= '        <col style="' . $style . '">' . PHP_EOL;
1117
                }
1118
            }
1119
        }
1120
1121
        return $html;
1122
    }
1123
1124
    /**
1125
     * Generate table footer
1126
     *
1127
     * @throws    \PhpSpreadsheet\Writer\Exception
1128
     */
1129
    private function generateTableFooter()
1130
    {
1131
        $html = '    </table>' . PHP_EOL;
1132
1133
        return $html;
1134
    }
1135
1136
    /**
1137
     * Generate row
1138
     *
1139
     * @param    \PhpSpreadsheet\Worksheet    $pSheet            \PhpSpreadsheet\Worksheet
1140
     * @param    array                $pValues        Array containing cells in a row
1141
     * @param    int                    $pRow            Row number (0-based)
1142
     * @throws    \PhpSpreadsheet\Writer\Exception
1143
     * @return    string
1144
     */
1145
    private function generateRow(\PhpSpreadsheet\Worksheet $pSheet, $pValues = null, $pRow = 0, $cellType = 'td')
1146
    {
1147
        if (is_array($pValues)) {
1148
            // Construct HTML
1149
            $html = '';
1150
1151
            // Sheet index
1152
            $sheetIndex = $pSheet->getParent()->getIndex($pSheet);
1153
1154
            // DomPDF and breaks
1155
            if ($this->isPdf && count($pSheet->getBreaks()) > 0) {
1156
                $breaks = $pSheet->getBreaks();
1157
1158
                // check if a break is needed before this row
1159
                if (isset($breaks['A' . $pRow])) {
1160
                    // close table: </table>
1161
                    $html .= $this->generateTableFooter();
1162
1163
                    // insert page break
1164
                    $html .= '<div style="page-break-before:always" />';
1165
1166
                    // open table again: <table> + <col> etc.
1167
                    $html .= $this->generateTableHeader($pSheet);
1168
                }
1169
            }
1170
1171
            // Write row start
1172 View Code Duplication
            if (!$this->useInlineCss) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1173
                $html .= '          <tr class="row' . $pRow . '">' . PHP_EOL;
1174
            } else {
1175
                $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow])
1176
                    ? $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]) : '';
1177
1178
                $html .= '          <tr style="' . $style . '">' . PHP_EOL;
1179
            }
1180
1181
            // Write cells
1182
            $colNum = 0;
1183
            foreach ($pValues as $cellAddress) {
1184
                $cell = ($cellAddress > '') ? $pSheet->getCell($cellAddress) : '';
1185
                $coordinate = \PhpSpreadsheet\Cell::stringFromColumnIndex($colNum) . ($pRow + 1);
1186
                if (!$this->useInlineCss) {
1187
                    $cssClass = '';
0 ignored issues
show
Unused Code introduced by
$cssClass is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1188
                    $cssClass = 'column' . $colNum;
1189
                } else {
1190
                    $cssClass = [];
1191
                    if ($cellType == 'th') {
1192 View Code Duplication
                        if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1193
                            $this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum];
1194
                        }
1195 View Code Duplication
                    } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1196
                        if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum])) {
1197
                            $this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum];
1198
                        }
1199
                    }
1200
                }
1201
                $colSpan = 1;
1202
                $rowSpan = 1;
1203
1204
                // initialize
1205
                $cellData = '&nbsp;';
1206
1207
                // \PhpSpreadsheet\Cell
1208
                if ($cell instanceof \PhpSpreadsheet\Cell) {
1209
                    $cellData = '';
1210
                    if (is_null($cell->getParent())) {
1211
                        $cell->attach($pSheet);
0 ignored issues
show
Documentation introduced by
$pSheet is of type object<PhpSpreadsheet\Worksheet>, but the function expects a object<PhpSpreadsheet\Ca...bjectStorage\CacheBase>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1212
                    }
1213
                    // Value
1214
                    if ($cell->getValue() instanceof \PhpSpreadsheet\RichText) {
1215
                        // Loop through rich text elements
1216
                        $elements = $cell->getValue()->getRichTextElements();
1217
                        foreach ($elements as $element) {
1218
                            // Rich text start?
1219
                            if ($element instanceof \PhpSpreadsheet\RichText\Run) {
1220
                                $cellData .= '<span style="' . $this->assembleCSS($this->createCSSStyleFont($element->getFont())) . '">';
1221
1222
                                if ($element->getFont()->getSuperScript()) {
1223
                                    $cellData .= '<sup>';
1224
                                } elseif ($element->getFont()->getSubScript()) {
1225
                                    $cellData .= '<sub>';
1226
                                }
1227
                            }
1228
1229
                            // Convert UTF8 data to PCDATA
1230
                            $cellText = $element->getText();
1231
                            $cellData .= htmlspecialchars($cellText);
1232
1233
                            if ($element instanceof \PhpSpreadsheet\RichText\Run) {
1234
                                if ($element->getFont()->getSuperScript()) {
1235
                                    $cellData .= '</sup>';
1236
                                } elseif ($element->getFont()->getSubScript()) {
1237
                                    $cellData .= '</sub>';
1238
                                }
1239
1240
                                $cellData .= '</span>';
1241
                            }
1242
                        }
1243
                    } else {
1244
                        if ($this->preCalculateFormulas) {
1245
                            $cellData = \PhpSpreadsheet\Style\NumberFormat::toFormattedString(
1246
                                $cell->getCalculatedValue(),
1247
                                $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(),
1248
                                [$this, 'formatColor']
1249
                            );
1250
                        } else {
1251
                            $cellData = \PhpSpreadsheet\Style\NumberFormat::toFormattedString(
1252
                                $cell->getValue(),
1253
                                $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(),
1254
                                [$this, 'formatColor']
1255
                            );
1256
                        }
1257
                        $cellData = htmlspecialchars($cellData);
1258
                        if ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSuperScript()) {
1259
                            $cellData = '<sup>' . $cellData . '</sup>';
1260
                        } elseif ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSubScript()) {
1261
                            $cellData = '<sub>' . $cellData . '</sub>';
1262
                        }
1263
                    }
1264
1265
                    // Converts the cell content so that spaces occuring at beginning of each new line are replaced by &nbsp;
1266
                    // Example: "  Hello\n to the world" is converted to "&nbsp;&nbsp;Hello\n&nbsp;to the world"
1267
                    $cellData = preg_replace('/(?m)(?:^|\\G) /', '&nbsp;', $cellData);
1268
1269
                    // convert newline "\n" to '<br>'
1270
                    $cellData = nl2br($cellData);
1271
1272
                    // Extend CSS class?
1273
                    if (!$this->useInlineCss) {
1274
                        $cssClass .= ' style' . $cell->getXfIndex();
1275
                        $cssClass .= ' ' . $cell->getDataType();
1276
                    } else {
1277
                        if ($cellType == 'th') {
1278 View Code Duplication
                            if (isset($this->cssStyles['th.style' . $cell->getXfIndex()])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1279
                                $cssClass = array_merge($cssClass, $this->cssStyles['th.style' . $cell->getXfIndex()]);
1280
                            }
1281 View Code Duplication
                        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1282
                            if (isset($this->cssStyles['td.style' . $cell->getXfIndex()])) {
1283
                                $cssClass = array_merge($cssClass, $this->cssStyles['td.style' . $cell->getXfIndex()]);
1284
                            }
1285
                        }
1286
1287
                        // General horizontal alignment: Actual horizontal alignment depends on dataType
1288
                        $sharedStyle = $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex());
1289
                        if ($sharedStyle->getAlignment()->getHorizontal() == \PhpSpreadsheet\Style\Alignment::HORIZONTAL_GENERAL
1290
                            && isset($this->cssStyles['.' . $cell->getDataType()]['text-align'])) {
1291
                            $cssClass['text-align'] = $this->cssStyles['.' . $cell->getDataType()]['text-align'];
1292
                        }
1293
                    }
1294
                }
1295
1296
                // Hyperlink?
1297
                if ($pSheet->hyperlinkExists($coordinate) && !$pSheet->getHyperlink($coordinate)->isInternal()) {
1298
                    $cellData = '<a href="' . htmlspecialchars($pSheet->getHyperlink($coordinate)->getUrl()) . '" title="' . htmlspecialchars($pSheet->getHyperlink($coordinate)->getTooltip()) . '">' . $cellData . '</a>';
1299
                }
1300
1301
                // Should the cell be written or is it swallowed by a rowspan or colspan?
1302
                $writeCell = !(isset($this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])
1303
                            && $this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum]);
1304
1305
                // Colspan and Rowspan
1306
                $colspan = 1;
0 ignored issues
show
Unused Code introduced by
$colspan is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1307
                $rowspan = 1;
0 ignored issues
show
Unused Code introduced by
$rowspan is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1308
                if (isset($this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])) {
1309
                    $spans = $this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum];
1310
                    $rowSpan = $spans['rowspan'];
1311
                    $colSpan = $spans['colspan'];
1312
1313
                    //    Also apply style from last cell in merge to fix borders -
1314
                    //        relies on !important for non-none border declarations in createCSSStyleBorder
1315
                    $endCellCoord = \PhpSpreadsheet\Cell::stringFromColumnIndex($colNum + $colSpan - 1) . ($pRow + $rowSpan);
1316
                    if (!$this->useInlineCss) {
1317
                        $cssClass .= ' style' . $pSheet->getCell($endCellCoord)->getXfIndex();
1318
                    }
1319
                }
1320
1321
                // Write
1322
                if ($writeCell) {
1323
                    // Column start
1324
                    $html .= '            <' . $cellType;
1325
                    if (!$this->useInlineCss) {
1326
                        $html .= ' class="' . $cssClass . '"';
1327
                    } else {
1328
                        //** Necessary redundant code for the sake of PhpSpreadsheet\Writer\PDF **
1329
                        // We must explicitly write the width of the <td> element because TCPDF
1330
                        // does not recognize e.g. <col style="width:42pt">
1331
                        $width = 0;
1332
                        $i = $colNum - 1;
1333
                        $e = $colNum + $colSpan - 1;
1334
                        while ($i++ < $e) {
1335
                            if (isset($this->columnWidths[$sheetIndex][$i])) {
1336
                                $width += $this->columnWidths[$sheetIndex][$i];
1337
                            }
1338
                        }
1339
                        $cssClass['width'] = $width . 'pt';
1340
1341
                        // We must also explicitly write the height of the <td> element because TCPDF
1342
                        // does not recognize e.g. <tr style="height:50pt">
1343
                        if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'])) {
1344
                            $height = $this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'];
1345
                            $cssClass['height'] = $height;
1346
                        }
1347
                        //** end of redundant code **
1348
1349
                        $html .= ' style="' . $this->assembleCSS($cssClass) . '"';
0 ignored issues
show
Bug introduced by
It seems like $cssClass defined by 'column' . $colNum on line 1188 can also be of type string; however, PhpSpreadsheet\Writer\HTML::assembleCSS() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1350
                    }
1351
                    if ($colSpan > 1) {
1352
                        $html .= ' colspan="' . $colSpan . '"';
1353
                    }
1354
                    if ($rowSpan > 1) {
1355
                        $html .= ' rowspan="' . $rowSpan . '"';
1356
                    }
1357
                    $html .= '>';
1358
1359
                    // Image?
1360
                    $html .= $this->writeImageInCell($pSheet, $coordinate);
1361
1362
                    // Chart?
1363
                    if ($this->includeCharts) {
1364
                        $html .= $this->writeChartInCell($pSheet, $coordinate);
1365
                    }
1366
1367
                    // Cell data
1368
                    $html .= $cellData;
1369
1370
                    // Column end
1371
                    $html .= '</' . $cellType . '>' . PHP_EOL;
1372
                }
1373
1374
                // Next column
1375
                ++$colNum;
1376
            }
1377
1378
            // Write row end
1379
            $html .= '          </tr>' . PHP_EOL;
1380
1381
            // Return
1382
            return $html;
1383
        } else {
1384
            throw new \PhpSpreadsheet\Writer\Exception('Invalid parameters passed.');
1385
        }
1386
    }
1387
1388
    /**
1389
     * Takes array where of CSS properties / values and converts to CSS string
1390
     *
1391
     * @param array
1392
     * @return string
1393
     */
1394
    private function assembleCSS($pValue = [])
1395
    {
1396
        $pairs = [];
1397
        foreach ($pValue as $property => $value) {
1398
            $pairs[] = $property . ':' . $value;
1399
        }
1400
        $string = implode('; ', $pairs);
1401
1402
        return $string;
1403
    }
1404
1405
    /**
1406
     * Get images root
1407
     *
1408
     * @return string
1409
     */
1410
    public function getImagesRoot()
1411
    {
1412
        return $this->imagesRoot;
1413
    }
1414
1415
    /**
1416
     * Set images root
1417
     *
1418
     * @param string $pValue
1419
     * @return HTML
1420
     */
1421
    public function setImagesRoot($pValue = '.')
1422
    {
1423
        $this->imagesRoot = $pValue;
1424
1425
        return $this;
1426
    }
1427
1428
    /**
1429
     * Get embed images
1430
     *
1431
     * @return bool
1432
     */
1433
    public function getEmbedImages()
1434
    {
1435
        return $this->embedImages;
1436
    }
1437
1438
    /**
1439
     * Set embed images
1440
     *
1441
     * @param bool $pValue
1442
     * @return HTML
1443
     */
1444
    public function setEmbedImages($pValue = true)
1445
    {
1446
        $this->embedImages = $pValue;
1447
1448
        return $this;
1449
    }
1450
1451
    /**
1452
     * Get use inline CSS?
1453
     *
1454
     * @return bool
1455
     */
1456
    public function getUseInlineCss()
1457
    {
1458
        return $this->useInlineCss;
1459
    }
1460
1461
    /**
1462
     * Set use inline CSS?
1463
     *
1464
     * @param bool $pValue
1465
     * @return HTML
1466
     */
1467
    public function setUseInlineCss($pValue = false)
1468
    {
1469
        $this->useInlineCss = $pValue;
1470
1471
        return $this;
1472
    }
1473
1474
    /**
1475
     * Add color to formatted string as inline style
1476
     *
1477
     * @param string $pValue Plain formatted value without color
1478
     * @param string $pFormat Format code
1479
     * @return string
1480
     */
1481
    public function formatColor($pValue, $pFormat)
1482
    {
1483
        // Color information, e.g. [Red] is always at the beginning
1484
        $color = null; // initialize
1485
        $matches = [];
1486
1487
        $color_regex = '/^\\[[a-zA-Z]+\\]/';
1488
        if (preg_match($color_regex, $pFormat, $matches)) {
1489
            $color = str_replace('[', '', $matches[0]);
1490
            $color = str_replace(']', '', $color);
1491
            $color = strtolower($color);
1492
        }
1493
1494
        // convert to PCDATA
1495
        $value = htmlspecialchars($pValue);
1496
1497
        // color span tag
1498
        if ($color !== null) {
1499
            $value = '<span style="color:' . $color . '">' . $value . '</span>';
1500
        }
1501
1502
        return $value;
1503
    }
1504
1505
    /**
1506
     * Calculate information about HTML colspan and rowspan which is not always the same as Excel's
1507
     */
1508
    private function calculateSpans()
1509
    {
1510
        // Identify all cells that should be omitted in HTML due to cell merge.
1511
        // In HTML only the upper-left cell should be written and it should have
1512
        //   appropriate rowspan / colspan attribute
1513
        $sheetIndexes = $this->sheetIndex !== null ?
1514
            [$this->sheetIndex] : range(0, $this->spreadsheet->getSheetCount() - 1);
1515
1516
        foreach ($sheetIndexes as $sheetIndex) {
1517
            $sheet = $this->spreadsheet->getSheet($sheetIndex);
1518
1519
            $candidateSpannedRow = [];
1520
1521
            // loop through all Excel merged cells
1522
            foreach ($sheet->getMergeCells() as $cells) {
1523
                list($cells) = \PhpSpreadsheet\Cell::splitRange($cells);
0 ignored issues
show
Documentation introduced by
$cells is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1524
                $first = $cells[0];
1525
                $last = $cells[1];
1526
1527
                list($fc, $fr) = \PhpSpreadsheet\Cell::coordinateFromString($first);
1528
                $fc = \PhpSpreadsheet\Cell::columnIndexFromString($fc) - 1;
1529
1530
                list($lc, $lr) = \PhpSpreadsheet\Cell::coordinateFromString($last);
1531
                $lc = \PhpSpreadsheet\Cell::columnIndexFromString($lc) - 1;
1532
1533
                // loop through the individual cells in the individual merge
1534
                $r = $fr - 1;
1535
                while ($r++ < $lr) {
1536
                    // also, flag this row as a HTML row that is candidate to be omitted
1537
                    $candidateSpannedRow[$r] = $r;
1538
1539
                    $c = $fc - 1;
1540
                    while ($c++ < $lc) {
1541
                        if (!($c == $fc && $r == $fr)) {
1542
                            // not the upper-left cell (should not be written in HTML)
1543
                            $this->isSpannedCell[$sheetIndex][$r][$c] = [
1544
                                'baseCell' => [$fr, $fc],
1545
                            ];
1546
                        } else {
1547
                            // upper-left is the base cell that should hold the colspan/rowspan attribute
1548
                            $this->isBaseCell[$sheetIndex][$r][$c] = [
1549
                                'xlrowspan' => $lr - $fr + 1, // Excel rowspan
1550
                                'rowspan' => $lr - $fr + 1, // HTML rowspan, value may change
1551
                                'xlcolspan' => $lc - $fc + 1, // Excel colspan
1552
                                'colspan' => $lc - $fc + 1, // HTML colspan, value may change
1553
                            ];
1554
                        }
1555
                    }
1556
                }
1557
            }
1558
1559
            // Identify which rows should be omitted in HTML. These are the rows where all the cells
1560
            //   participate in a merge and the where base cells are somewhere above.
1561
            $countColumns = \PhpSpreadsheet\Cell::columnIndexFromString($sheet->getHighestColumn());
1562
            foreach ($candidateSpannedRow as $rowIndex) {
1563
                if (isset($this->isSpannedCell[$sheetIndex][$rowIndex])) {
1564
                    if (count($this->isSpannedCell[$sheetIndex][$rowIndex]) == $countColumns) {
1565
                        $this->isSpannedRow[$sheetIndex][$rowIndex] = $rowIndex;
1566
                    }
1567
                }
1568
            }
1569
1570
            // For each of the omitted rows we found above, the affected rowspans should be subtracted by 1
1571
            if (isset($this->isSpannedRow[$sheetIndex])) {
1572
                foreach ($this->isSpannedRow[$sheetIndex] as $rowIndex) {
1573
                    $adjustedBaseCells = [];
1574
                    $c = -1;
1575
                    $e = $countColumns - 1;
1576
                    while ($c++ < $e) {
1577
                        $baseCell = $this->isSpannedCell[$sheetIndex][$rowIndex][$c]['baseCell'];
1578
1579
                        if (!in_array($baseCell, $adjustedBaseCells)) {
1580
                            // subtract rowspan by 1
1581
                            --$this->isBaseCell[$sheetIndex][$baseCell[0]][$baseCell[1]]['rowspan'];
1582
                            $adjustedBaseCells[] = $baseCell;
1583
                        }
1584
                    }
1585
                }
1586
            }
1587
1588
            // TODO: Same for columns
1589
        }
1590
1591
        // We have calculated the spans
1592
        $this->spansAreCalculated = true;
1593
    }
1594
1595
    private function setMargins(\PhpSpreadsheet\Worksheet $pSheet)
1596
    {
1597
        $htmlPage = '@page { ';
1598
        $htmlBody = 'body { ';
1599
1600
        $left = StringHelper::formatNumber($pSheet->getPageMargins()->getLeft()) . 'in; ';
1601
        $htmlPage .= 'margin-left: ' . $left;
1602
        $htmlBody .= 'margin-left: ' . $left;
1603
        $right = StringHelper::formatNumber($pSheet->getPageMargins()->getRight()) . 'in; ';
1604
        $htmlPage .= 'margin-right: ' . $right;
1605
        $htmlBody .= 'margin-right: ' . $right;
1606
        $top = StringHelper::formatNumber($pSheet->getPageMargins()->getTop()) . 'in; ';
1607
        $htmlPage .= 'margin-top: ' . $top;
1608
        $htmlBody .= 'margin-top: ' . $top;
1609
        $bottom = StringHelper::formatNumber($pSheet->getPageMargins()->getBottom()) . 'in; ';
1610
        $htmlPage .= 'margin-bottom: ' . $bottom;
1611
        $htmlBody .= 'margin-bottom: ' . $bottom;
1612
1613
        $htmlPage .= "}\n";
1614
        $htmlBody .= "}\n";
1615
1616
        return "<style>\n" . $htmlPage . $htmlBody . "</style>\n";
1617
    }
1618
}
1619