Completed
Push — develop ( e1f81f...539a89 )
by Adrien
16:11
created

HTML::createCSSStyleFont()   C

Complexity

Conditions 7
Paths 16

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 16
nc 16
nop 1
dl 0
loc 26
rs 6.7272
c 1
b 0
f 0
1
<?php
2
3
namespace Spreadsheet\Writer;
4
5
use Spreadsheet\Calculation;
6
use Spreadsheet\Shared\Font;
7
use Spreadsheet\Shared\StringHelper;
8
use Spreadsheet\Spreadsheet;
9
10
/**
11
 * Spreadsheet_Writer_HTML
12
 *
13
 * Copyright (c) 2006 - 2015 Spreadsheet
14
 *
15
 * This library is free software; you can redistribute it and/or
16
 * modify it under the terms of the GNU Lesser General Public
17
 * License as published by the Free Software Foundation; either
18
 * version 2.1 of the License, or (at your option) any later version.
19
 *
20
 * This library is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23
 * Lesser General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Lesser General Public
26
 * License along with this library; if not, write to the Free Software
27
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
28
 *
29
 * @category   Spreadsheet
30
 * @copyright  Copyright (c) 2006 - 2015 Spreadsheet (https://github.com/PHPOffice/Spreadsheet)
31
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
32
 * @version    ##VERSION##, ##DATE##
33
 */
34
class HTML extends BaseWriter implements IWriter
35
{
36
    /**
37
     * Spreadsheet object
38
     *
39
     * @var Spreadsheet
40
     */
41
    protected $spreadsheet;
42
43
    /**
44
     * Sheet index to write
45
     *
46
     * @var int
47
     */
48
    private $sheetIndex = 0;
49
50
    /**
51
     * Images root
52
     *
53
     * @var string
54
     */
55
    private $imagesRoot = '.';
56
57
    /**
58
     * embed images, or link to images
59
     *
60
     * @var boolean
61
     */
62
    private $embedImages = false;
63
64
    /**
65
     * Use inline CSS?
66
     *
67
     * @var boolean
68
     */
69
    private $useInlineCss = false;
70
71
    /**
72
     * Array of CSS styles
73
     *
74
     * @var array
75
     */
76
    private $cssStyles;
77
78
    /**
79
     * Array of column widths in points
80
     *
81
     * @var array
82
     */
83
    private $columnWidths;
84
85
    /**
86
     * Default font
87
     *
88
     * @var \Spreadsheet\Style\Font
89
     */
90
    private $defaultFont;
91
92
    /**
93
     * Flag whether spans have been calculated
94
     *
95
     * @var boolean
96
     */
97
    private $spansAreCalculated = false;
98
99
    /**
100
     * Excel cells that should not be written as HTML cells
101
     *
102
     * @var array
103
     */
104
    private $isSpannedCell = array();
105
106
    /**
107
     * Excel cells that are upper-left corner in a cell merge
108
     *
109
     * @var array
110
     */
111
    private $isBaseCell = array();
112
113
    /**
114
     * Excel rows that should not be written as HTML rows
115
     *
116
     * @var array
117
     */
118
    private $isSpannedRow = array();
119
120
    /**
121
     * Is the current writer creating PDF?
122
     *
123
     * @var boolean
124
     */
125
    protected $isPdf = false;
126
127
    /**
128
     * Generate the Navigation block
129
     *
130
     * @var boolean
131
     */
132
    private $generateSheetNavigationBlock = true;
133
134
    /**
135
     * Create a new Spreadsheet_Writer_HTML
136
     *
137
     * @param    Spreadsheet    $spreadsheet
138
     */
139
    public function __construct(Spreadsheet $spreadsheet)
140
    {
141
        $this->spreadsheet = $spreadsheet;
142
        $this->defaultFont = $this->spreadsheet->getDefaultStyle()->getFont();
143
    }
144
145
    /**
146
     * Save Spreadsheet to file
147
     *
148
     * @param    string        $pFilename
149
     * @throws    \Spreadsheet\Writer\Exception
150
     */
151
    public function save($pFilename = null)
152
    {
153
        // garbage collect
154
        $this->spreadsheet->garbageCollect();
155
156
        $saveDebugLog = Calculation::getInstance($this->spreadsheet)->getDebugLog()->getWriteDebugLog();
157
        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog(false);
158
        $saveArrayReturnType = Calculation::getArrayReturnType();
159
        Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE);
160
161
        // Build CSS
162
        $this->buildCSS(!$this->useInlineCss);
163
164
        // Open file
165
        $fileHandle = fopen($pFilename, 'wb+');
166
        if ($fileHandle === false) {
167
            throw new \Spreadsheet\Writer\Exception("Could not open file $pFilename for writing.");
168
        }
169
170
        // Write headers
171
        fwrite($fileHandle, $this->generateHTMLHeader(!$this->useInlineCss));
172
173
        // Write navigation (tabs)
174
        if ((!$this->isPdf) && ($this->generateSheetNavigationBlock)) {
175
            fwrite($fileHandle, $this->generateNavigation());
176
        }
177
178
        // Write data
179
        fwrite($fileHandle, $this->generateSheetData());
180
181
        // Write footer
182
        fwrite($fileHandle, $this->generateHTMLFooter());
183
184
        // Close file
185
        fclose($fileHandle);
186
187
        Calculation::setArrayReturnType($saveArrayReturnType);
188
        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
189
    }
190
191
    /**
192
     * Map VAlign
193
     *
194
     * @param    string        $vAlign        Vertical alignment
195
     * @return string
196
     */
197
    private function mapVAlign($vAlign)
198
    {
199
        switch ($vAlign) {
200
            case \Spreadsheet\Style\Alignment::VERTICAL_BOTTOM:
201
                return 'bottom';
202
            case \Spreadsheet\Style\Alignment::VERTICAL_TOP:
203
                return 'top';
204
            case \Spreadsheet\Style\Alignment::VERTICAL_CENTER:
205
            case \Spreadsheet\Style\Alignment::VERTICAL_JUSTIFY:
206
                return 'middle';
207
            default:
208
                return 'baseline';
209
        }
210
    }
211
212
    /**
213
     * Map HAlign
214
     *
215
     * @param    string        $hAlign        Horizontal alignment
216
     * @return string|false
217
     */
218
    private function mapHAlign($hAlign)
219
    {
220
        switch ($hAlign) {
221
            case \Spreadsheet\Style\Alignment::HORIZONTAL_GENERAL:
222
                return false;
223
            case \Spreadsheet\Style\Alignment::HORIZONTAL_LEFT:
224
                return 'left';
225
            case \Spreadsheet\Style\Alignment::HORIZONTAL_RIGHT:
226
                return 'right';
227
            case \Spreadsheet\Style\Alignment::HORIZONTAL_CENTER:
228
            case \Spreadsheet\Style\Alignment::HORIZONTAL_CENTER_CONTINUOUS:
229
                return 'center';
230
            case \Spreadsheet\Style\Alignment::HORIZONTAL_JUSTIFY:
231
                return 'justify';
232
            default:
233
                return false;
234
        }
235
    }
236
237
    /**
238
     * Map border style
239
     *
240
     * @param    int        $borderStyle        Sheet index
241
     * @return    string
242
     */
243
    private function mapBorderStyle($borderStyle)
244
    {
245
        switch ($borderStyle) {
246
            case \Spreadsheet\Style\Border::BORDER_NONE:
247
                return 'none';
248
            case \Spreadsheet\Style\Border::BORDER_DASHDOT:
249
                return '1px dashed';
250
            case \Spreadsheet\Style\Border::BORDER_DASHDOTDOT:
251
                return '1px dotted';
252
            case \Spreadsheet\Style\Border::BORDER_DASHED:
253
                return '1px dashed';
254
            case \Spreadsheet\Style\Border::BORDER_DOTTED:
255
                return '1px dotted';
256
            case \Spreadsheet\Style\Border::BORDER_DOUBLE:
257
                return '3px double';
258
            case \Spreadsheet\Style\Border::BORDER_HAIR:
259
                return '1px solid';
260
            case \Spreadsheet\Style\Border::BORDER_MEDIUM:
261
                return '2px solid';
262
            case \Spreadsheet\Style\Border::BORDER_MEDIUMDASHDOT:
263
                return '2px dashed';
264
            case \Spreadsheet\Style\Border::BORDER_MEDIUMDASHDOTDOT:
265
                return '2px dotted';
266
            case \Spreadsheet\Style\Border::BORDER_MEDIUMDASHED:
267
                return '2px dashed';
268
            case \Spreadsheet\Style\Border::BORDER_SLANTDASHDOT:
269
                return '2px dashed';
270
            case \Spreadsheet\Style\Border::BORDER_THICK:
271
                return '3px solid';
272
            case \Spreadsheet\Style\Border::BORDER_THIN:
273
                return '1px solid';
274
            default:
275
                // map others to thin
276
                return '1px solid';
277
        }
278
    }
279
280
    /**
281
     * Get sheet index
282
     *
283
     * @return int
284
     */
285
    public function getSheetIndex()
286
    {
287
        return $this->sheetIndex;
288
    }
289
290
    /**
291
     * Set sheet index
292
     *
293
     * @param    int        $pValue        Sheet index
294
     * @return HTML
295
     */
296
    public function setSheetIndex($pValue = 0)
297
    {
298
        $this->sheetIndex = $pValue;
299
        return $this;
300
    }
301
302
    /**
303
     * Get sheet index
304
     *
305
     * @return boolean
306
     */
307
    public function getGenerateSheetNavigationBlock()
308
    {
309
        return $this->generateSheetNavigationBlock;
310
    }
311
312
    /**
313
     * Set sheet index
314
     *
315
     * @param    boolean        $pValue        Flag indicating whether the sheet navigation block should be generated or not
316
     * @return HTML
317
     */
318
    public function setGenerateSheetNavigationBlock($pValue = true)
319
    {
320
        $this->generateSheetNavigationBlock = (bool) $pValue;
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
        return $this;
331
    }
332
333
    /**
334
     * Generate HTML header
335
     *
336
     * @param    boolean        $pIncludeStyles        Include styles?
337
     * @return    string
338
     * @throws \Spreadsheet\Writer\Exception
339
     */
340
    public function generateHTMLHeader($pIncludeStyles = false)
341
    {
342
        // Spreadsheet object known?
343
        if (is_null($this->spreadsheet)) {
344
            throw new \Spreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
345
        }
346
347
        // Construct HTML
348
        $properties = $this->spreadsheet->getProperties();
349
        $html = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' . PHP_EOL;
350
        $html .= '<!-- Generated by Spreadsheet - https://github.com/PHPOffice/Spreadsheet -->' . PHP_EOL;
351
        $html .= '<html>' . PHP_EOL;
352
        $html .= '  <head>' . PHP_EOL;
353
        $html .= '      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . PHP_EOL;
354
        if ($properties->getTitle() > '') {
355
            $html .= '      <title>' . htmlspecialchars($properties->getTitle()) . '</title>' . PHP_EOL;
356
        }
357
        if ($properties->getCreator() > '') {
358
            $html .= '      <meta name="author" content="' . htmlspecialchars($properties->getCreator()) . '" />' . PHP_EOL;
359
        }
360
        if ($properties->getTitle() > '') {
361
            $html .= '      <meta name="title" content="' . htmlspecialchars($properties->getTitle()) . '" />' . PHP_EOL;
362
        }
363 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...
364
            $html .= '      <meta name="description" content="' . htmlspecialchars($properties->getDescription()) . '" />' . PHP_EOL;
365
        }
366
        if ($properties->getSubject() > '') {
367
            $html .= '      <meta name="subject" content="' . htmlspecialchars($properties->getSubject()) . '" />' . PHP_EOL;
368
        }
369
        if ($properties->getKeywords() > '') {
370
            $html .= '      <meta name="keywords" content="' . htmlspecialchars($properties->getKeywords()) . '" />' . PHP_EOL;
371
        }
372
        if ($properties->getCategory() > '') {
373
            $html .= '      <meta name="category" content="' . htmlspecialchars($properties->getCategory()) . '" />' . PHP_EOL;
374
        }
375 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...
376
            $html .= '      <meta name="company" content="' . htmlspecialchars($properties->getCompany()) . '" />' . PHP_EOL;
377
        }
378 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...
379
            $html .= '      <meta name="manager" content="' . htmlspecialchars($properties->getManager()) . '" />' . PHP_EOL;
380
        }
381
382
        if ($pIncludeStyles) {
383
            $html .= $this->generateStyles(true);
384
        }
385
386
        $html .= '  </head>' . PHP_EOL;
387
        $html .= '' . PHP_EOL;
388
        $html .= '  <body>' . PHP_EOL;
389
390
        return $html;
391
    }
392
393
    /**
394
     * Generate sheet data
395
     *
396
     * @return    string
397
     * @throws \Spreadsheet\Writer\Exception
398
     */
399
    public function generateSheetData()
400
    {
401
        // Spreadsheet object known?
402
        if (is_null($this->spreadsheet)) {
403
            throw new \Spreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
404
        }
405
406
        // Ensure that Spans have been calculated?
407
        if ($this->sheetIndex !== null || !$this->spansAreCalculated) {
408
            $this->calculateSpans();
409
        }
410
411
        // Fetch sheets
412
        $sheets = array();
413 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...
414
            $sheets = $this->spreadsheet->getAllSheets();
415
        } else {
416
            $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex);
417
        }
418
419
        // Construct HTML
420
        $html = '';
421
422
        // Loop all sheets
423
        $sheetId = 0;
424
        foreach ($sheets as $sheet) {
425
            // Write table header
426
            $html .= $this->generateTableHeader($sheet);
427
428
            // Get worksheet dimension
429
            $dimension = explode(':', $sheet->calculateWorksheetDimension());
430
            $dimension[0] = \Spreadsheet\Cell::coordinateFromString($dimension[0]);
431
            $dimension[0][0] = \Spreadsheet\Cell::columnIndexFromString($dimension[0][0]) - 1;
432
            $dimension[1] = \Spreadsheet\Cell::coordinateFromString($dimension[1]);
433
            $dimension[1][0] = \Spreadsheet\Cell::columnIndexFromString($dimension[1][0]) - 1;
434
435
            // row min,max
436
            $rowMin = $dimension[0][1];
437
            $rowMax = $dimension[1][1];
438
439
            // calculate start of <tbody>, <thead>
440
            $tbodyStart = $rowMin;
441
            $theadStart = $theadEnd   = 0; // default: no <thead>    no </thead>
442
            if ($sheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
443
                $rowsToRepeatAtTop = $sheet->getPageSetup()->getRowsToRepeatAtTop();
444
445
                // we can only support repeating rows that start at top row
446
                if ($rowsToRepeatAtTop[0] == 1) {
447
                    $theadStart = $rowsToRepeatAtTop[0];
448
                    $theadEnd   = $rowsToRepeatAtTop[1];
449
                    $tbodyStart = $rowsToRepeatAtTop[1] + 1;
450
                }
451
            }
452
453
            // Loop through cells
454
            $row = $rowMin-1;
455
            while ($row++ < $rowMax) {
456
                // <thead> ?
457
                if ($row == $theadStart) {
458
                    $html .= '        <thead>' . PHP_EOL;
459
                    $cellType = 'th';
460
                }
461
462
                // <tbody> ?
463
                if ($row == $tbodyStart) {
464
                    $html .= '        <tbody>' . PHP_EOL;
465
                    $cellType = 'td';
466
                }
467
468
                // Write row if there are HTML table cells in it
469
                if (!isset($this->isSpannedRow[$sheet->getParent()->getIndex($sheet)][$row])) {
470
                    // Start a new rowData
471
                    $rowData = array();
472
                    // Loop through columns
473
                    $column = $dimension[0][0] - 1;
474
                    while ($column++ < $dimension[1][0]) {
475
                        // Cell exists?
476
                        if ($sheet->cellExistsByColumnAndRow($column, $row)) {
477
                            $rowData[$column] = \Spreadsheet\Cell::stringFromColumnIndex($column) . $row;
478
                        } else {
479
                            $rowData[$column] = '';
480
                        }
481
                    }
482
                    $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...
483
                }
484
485
                // </thead> ?
486
                if ($row == $theadEnd) {
487
                    $html .= '        </thead>' . PHP_EOL;
488
                }
489
            }
490
            $html .= $this->extendRowsForChartsAndImages($sheet, $row);
491
492
            // Close table body.
493
            $html .= '        </tbody>' . PHP_EOL;
494
495
            // Write table footer
496
            $html .= $this->generateTableFooter();
497
498
            // Writing PDF?
499
            if ($this->isPdf) {
500
                if (is_null($this->sheetIndex) && $sheetId + 1 < $this->spreadsheet->getSheetCount()) {
501
                    $html .= '<div style="page-break-before:always" />';
502
                }
503
            }
504
505
            // Next sheet
506
            ++$sheetId;
507
        }
508
509
        return $html;
510
    }
511
512
    /**
513
     * Generate sheet tabs
514
     *
515
     * @return    string
516
     * @throws \Spreadsheet\Writer\Exception
517
     */
518
    public function generateNavigation()
519
    {
520
        // Spreadsheet object known?
521
        if (is_null($this->spreadsheet)) {
522
            throw new \Spreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
523
        }
524
525
        // Fetch sheets
526
        $sheets = array();
527 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...
528
            $sheets = $this->spreadsheet->getAllSheets();
529
        } else {
530
            $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex);
531
        }
532
533
        // Construct HTML
534
        $html = '';
535
536
        // Only if there are more than 1 sheets
537
        if (count($sheets) > 1) {
538
            // Loop all sheets
539
            $sheetId = 0;
540
541
            $html .= '<ul class="navigation">' . PHP_EOL;
542
543
            foreach ($sheets as $sheet) {
544
                $html .= '  <li class="sheet' . $sheetId . '"><a href="#sheet' . $sheetId . '">' . $sheet->getTitle() . '</a></li>' . PHP_EOL;
545
                ++$sheetId;
546
            }
547
548
            $html .= '</ul>' . PHP_EOL;
549
        }
550
551
        return $html;
552
    }
553
554
    private function extendRowsForChartsAndImages(\Spreadsheet\Worksheet $pSheet, $row)
555
    {
556
        $rowMax = $row;
557
        $colMax = 'A';
558
        if ($this->includeCharts) {
559
            foreach ($pSheet->getChartCollection() as $chart) {
560
                if ($chart instanceof Spreadsheet_Chart) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\Writer\Spreadsheet_Chart does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
561
                    $chartCoordinates = $chart->getTopLeftPosition();
562
                    $chartTL = \Spreadsheet\Cell::coordinateFromString($chartCoordinates['cell']);
563
                    $chartCol = \Spreadsheet\Cell::columnIndexFromString($chartTL[0]);
564
                    if ($chartTL[1] > $rowMax) {
565
                        $rowMax = $chartTL[1];
566
                        if ($chartCol > \Spreadsheet\Cell::columnIndexFromString($colMax)) {
567
                            $colMax = $chartTL[0];
568
                        }
569
                    }
570
                }
571
            }
572
        }
573
574
        foreach ($pSheet->getDrawingCollection() as $drawing) {
575
            if ($drawing instanceof \Spreadsheet\Worksheet\Drawing) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\Worksheet\Drawing does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
576
                $imageTL = \Spreadsheet\Cell::coordinateFromString($drawing->getCoordinates());
577
                $imageCol = \Spreadsheet\Cell::columnIndexFromString($imageTL[0]);
578
                if ($imageTL[1] > $rowMax) {
579
                    $rowMax = $imageTL[1];
580
                    if ($imageCol > \Spreadsheet\Cell::columnIndexFromString($colMax)) {
581
                        $colMax = $imageTL[0];
582
                    }
583
                }
584
            }
585
        }
586
        $html = '';
587
        $colMax++;
588
        while ($row <= $rowMax) {
589
            $html .= '<tr>';
590
            for ($col = 'A'; $col != $colMax; ++$col) {
591
                $html .= '<td>';
592
                $html .= $this->writeImageInCell($pSheet, $col.$row);
593
                if ($this->includeCharts) {
594
                    $html .= $this->writeChartInCell($pSheet, $col.$row);
595
                }
596
                $html .= '</td>';
597
            }
598
            ++$row;
599
            $html .= '</tr>';
600
        }
601
        return $html;
602
    }
603
604
605
    /**
606
     * Generate image tag in cell
607
     *
608
     * @param    \Spreadsheet\Worksheet    $pSheet            \Spreadsheet\Worksheet
609
     * @param    string                $coordinates    Cell coordinates
610
     * @return    string
611
     * @throws    \Spreadsheet\Writer\Exception
612
     */
613
    private function writeImageInCell(\Spreadsheet\Worksheet $pSheet, $coordinates)
614
    {
615
        // Construct HTML
616
        $html = '';
617
618
        // Write images
619
        foreach ($pSheet->getDrawingCollection() as $drawing) {
620
            if ($drawing instanceof \Spreadsheet\Worksheet\Drawing) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\Worksheet\Drawing does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
621
                if ($drawing->getCoordinates() == $coordinates) {
622
                    $filename = $drawing->getPath();
623
624
                    // Strip off eventual '.'
625
                    if (substr($filename, 0, 1) == '.') {
626
                        $filename = substr($filename, 1);
627
                    }
628
629
                    // Prepend images root
630
                    $filename = $this->getImagesRoot() . $filename;
631
632
                    // Strip off eventual '.'
633
                    if (substr($filename, 0, 1) == '.' && substr($filename, 0, 2) != './') {
634
                        $filename = substr($filename, 1);
635
                    }
636
637
                    // Convert UTF8 data to PCDATA
638
                    $filename = htmlspecialchars($filename);
639
640
                    $html .= PHP_EOL;
641
                    if ((!$this->embedImages) || ($this->isPdf)) {
642
                        $imageData = $filename;
643
                    } else {
644
                        $imageDetails = getimagesize($filename);
645
                        if ($fp = fopen($filename, "rb", 0)) {
646
                            $picture = fread($fp, filesize($filename));
647
                            fclose($fp);
648
                            // base64 encode the binary data, then break it
649
                            // into chunks according to RFC 2045 semantics
650
                            $base64 = chunk_split(base64_encode($picture));
651
                            $imageData = 'data:'.$imageDetails['mime'].';base64,' . $base64;
652
                        } else {
653
                            $imageData = $filename;
654
                        }
655
                    }
656
657
                    $html .= '<div style="position: relative;">';
658
                    $html .= '<img style="position: absolute; z-index: 1; left: ' .
659
                        $drawing->getOffsetX() . 'px; top: ' . $drawing->getOffsetY() . 'px; width: ' .
660
                        $drawing->getWidth() . 'px; height: ' . $drawing->getHeight() . 'px;" src="' .
661
                        $imageData . '" border="0" />';
662
                    $html .= '</div>';
663
                }
664
            } elseif ($drawing instanceof \Spreadsheet\Worksheet\MemoryDrawing) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\Worksheet\MemoryDrawing does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
665
                if ($drawing->getCoordinates() != $coordinates) {
666
                    continue;
667
                }
668
                ob_start();                                //  Let's start output buffering.
669
                imagepng($drawing->getImageResource());    //  This will normally output the image, but because of ob_start(), it won't.
670
                $contents = ob_get_contents();             //  Instead, output above is saved to $contents
671
                ob_end_clean();                            //  End the output buffer.
672
673
                $dataUri = "data:image/jpeg;base64," . base64_encode($contents);
674
675
                //  Because of the nature of tables, width is more important than height.
676
                //  max-width: 100% ensures that image doesnt overflow containing cell
677
                //  width: X sets width of supplied image.
678
                //  As a result, images bigger than cell will be contained and images smaller will not get stretched
679
                $html .= '<img src="'.$dataUri.'" style="max-width:100%;width:'.$drawing->getWidth().'px;" />';
680
            }
681
        }
682
683
        return $html;
684
    }
685
686
    /**
687
     * Generate chart tag in cell
688
     *
689
     * @param    \Spreadsheet\Worksheet    $pSheet            \Spreadsheet\Worksheet
690
     * @param    string                $coordinates    Cell coordinates
691
     * @return    string
692
     * @throws    \Spreadsheet\Writer\Exception
693
     */
694
    private function writeChartInCell(\Spreadsheet\Worksheet $pSheet, $coordinates)
695
    {
696
        // Construct HTML
697
        $html = '';
698
699
        // Write charts
700
        foreach ($pSheet->getChartCollection() as $chart) {
701
            if ($chart instanceof Spreadsheet_Chart) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\Writer\Spreadsheet_Chart does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
702
                $chartCoordinates = $chart->getTopLeftPosition();
703
                if ($chartCoordinates['cell'] == $coordinates) {
704
                    $chartFileName = \Spreadsheet\Shared\File::sysGetTempDir().'/'.uniqid().'.png';
705
                    if (!$chart->render($chartFileName)) {
706
                        return;
707
                    }
708
709
                    $html .= PHP_EOL;
710
                    $imageDetails = getimagesize($chartFileName);
711
                    if ($fp = fopen($chartFileName, "rb", 0)) {
712
                        $picture = fread($fp, filesize($chartFileName));
713
                        fclose($fp);
714
                        // base64 encode the binary data, then break it
715
                        // into chunks according to RFC 2045 semantics
716
                        $base64 = chunk_split(base64_encode($picture));
717
                        $imageData = 'data:'.$imageDetails['mime'].';base64,' . $base64;
718
719
                        $html .= '<div style="position: relative;">';
720
                        $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;
721
                        $html .= '</div>';
722
723
                        unlink($chartFileName);
724
                    }
725
                }
726
            }
727
        }
728
729
        // Return
730
        return $html;
731
    }
732
733
    /**
734
     * Generate CSS styles
735
     *
736
     * @param    boolean    $generateSurroundingHTML    Generate surrounding HTML tags? (&lt;style&gt; and &lt;/style&gt;)
737
     * @return    string
738
     * @throws    \Spreadsheet\Writer\Exception
739
     */
740
    public function generateStyles($generateSurroundingHTML = true)
741
    {
742
        // Spreadsheet object known?
743
        if (is_null($this->spreadsheet)) {
744
            throw new \Spreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
745
        }
746
747
        // Build CSS
748
        $css = $this->buildCSS($generateSurroundingHTML);
749
750
        // Construct HTML
751
        $html = '';
752
753
        // Start styles
754
        if ($generateSurroundingHTML) {
755
            $html .= '    <style type="text/css">' . PHP_EOL;
756
            $html .= '      html { ' . $this->assembleCSS($css['html']) . ' }' . PHP_EOL;
757
        }
758
759
        // Write all other styles
760
        foreach ($css as $styleName => $styleDefinition) {
761
            if ($styleName != 'html') {
762
                $html .= '      ' . $styleName . ' { ' . $this->assembleCSS($styleDefinition) . ' }' . PHP_EOL;
763
            }
764
        }
765
766
        // End styles
767
        if ($generateSurroundingHTML) {
768
            $html .= '    </style>' . PHP_EOL;
769
        }
770
771
        // Return
772
        return $html;
773
    }
774
775
    /**
776
     * Build CSS styles
777
     *
778
     * @param    boolean    $generateSurroundingHTML    Generate surrounding HTML style? (html { })
779
     * @return    array
780
     * @throws    \Spreadsheet\Writer\Exception
781
     */
782
    public function buildCSS($generateSurroundingHTML = true)
783
    {
784
        // Spreadsheet object known?
785
        if (is_null($this->spreadsheet)) {
786
            throw new \Spreadsheet\Writer\Exception('Internal Spreadsheet object not set to an instance of an object.');
787
        }
788
789
        // Cached?
790
        if (!is_null($this->cssStyles)) {
791
            return $this->cssStyles;
792
        }
793
794
        // Ensure that spans have been calculated
795
        if (!$this->spansAreCalculated) {
796
            $this->calculateSpans();
797
        }
798
799
        // Construct CSS
800
        $css = array();
801
802
        // Start styles
803
        if ($generateSurroundingHTML) {
804
            // html { }
805
            $css['html']['font-family']      = 'Calibri, Arial, Helvetica, sans-serif';
806
            $css['html']['font-size']        = '11pt';
807
            $css['html']['background-color'] = 'white';
808
        }
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 = array();
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 = \Spreadsheet\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 = \Spreadsheet\Shared\Drawing::cellDimensionToPixels($columnDimension->getWidth(), $this->defaultFont)) >= 0) {
873
                    $width = \Spreadsheet\Shared\Drawing::pixelsToPoints($width);
874
                    $column = \Spreadsheet\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'] = array();
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] = array();
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    \Spreadsheet\Style        $pStyle            Spreadsheet_Style
935
     * @return    array
936
     */
937
    private function createCSSStyle(\Spreadsheet\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 (\Spreadsheet\Style\Alignment)
956
     *
957
     * @param    \Spreadsheet\Style\Alignment        $pStyle            \Spreadsheet\Style\Alignment
958
     * @return    array
959
     */
960
    private function createCSSStyleAlignment(\Spreadsheet\Style\Alignment $pStyle)
961
    {
962
        // Construct CSS
963
        $css = array();
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, array('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 (\Spreadsheet\Style\Font)
979
     *
980
     * @param    \Spreadsheet\Style\Font        $pStyle            \Spreadsheet\Style\Font
981
     * @return    array
982
     */
983
    private function createCSSStyleFont(\Spreadsheet\Style\Font $pStyle)
984
    {
985
        // Construct CSS
986
        $css = array();
987
988
        // Create CSS
989
        if ($pStyle->getBold()) {
990
            $css['font-weight'] = 'bold';
991
        }
992
        if ($pStyle->getUnderline() != \Spreadsheet\Style\Font::UNDERLINE_NONE && $pStyle->getStrikethrough()) {
993
            $css['text-decoration'] = 'underline line-through';
994
        } elseif ($pStyle->getUnderline() != \Spreadsheet\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 (\Spreadsheet\Style\Borders)
1012
     *
1013
     * @param    \Spreadsheet\Style\Borders        $pStyle            \Spreadsheet\Style\Borders
1014
     * @return    array
1015
     */
1016
    private function createCSSStyleBorders(\Spreadsheet\Style\Borders $pStyle)
1017
    {
1018
        // Construct CSS
1019
        $css = array();
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 (\Spreadsheet\Style\Border)
1032
     *
1033
     * @param    \Spreadsheet\Style\Border        $pStyle            \Spreadsheet\Style\Border
1034
     * @return    string
1035
     */
1036
    private function createCSSStyleBorder(\Spreadsheet\Style\Border $pStyle)
1037
    {
1038
        // Create CSS
0 ignored issues
show
Unused Code Comprehensibility introduced by
49% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1039
//        $css = $this->mapBorderStyle($pStyle->getBorderStyle()) . ' #' . $pStyle->getColor()->getRGB();
1040
        //    Create CSS - add !important to non-none border styles for merged cells
1041
        $borderStyle = $this->mapBorderStyle($pStyle->getBorderStyle());
1042
        $css = $borderStyle . ' #' . $pStyle->getColor()->getRGB() . (($borderStyle == 'none') ? '' : ' !important');
1043
1044
        return $css;
1045
    }
1046
1047
    /**
1048
     * Create CSS style (\Spreadsheet\Style\Fill)
1049
     *
1050
     * @param    \Spreadsheet\Style\Fill        $pStyle            \Spreadsheet\Style\Fill
1051
     * @return    array
1052
     */
1053
    private function createCSSStyleFill(\Spreadsheet\Style\Fill $pStyle)
1054
    {
1055
        // Construct HTML
1056
        $css = array();
1057
1058
        // Create CSS
1059
        $value = $pStyle->getFillType() == \Spreadsheet\Style\Fill::FILL_NONE ?
1060
            'white' : '#' . $pStyle->getStartColor()->getRGB();
1061
        $css['background-color'] = $value;
1062
1063
        return $css;
1064
    }
1065
1066
    /**
1067
     * Generate HTML footer
1068
     */
1069
    public function generateHTMLFooter()
1070
    {
1071
        // Construct HTML
1072
        $html = '';
1073
        $html .= '  </body>' . PHP_EOL;
1074
        $html .= '</html>' . PHP_EOL;
1075
1076
        return $html;
1077
    }
1078
1079
    /**
1080
     * Generate table header
1081
     *
1082
     * @param    \Spreadsheet\Worksheet    $pSheet        The worksheet for the table we are writing
1083
     * @return    string
1084
     * @throws    \Spreadsheet\Writer\Exception
1085
     */
1086
    private function generateTableHeader($pSheet)
1087
    {
1088
        $sheetIndex = $pSheet->getParent()->getIndex($pSheet);
1089
1090
        // Construct HTML
1091
        $html = '';
1092
        $html .= $this->setMargins($pSheet);
1093
1094
        if (!$this->useInlineCss) {
1095
            $gridlines = $pSheet->getShowGridlines() ? ' gridlines' : '';
1096
            $html .= '    <table border="0" cellpadding="0" cellspacing="0" id="sheet' . $sheetIndex . '" class="sheet' . $sheetIndex . $gridlines . '">' . PHP_EOL;
1097
        } else {
1098
            $style = isset($this->cssStyles['table']) ?
1099
                $this->assembleCSS($this->cssStyles['table']) : '';
1100
1101
            if ($this->isPdf && $pSheet->getShowGridlines()) {
1102
                $html .= '    <table border="1" cellpadding="1" id="sheet' . $sheetIndex . '" cellspacing="1" style="' . $style . '">' . PHP_EOL;
1103
            } else {
1104
                $html .= '    <table border="0" cellpadding="1" id="sheet' . $sheetIndex . '" cellspacing="0" style="' . $style . '">' . PHP_EOL;
1105
            }
1106
        }
1107
1108
        // Write <col> elements
1109
        $highestColumnIndex = \Spreadsheet\Cell::columnIndexFromString($pSheet->getHighestColumn()) - 1;
1110
        $i = -1;
1111
        while ($i++ < $highestColumnIndex) {
1112 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...
1113
                if (!$this->useInlineCss) {
1114
                    $html .= '        <col class="col' . $i . '">' . PHP_EOL;
1115
                } else {
1116
                    $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i]) ?
1117
                        $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i]) : '';
1118
                    $html .= '        <col style="' . $style . '">' . PHP_EOL;
1119
                }
1120
            }
1121
        }
1122
1123
        return $html;
1124
    }
1125
1126
    /**
1127
     * Generate table footer
1128
     *
1129
     * @throws    \Spreadsheet\Writer\Exception
1130
     */
1131
    private function generateTableFooter()
1132
    {
1133
        $html = '    </table>' . PHP_EOL;
1134
1135
        return $html;
1136
    }
1137
1138
    /**
1139
     * Generate row
1140
     *
1141
     * @param    \Spreadsheet\Worksheet    $pSheet            \Spreadsheet\Worksheet
1142
     * @param    array                $pValues        Array containing cells in a row
1143
     * @param    int                    $pRow            Row number (0-based)
1144
     * @return    string
1145
     * @throws    \Spreadsheet\Writer\Exception
1146
     */
1147
    private function generateRow(\Spreadsheet\Worksheet $pSheet, $pValues = null, $pRow = 0, $cellType = 'td')
1148
    {
1149
        if (is_array($pValues)) {
1150
            // Construct HTML
1151
            $html = '';
1152
1153
            // Sheet index
1154
            $sheetIndex = $pSheet->getParent()->getIndex($pSheet);
1155
1156
            // DomPDF and breaks
1157
            if ($this->isPdf && count($pSheet->getBreaks()) > 0) {
1158
                $breaks = $pSheet->getBreaks();
1159
1160
                // check if a break is needed before this row
1161
                if (isset($breaks['A' . $pRow])) {
1162
                    // close table: </table>
1163
                    $html .= $this->generateTableFooter();
1164
1165
                    // insert page break
1166
                    $html .= '<div style="page-break-before:always" />';
1167
1168
                    // open table again: <table> + <col> etc.
1169
                    $html .= $this->generateTableHeader($pSheet);
1170
                }
1171
            }
1172
1173
            // Write row start
1174 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...
1175
                $html .= '          <tr class="row' . $pRow . '">' . PHP_EOL;
1176
            } else {
1177
                $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow])
1178
                    ? $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]) : '';
1179
1180
                $html .= '          <tr style="' . $style . '">' . PHP_EOL;
1181
            }
1182
1183
            // Write cells
1184
            $colNum = 0;
1185
            foreach ($pValues as $cellAddress) {
1186
                $cell = ($cellAddress > '') ? $pSheet->getCell($cellAddress) : '';
1187
                $coordinate = \Spreadsheet\Cell::stringFromColumnIndex($colNum) . ($pRow + 1);
1188
                if (!$this->useInlineCss) {
1189
                    $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...
1190
                    $cssClass = 'column' . $colNum;
1191
                } else {
1192
                    $cssClass = array();
1193
                    if ($cellType == 'th') {
1194 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...
1195
                            $this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum];
1196
                        }
1197 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...
1198
                        if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum])) {
1199
                            $this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum];
1200
                        }
1201
                    }
1202
                }
1203
                $colSpan = 1;
1204
                $rowSpan = 1;
1205
1206
                // initialize
1207
                $cellData = '&nbsp;';
1208
1209
                // \Spreadsheet\Cell
1210
                if ($cell instanceof \Spreadsheet\Cell) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\Cell does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1211
                    $cellData = '';
1212
                    if (is_null($cell->getParent())) {
1213
                        $cell->attach($pSheet);
1214
                    }
1215
                    // Value
1216
                    if ($cell->getValue() instanceof \Spreadsheet\RichText) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\RichText does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1217
                        // Loop through rich text elements
1218
                        $elements = $cell->getValue()->getRichTextElements();
1219
                        foreach ($elements as $element) {
1220
                            // Rich text start?
1221
                            if ($element instanceof \Spreadsheet\RichText\Run) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\RichText\Run does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1222
                                $cellData .= '<span style="' . $this->assembleCSS($this->createCSSStyleFont($element->getFont())) . '">';
1223
1224
                                if ($element->getFont()->getSuperScript()) {
1225
                                    $cellData .= '<sup>';
1226
                                } elseif ($element->getFont()->getSubScript()) {
1227
                                    $cellData .= '<sub>';
1228
                                }
1229
                            }
1230
1231
                            // Convert UTF8 data to PCDATA
1232
                            $cellText = $element->getText();
1233
                            $cellData .= htmlspecialchars($cellText);
1234
1235
                            if ($element instanceof \Spreadsheet\RichText\Run) {
0 ignored issues
show
Bug introduced by
The class Spreadsheet\RichText\Run does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1236
                                if ($element->getFont()->getSuperScript()) {
1237
                                    $cellData .= '</sup>';
1238
                                } elseif ($element->getFont()->getSubScript()) {
1239
                                    $cellData .= '</sub>';
1240
                                }
1241
1242
                                $cellData .= '</span>';
1243
                            }
1244
                        }
1245
                    } else {
1246
                        if ($this->preCalculateFormulas) {
1247
                            $cellData = \Spreadsheet\Style\NumberFormat::toFormattedString(
1248
                                $cell->getCalculatedValue(),
1249
                                $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(),
1250
                                array($this, 'formatColor')
1251
                            );
1252
                        } else {
1253
                            $cellData = \Spreadsheet\Style\NumberFormat::toFormattedString(
1254
                                $cell->getValue(),
1255
                                $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(),
1256
                                array($this, 'formatColor')
1257
                            );
1258
                        }
1259
                        $cellData = htmlspecialchars($cellData);
1260
                        if ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSuperScript()) {
1261
                            $cellData = '<sup>'.$cellData.'</sup>';
1262
                        } elseif ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSubScript()) {
1263
                            $cellData = '<sub>'.$cellData.'</sub>';
1264
                        }
1265
                    }
1266
1267
                    // Converts the cell content so that spaces occuring at beginning of each new line are replaced by &nbsp;
1268
                    // Example: "  Hello\n to the world" is converted to "&nbsp;&nbsp;Hello\n&nbsp;to the world"
1269
                    $cellData = preg_replace("/(?m)(?:^|\\G) /", '&nbsp;', $cellData);
1270
1271
                    // convert newline "\n" to '<br>'
1272
                    $cellData = nl2br($cellData);
1273
1274
                    // Extend CSS class?
1275
                    if (!$this->useInlineCss) {
1276
                        $cssClass .= ' style' . $cell->getXfIndex();
1277
                        $cssClass .= ' ' . $cell->getDataType();
1278
                    } else {
1279
                        if ($cellType == 'th') {
1280 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...
1281
                                $cssClass = array_merge($cssClass, $this->cssStyles['th.style' . $cell->getXfIndex()]);
1282
                            }
1283 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...
1284
                            if (isset($this->cssStyles['td.style' . $cell->getXfIndex()])) {
1285
                                $cssClass = array_merge($cssClass, $this->cssStyles['td.style' . $cell->getXfIndex()]);
1286
                            }
1287
                        }
1288
1289
                        // General horizontal alignment: Actual horizontal alignment depends on dataType
1290
                        $sharedStyle = $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex());
1291
                        if ($sharedStyle->getAlignment()->getHorizontal() == \Spreadsheet\Style\Alignment::HORIZONTAL_GENERAL
1292
                            && isset($this->cssStyles['.' . $cell->getDataType()]['text-align'])) {
1293
                            $cssClass['text-align'] = $this->cssStyles['.' . $cell->getDataType()]['text-align'];
1294
                        }
1295
                    }
1296
                }
1297
1298
                // Hyperlink?
1299
                if ($pSheet->hyperlinkExists($coordinate) && !$pSheet->getHyperlink($coordinate)->isInternal()) {
1300
                    $cellData = '<a href="' . htmlspecialchars($pSheet->getHyperlink($coordinate)->getUrl()) . '" title="' . htmlspecialchars($pSheet->getHyperlink($coordinate)->getTooltip()) . '">' . $cellData . '</a>';
1301
                }
1302
1303
                // Should the cell be written or is it swallowed by a rowspan or colspan?
1304
                $writeCell = !(isset($this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])
1305
                            && $this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum]);
1306
1307
                // Colspan and Rowspan
1308
                $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...
1309
                $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...
1310
                if (isset($this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])) {
1311
                    $spans = $this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum];
1312
                    $rowSpan = $spans['rowspan'];
1313
                    $colSpan = $spans['colspan'];
1314
1315
                    //    Also apply style from last cell in merge to fix borders -
1316
                    //        relies on !important for non-none border declarations in createCSSStyleBorder
1317
                    $endCellCoord = \Spreadsheet\Cell::stringFromColumnIndex($colNum + $colSpan - 1) . ($pRow + $rowSpan);
1318
                    if (!$this->useInlineCss) {
1319
                        $cssClass .= ' style' . $pSheet->getCell($endCellCoord)->getXfIndex();
1320
                    }
1321
                }
1322
1323
                // Write
1324
                if ($writeCell) {
1325
                    // Column start
1326
                    $html .= '            <' . $cellType;
1327
                    if (!$this->useInlineCss) {
1328
                        $html .= ' class="' . $cssClass . '"';
1329
                    } else {
1330
                        //** Necessary redundant code for the sake of Spreadsheet_Writer_PDF **
1331
                        // We must explicitly write the width of the <td> element because TCPDF
1332
                        // does not recognize e.g. <col style="width:42pt">
1333
                        $width = 0;
1334
                        $i = $colNum - 1;
1335
                        $e = $colNum + $colSpan - 1;
1336
                        while ($i++ < $e) {
1337
                            if (isset($this->columnWidths[$sheetIndex][$i])) {
1338
                                $width += $this->columnWidths[$sheetIndex][$i];
1339
                            }
1340
                        }
1341
                        $cssClass['width'] = $width . 'pt';
1342
1343
                        // We must also explicitly write the height of the <td> element because TCPDF
1344
                        // does not recognize e.g. <tr style="height:50pt">
1345
                        if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'])) {
1346
                            $height = $this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'];
1347
                            $cssClass['height'] = $height;
1348
                        }
1349
                        //** end of redundant code **
1350
1351
                        $html .= ' style="' . $this->assembleCSS($cssClass) . '"';
1352
                    }
1353
                    if ($colSpan > 1) {
1354
                        $html .= ' colspan="' . $colSpan . '"';
1355
                    }
1356
                    if ($rowSpan > 1) {
1357
                        $html .= ' rowspan="' . $rowSpan . '"';
1358
                    }
1359
                    $html .= '>';
1360
1361
                    // Image?
1362
                    $html .= $this->writeImageInCell($pSheet, $coordinate);
1363
1364
                    // Chart?
1365
                    if ($this->includeCharts) {
1366
                        $html .= $this->writeChartInCell($pSheet, $coordinate);
1367
                    }
1368
1369
                    // Cell data
1370
                    $html .= $cellData;
1371
1372
                    // Column end
1373
                    $html .= '</'.$cellType.'>' . PHP_EOL;
1374
                }
1375
1376
                // Next column
1377
                ++$colNum;
1378
            }
1379
1380
            // Write row end
1381
            $html .= '          </tr>' . PHP_EOL;
1382
1383
            // Return
1384
            return $html;
1385
        } else {
1386
            throw new \Spreadsheet\Writer\Exception("Invalid parameters passed.");
1387
        }
1388
    }
1389
1390
    /**
1391
     * Takes array where of CSS properties / values and converts to CSS string
1392
     *
1393
     * @param array
1394
     * @return string
1395
     */
1396
    private function assembleCSS($pValue = array())
1397
    {
1398
        $pairs = array();
1399
        foreach ($pValue as $property => $value) {
1400
            $pairs[] = $property . ':' . $value;
1401
        }
1402
        $string = implode('; ', $pairs);
1403
1404
        return $string;
1405
    }
1406
1407
    /**
1408
     * Get images root
1409
     *
1410
     * @return string
1411
     */
1412
    public function getImagesRoot()
1413
    {
1414
        return $this->imagesRoot;
1415
    }
1416
1417
    /**
1418
     * Set images root
1419
     *
1420
     * @param string $pValue
1421
     * @return Spreadsheet_Writer_HTML
1422
     */
1423
    public function setImagesRoot($pValue = '.')
1424
    {
1425
        $this->imagesRoot = $pValue;
1426
        return $this;
1427
    }
1428
1429
    /**
1430
     * Get embed images
1431
     *
1432
     * @return boolean
1433
     */
1434
    public function getEmbedImages()
1435
    {
1436
        return $this->embedImages;
1437
    }
1438
1439
    /**
1440
     * Set embed images
1441
     *
1442
     * @param boolean $pValue
1443
     * @return Spreadsheet_Writer_HTML
1444
     */
1445
    public function setEmbedImages($pValue = '.')
1446
    {
1447
        $this->embedImages = $pValue;
0 ignored issues
show
Documentation Bug introduced by
It seems like $pValue can also be of type string. However, the property $embedImages is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1448
        return $this;
1449
    }
1450
1451
    /**
1452
     * Get use inline CSS?
1453
     *
1454
     * @return boolean
1455
     */
1456
    public function getUseInlineCss()
1457
    {
1458
        return $this->useInlineCss;
1459
    }
1460
1461
    /**
1462
     * Set use inline CSS?
1463
     *
1464
     * @param boolean $pValue
1465
     * @return Spreadsheet_Writer_HTML
1466
     */
1467
    public function setUseInlineCss($pValue = false)
1468
    {
1469
        $this->useInlineCss = $pValue;
1470
        return $this;
1471
    }
1472
1473
    /**
1474
     * Add color to formatted string as inline style
1475
     *
1476
     * @param string $pValue Plain formatted value without color
1477
     * @param string $pFormat Format code
1478
     * @return string
1479
     */
1480
    public function formatColor($pValue, $pFormat)
1481
    {
1482
        // Color information, e.g. [Red] is always at the beginning
1483
        $color = null; // initialize
1484
        $matches = array();
1485
1486
        $color_regex = '/^\\[[a-zA-Z]+\\]/';
1487
        if (preg_match($color_regex, $pFormat, $matches)) {
1488
            $color = str_replace('[', '', $matches[0]);
1489
            $color = str_replace(']', '', $color);
1490
            $color = strtolower($color);
1491
        }
1492
1493
        // convert to PCDATA
1494
        $value = htmlspecialchars($pValue);
1495
1496
        // color span tag
1497
        if ($color !== null) {
1498
            $value = '<span style="color:' . $color . '">' . $value . '</span>';
1499
        }
1500
1501
        return $value;
1502
    }
1503
1504
    /**
1505
     * Calculate information about HTML colspan and rowspan which is not always the same as Excel's
1506
     */
1507
    private function calculateSpans()
1508
    {
1509
        // Identify all cells that should be omitted in HTML due to cell merge.
1510
        // In HTML only the upper-left cell should be written and it should have
1511
        //   appropriate rowspan / colspan attribute
1512
        $sheetIndexes = $this->sheetIndex !== null ?
1513
            array($this->sheetIndex) : range(0, $this->spreadsheet->getSheetCount() - 1);
1514
1515
        foreach ($sheetIndexes as $sheetIndex) {
1516
            $sheet = $this->spreadsheet->getSheet($sheetIndex);
1517
1518
            $candidateSpannedRow  = array();
1519
1520
            // loop through all Excel merged cells
1521
            foreach ($sheet->getMergeCells() as $cells) {
1522
                list($cells,) = \Spreadsheet\Cell::splitRange($cells);
1523
                $first = $cells[0];
1524
                $last  = $cells[1];
1525
1526
                list($fc, $fr) = \Spreadsheet\Cell::coordinateFromString($first);
1527
                $fc = \Spreadsheet\Cell::columnIndexFromString($fc) - 1;
1528
1529
                list($lc, $lr) = \Spreadsheet\Cell::coordinateFromString($last);
1530
                $lc = \Spreadsheet\Cell::columnIndexFromString($lc) - 1;
1531
1532
                // loop through the individual cells in the individual merge
1533
                $r = $fr - 1;
1534
                while ($r++ < $lr) {
1535
                    // also, flag this row as a HTML row that is candidate to be omitted
1536
                    $candidateSpannedRow[$r] = $r;
1537
1538
                    $c = $fc - 1;
1539
                    while ($c++ < $lc) {
1540
                        if (!($c == $fc && $r == $fr)) {
1541
                            // not the upper-left cell (should not be written in HTML)
1542
                            $this->isSpannedCell[$sheetIndex][$r][$c] = array(
1543
                                'baseCell' => array($fr, $fc),
1544
                            );
1545
                        } else {
1546
                            // upper-left is the base cell that should hold the colspan/rowspan attribute
1547
                            $this->isBaseCell[$sheetIndex][$r][$c] = array(
1548
                                'xlrowspan' => $lr - $fr + 1, // Excel rowspan
1549
                                'rowspan'   => $lr - $fr + 1, // HTML rowspan, value may change
1550
                                'xlcolspan' => $lc - $fc + 1, // Excel colspan
1551
                                'colspan'   => $lc - $fc + 1, // HTML colspan, value may change
1552
                            );
1553
                        }
1554
                    }
1555
                }
1556
            }
1557
1558
            // Identify which rows should be omitted in HTML. These are the rows where all the cells
1559
            //   participate in a merge and the where base cells are somewhere above.
1560
            $countColumns = \Spreadsheet\Cell::columnIndexFromString($sheet->getHighestColumn());
1561
            foreach ($candidateSpannedRow as $rowIndex) {
1562
                if (isset($this->isSpannedCell[$sheetIndex][$rowIndex])) {
1563
                    if (count($this->isSpannedCell[$sheetIndex][$rowIndex]) == $countColumns) {
1564
                        $this->isSpannedRow[$sheetIndex][$rowIndex] = $rowIndex;
1565
                    };
1566
                }
1567
            }
1568
1569
            // For each of the omitted rows we found above, the affected rowspans should be subtracted by 1
1570
            if (isset($this->isSpannedRow[$sheetIndex])) {
1571
                foreach ($this->isSpannedRow[$sheetIndex] as $rowIndex) {
1572
                    $adjustedBaseCells = array();
1573
                    $c = -1;
1574
                    $e = $countColumns - 1;
1575
                    while ($c++ < $e) {
1576
                        $baseCell = $this->isSpannedCell[$sheetIndex][$rowIndex][$c]['baseCell'];
1577
1578
                        if (!in_array($baseCell, $adjustedBaseCells)) {
1579
                            // subtract rowspan by 1
1580
                            --$this->isBaseCell[$sheetIndex][ $baseCell[0] ][ $baseCell[1] ]['rowspan'];
1581
                            $adjustedBaseCells[] = $baseCell;
1582
                        }
1583
                    }
1584
                }
1585
            }
1586
1587
            // TODO: Same for columns
1588
        }
1589
1590
        // We have calculated the spans
1591
        $this->spansAreCalculated = true;
1592
    }
1593
1594
    private function setMargins(\Spreadsheet\Worksheet $pSheet)
1595
    {
1596
        $htmlPage = '@page { ';
1597
        $htmlBody = 'body { ';
1598
1599
        $left = StringHelper::formatNumber($pSheet->getPageMargins()->getLeft()) . 'in; ';
1600
        $htmlPage .= 'margin-left: ' . $left;
1601
        $htmlBody .= 'margin-left: ' . $left;
1602
        $right = StringHelper::formatNumber($pSheet->getPageMargins()->getRight()) . 'in; ';
1603
        $htmlPage .= 'margin-right: ' . $right;
1604
        $htmlBody .= 'margin-right: ' . $right;
1605
        $top = StringHelper::formatNumber($pSheet->getPageMargins()->getTop()) . 'in; ';
1606
        $htmlPage .= 'margin-top: ' . $top;
1607
        $htmlBody .= 'margin-top: ' . $top;
1608
        $bottom = StringHelper::formatNumber($pSheet->getPageMargins()->getBottom()) . 'in; ';
1609
        $htmlPage .= 'margin-bottom: ' . $bottom;
1610
        $htmlBody .= 'margin-bottom: ' . $bottom;
1611
1612
        $htmlPage .= "}\n";
1613
        $htmlBody .= "}\n";
1614
1615
        return "<style>\n" . $htmlPage . $htmlBody . "</style>\n";
1616
    }
1617
}
1618