Failed Conditions
Push — master ( 4b6ad7...9fab89 )
by Adrien
06:57
created

Html::loadIntoExisting()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.1406

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 7
nc 3
nop 2
dl 0
loc 16
ccs 6
cts 8
cp 0.75
crap 3.1406
rs 10
c 1
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader;
4
5
use DOMDocument;
6
use DOMElement;
7
use DOMNode;
8
use DOMText;
9
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
10
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
11
use PhpOffice\PhpSpreadsheet\Spreadsheet;
12
use PhpOffice\PhpSpreadsheet\Style\Border;
13
use PhpOffice\PhpSpreadsheet\Style\Color;
14
use PhpOffice\PhpSpreadsheet\Style\Fill;
15
use PhpOffice\PhpSpreadsheet\Style\Font;
16
use PhpOffice\PhpSpreadsheet\Style\Style;
17
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
18
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
19
20
/** PhpSpreadsheet root directory */
21
class Html extends BaseReader
22
{
23
    /**
24
     * Sample size to read to determine if it's HTML or not.
25
     */
26
    const TEST_SAMPLE_SIZE = 2048;
27
28
    /**
29
     * Input encoding.
30
     *
31
     * @var string
32
     */
33
    protected $inputEncoding = 'ANSI';
34
35
    /**
36
     * Sheet index to read.
37
     *
38
     * @var int
39
     */
40
    protected $sheetIndex = 0;
41
42
    /**
43
     * Formats.
44
     *
45
     * @var array
46
     */
47
    protected $formats = [
48
        'h1' => [
49
            'font' => [
50
                'bold' => true,
51
                'size' => 24,
52
            ],
53
        ], //    Bold, 24pt
54
        'h2' => [
55
            'font' => [
56
                'bold' => true,
57
                'size' => 18,
58
            ],
59
        ], //    Bold, 18pt
60
        'h3' => [
61
            'font' => [
62
                'bold' => true,
63
                'size' => 13.5,
64
            ],
65
        ], //    Bold, 13.5pt
66
        'h4' => [
67
            'font' => [
68
                'bold' => true,
69
                'size' => 12,
70
            ],
71
        ], //    Bold, 12pt
72
        'h5' => [
73
            'font' => [
74
                'bold' => true,
75
                'size' => 10,
76
            ],
77
        ], //    Bold, 10pt
78
        'h6' => [
79
            'font' => [
80
                'bold' => true,
81
                'size' => 7.5,
82
            ],
83
        ], //    Bold, 7.5pt
84
        'a' => [
85
            'font' => [
86
                'underline' => true,
87
                'color' => [
88
                    'argb' => Color::COLOR_BLUE,
89
                ],
90
            ],
91
        ], //    Blue underlined
92
        'hr' => [
93
            'borders' => [
94
                'bottom' => [
95
                    'borderStyle' => Border::BORDER_THIN,
96
                    'color' => [
97
                        Color::COLOR_BLACK,
98
                    ],
99
                ],
100
            ],
101
        ], //    Bottom border
102
        'strong' => [
103
            'font' => [
104
                'bold' => true,
105
            ],
106
        ], //    Bold
107
        'b' => [
108
            'font' => [
109
                'bold' => true,
110
            ],
111
        ], //    Bold
112
        'i' => [
113
            'font' => [
114
                'italic' => true,
115
            ],
116
        ], //    Italic
117
        'em' => [
118
            'font' => [
119
                'italic' => true,
120
            ],
121
        ], //    Italic
122
    ];
123
124
    protected $rowspan = [];
125
126
    /**
127
     * Create a new HTML Reader instance.
128
     */
129 32
    public function __construct()
130
    {
131 32
        parent::__construct();
132 32
        $this->securityScanner = XmlScanner::getInstance($this);
133 32
    }
134
135
    /**
136
     * Validate that the current file is an HTML file.
137
     *
138
     * @param string $pFilename
139
     *
140
     * @return bool
141
     */
142 28
    public function canRead($pFilename)
143
    {
144
        // Check if file exists
145
        try {
146 28
            $this->openFile($pFilename);
147
        } catch (Exception $e) {
148
            return false;
149
        }
150
151 28
        $beginning = $this->readBeginning();
152 28
        $startWithTag = self::startsWithTag($beginning);
153 28
        $containsTags = self::containsTags($beginning);
154 28
        $endsWithTag = self::endsWithTag($this->readEnding());
155
156 28
        fclose($this->fileHandle);
1 ignored issue
show
Bug introduced by
It seems like $this->fileHandle can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

156
        fclose(/** @scrutinizer ignore-type */ $this->fileHandle);
Loading history...
157
158 28
        return $startWithTag && $containsTags && $endsWithTag;
159
    }
160
161 28
    private function readBeginning()
162
    {
163 28
        fseek($this->fileHandle, 0);
164
165 28
        return fread($this->fileHandle, self::TEST_SAMPLE_SIZE);
166
    }
167
168 28
    private function readEnding()
169
    {
170 28
        $meta = stream_get_meta_data($this->fileHandle);
171 28
        $filename = $meta['uri'];
172
173 28
        $size = filesize($filename);
174 28
        if ($size === 0) {
175 1
            return '';
176
        }
177
178 27
        $blockSize = self::TEST_SAMPLE_SIZE;
179 27
        if ($size < $blockSize) {
180 13
            $blockSize = $size;
181
        }
182
183 27
        fseek($this->fileHandle, $size - $blockSize);
184
185 27
        return fread($this->fileHandle, $blockSize);
186
    }
187
188 28
    private static function startsWithTag($data)
189
    {
190 28
        return '<' === substr(trim($data), 0, 1);
191
    }
192
193 28
    private static function endsWithTag($data)
194
    {
195 28
        return '>' === substr(trim($data), -1, 1);
196
    }
197
198 28
    private static function containsTags($data)
199
    {
200 28
        return strlen($data) !== strlen(strip_tags($data));
201
    }
202
203
    /**
204
     * Loads Spreadsheet from file.
205
     *
206
     * @param string $pFilename
207
     *
208
     * @throws Exception
209
     *
210
     * @return Spreadsheet
211
     */
212 21
    public function load($pFilename)
213
    {
214
        // Create new Spreadsheet
215 21
        $spreadsheet = new Spreadsheet();
216
217
        // Load into this instance
218 21
        return $this->loadIntoExisting($pFilename, $spreadsheet);
219
    }
220
221
    /**
222
     * Set input encoding.
223
     *
224
     * @param string $pValue Input encoding, eg: 'ANSI'
225
     *
226
     * @return Html
227
     */
228 2
    public function setInputEncoding($pValue)
229
    {
230 2
        $this->inputEncoding = $pValue;
231
232 2
        return $this;
233
    }
234
235
    /**
236
     * Get input encoding.
237
     *
238
     * @return string
239
     */
240
    public function getInputEncoding()
241
    {
242
        return $this->inputEncoding;
243
    }
244
245
    //    Data Array used for testing only, should write to Spreadsheet object on completion of tests
246
    protected $dataArray = [];
247
248
    protected $tableLevel = 0;
249
250
    protected $nestedColumn = ['A'];
251
252 23
    protected function setTableStartColumn($column)
253
    {
254 23
        if ($this->tableLevel == 0) {
255 23
            $column = 'A';
256
        }
257 23
        ++$this->tableLevel;
258 23
        $this->nestedColumn[$this->tableLevel] = $column;
259
260 23
        return $this->nestedColumn[$this->tableLevel];
261
    }
262
263 23
    protected function getTableStartColumn()
264
    {
265 23
        return $this->nestedColumn[$this->tableLevel];
266
    }
267
268 23
    protected function releaseTableStartColumn()
269
    {
270 23
        --$this->tableLevel;
271
272 23
        return array_pop($this->nestedColumn);
273
    }
274
275 23
    protected function flushCell(Worksheet $sheet, $column, $row, &$cellContent)
276
    {
277 23
        if (is_string($cellContent)) {
278
            //    Simple String content
279 23
            if (trim($cellContent) > '') {
280
                //    Only actually write it if there's content in the string
281
                //    Write to worksheet to be done here...
282
                //    ... we return the cell so we can mess about with styles more easily
283 22
                $sheet->setCellValue($column . $row, $cellContent);
284 23
                $this->dataArray[$row][$column] = $cellContent;
285
            }
286
        } else {
287
            //    We have a Rich Text run
288
            //    TODO
289
            $this->dataArray[$row][$column] = 'RICH TEXT: ' . $cellContent;
290
        }
291 23
        $cellContent = (string) '';
292 23
    }
293
294
    /**
295
     * @param DOMNode $element
296
     * @param Worksheet $sheet
297
     * @param int $row
298
     * @param string $column
299
     * @param string $cellContent
300
     */
301 23
    protected function processDomElement(DOMNode $element, Worksheet $sheet, &$row, &$column, &$cellContent)
302
    {
303 23
        foreach ($element->childNodes as $child) {
304 23
            if ($child instanceof DOMText) {
305 23
                $domText = preg_replace('/\s+/u', ' ', trim($child->nodeValue));
306 23
                if (is_string($cellContent)) {
307
                    //    simply append the text if the cell content is a plain text string
308 23
                    $cellContent .= $domText;
309
                }
310
                //    but if we have a rich text run instead, we need to append it correctly
311
                    //    TODO
312 23
            } elseif ($child instanceof DOMElement) {
313 23
                $attributeArray = [];
314 23
                foreach ($child->attributes as $attribute) {
315 20
                    $attributeArray[$attribute->name] = $attribute->value;
316
                }
317
318 23
                switch ($child->nodeName) {
319 23
                    case 'meta':
320 10
                        foreach ($attributeArray as $attributeName => $attributeValue) {
321
                            // Extract character set, so we can convert to UTF-8 if required
322 10
                            if ($attributeName === 'charset') {
323 2
                                $this->setInputEncoding($attributeValue);
324
                            }
325
                        }
326 10
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
327
328 10
                        break;
329 23
                    case 'title':
330 10
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
331 10
                        $sheet->setTitle($cellContent, true, false);
332 10
                        $cellContent = '';
333
334 10
                        break;
335 23
                    case 'span':
336 23
                    case 'div':
337 23
                    case 'font':
338 23
                    case 'i':
339 23
                    case 'em':
340 23
                    case 'strong':
341 23
                    case 'b':
342 6
                        if (isset($attributeArray['class']) && $attributeArray['class'] === 'comment') {
343 6
                            $sheet->getComment($column . $row)
344 6
                                ->getText()
345 6
                                ->createTextRun($child->textContent);
346
347 6
                            break;
348
                        }
349
350
                        if ($cellContent > '') {
351
                            $cellContent .= ' ';
352
                        }
353
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
354
                        if ($cellContent > '') {
355
                            $cellContent .= ' ';
356
                        }
357
358
                        if (isset($this->formats[$child->nodeName])) {
359
                            $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
360
                        }
361
362
                        break;
363 23
                    case 'hr':
364
                        $this->flushCell($sheet, $column, $row, $cellContent);
365
                        ++$row;
366
                        if (isset($this->formats[$child->nodeName])) {
367
                            $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
368
                        } else {
369
                            $cellContent = '----------';
370
                            $this->flushCell($sheet, $column, $row, $cellContent);
371
                        }
372
                        ++$row;
373
                        // Add a break after a horizontal rule, simply by allowing the code to dropthru
374
                        // no break
375 23
                    case 'br':
376 3
                        if ($this->tableLevel > 0) {
377
                            //    If we're inside a table, replace with a \n and set the cell to wrap
378 3
                            $cellContent .= "\n";
379 3
                            $sheet->getStyle($column . $row)->getAlignment()->setWrapText(true);
380
                        } else {
381
                            //    Otherwise flush our existing content and move the row cursor on
382
                            $this->flushCell($sheet, $column, $row, $cellContent);
383
                            ++$row;
384
                        }
385
386 3
                        break;
387 23
                    case 'a':
388 6
                        foreach ($attributeArray as $attributeName => $attributeValue) {
389
                            switch ($attributeName) {
390 6
                                case 'href':
391
                                    $sheet->getCell($column . $row)->getHyperlink()->setUrl($attributeValue);
392
                                    if (isset($this->formats[$child->nodeName])) {
393
                                        $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
394
                                    }
395
396
                                    break;
397 6
                                case 'class':
398 6
                                    if ($attributeValue === 'comment-indicator') {
399 6
                                        break; // Ignore - it's just a red square.
400
                                    }
401
                            }
402
                        }
403 6
                        $cellContent .= ' ';
404 6
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
405
406 6
                        break;
407 23
                    case 'h1':
408 23
                    case 'h2':
409 23
                    case 'h3':
410 23
                    case 'h4':
411 23
                    case 'h5':
412 23
                    case 'h6':
413 23
                    case 'ol':
414 23
                    case 'ul':
415 23
                    case 'p':
416
                        if ($this->tableLevel > 0) {
417
                            //    If we're inside a table, replace with a \n
418
                            $cellContent .= "\n";
419
                            $this->processDomElement($child, $sheet, $row, $column, $cellContent);
420
                        } else {
421
                            if ($cellContent > '') {
422
                                $this->flushCell($sheet, $column, $row, $cellContent);
423
                                ++$row;
424
                            }
425
                            $this->processDomElement($child, $sheet, $row, $column, $cellContent);
426
                            $this->flushCell($sheet, $column, $row, $cellContent);
427
428
                            if (isset($this->formats[$child->nodeName])) {
429
                                $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
430
                            }
431
432
                            ++$row;
433
                            $column = 'A';
434
                        }
435
436
                        break;
437 23
                    case 'li':
438
                        if ($this->tableLevel > 0) {
439
                            //    If we're inside a table, replace with a \n
440
                            $cellContent .= "\n";
441
                            $this->processDomElement($child, $sheet, $row, $column, $cellContent);
442
                        } else {
443
                            if ($cellContent > '') {
444
                                $this->flushCell($sheet, $column, $row, $cellContent);
445
                            }
446
                            ++$row;
447
                            $this->processDomElement($child, $sheet, $row, $column, $cellContent);
448
                            $this->flushCell($sheet, $column, $row, $cellContent);
449
                            $column = 'A';
450
                        }
451
452
                        break;
453 23
                    case 'img':
454 1
                        $this->insertImage($sheet, $column, $row, $attributeArray);
455
456 1
                        break;
457 23
                    case 'table':
458 23
                        $this->flushCell($sheet, $column, $row, $cellContent);
459 23
                        $column = $this->setTableStartColumn($column);
460 23
                        if ($this->tableLevel > 1) {
461
                            --$row;
462
                        }
463 23
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
464 23
                        $column = $this->releaseTableStartColumn();
465 23
                        if ($this->tableLevel > 1) {
466
                            ++$column;
467
                        } else {
468 23
                            ++$row;
469
                        }
470
471 23
                        break;
472 23
                    case 'thead':
473 23
                    case 'tbody':
474 9
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
475
476 9
                        break;
477 23
                    case 'tr':
478 23
                        $column = $this->getTableStartColumn();
479 23
                        $cellContent = '';
480 23
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
481
482 23
                        if (isset($attributeArray['height'])) {
483
                            $sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
484
                        }
485
486 23
                        ++$row;
487
488 23
                        break;
489 23
                    case 'th':
490 23
                    case 'td':
491 23
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
492
493 23
                        while (isset($this->rowspan[$column . $row])) {
494 1
                            ++$column;
495
                        }
496
497
                        // apply inline style
498 23
                        $this->applyInlineStyle($sheet, $row, $column, $attributeArray);
499
500 23
                        $this->flushCell($sheet, $column, $row, $cellContent);
501
502 23
                        if (isset($attributeArray['rowspan'], $attributeArray['colspan'])) {
503
                            //create merging rowspan and colspan
504
                            $columnTo = $column;
505
                            for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
506
                                ++$columnTo;
507
                            }
508
                            $range = $column . $row . ':' . $columnTo . ($row + (int) $attributeArray['rowspan'] - 1);
509
                            foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
510
                                $this->rowspan[$value] = true;
511
                            }
512
                            $sheet->mergeCells($range);
513
                            $column = $columnTo;
514 23
                        } elseif (isset($attributeArray['rowspan'])) {
515
                            //create merging rowspan
516 1
                            $range = $column . $row . ':' . $column . ($row + (int) $attributeArray['rowspan'] - 1);
517 1
                            foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
518 1
                                $this->rowspan[$value] = true;
519
                            }
520 1
                            $sheet->mergeCells($range);
521 23
                        } elseif (isset($attributeArray['colspan'])) {
522
                            //create merging colspan
523 2
                            $columnTo = $column;
524 2
                            for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
525 2
                                ++$columnTo;
526
                            }
527 2
                            $sheet->mergeCells($column . $row . ':' . $columnTo . $row);
528 2
                            $column = $columnTo;
529 23
                        } elseif (isset($attributeArray['bgcolor'])) {
530
                            $sheet->getStyle($column . $row)->applyFromArray(
531
                                [
532
                                    'fill' => [
533
                                        'fillType' => Fill::FILL_SOLID,
534
                                        'color' => ['rgb' => $attributeArray['bgcolor']],
535
                                    ],
536
                                ]
537
                            );
538
                        }
539
540 23
                        if (isset($attributeArray['width'])) {
541 1
                            $sheet->getColumnDimension($column)->setWidth($attributeArray['width']);
542
                        }
543
544 23
                        if (isset($attributeArray['height'])) {
545 1
                            $sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
546
                        }
547
548 23
                        if (isset($attributeArray['align'])) {
549 1
                            $sheet->getStyle($column . $row)->getAlignment()->setHorizontal($attributeArray['align']);
550
                        }
551
552 23
                        if (isset($attributeArray['valign'])) {
553 1
                            $sheet->getStyle($column . $row)->getAlignment()->setVertical($attributeArray['valign']);
554
                        }
555
556 23
                        if (isset($attributeArray['data-format'])) {
557 1
                            $sheet->getStyle($column . $row)->getNumberFormat()->setFormatCode($attributeArray['data-format']);
558
                        }
559
560 23
                        ++$column;
561
562 23
                        break;
563 23
                    case 'body':
564 23
                        $row = 1;
565 23
                        $column = 'A';
566 23
                        $cellContent = '';
567 23
                        $this->tableLevel = 0;
568 23
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
569
570 23
                        break;
571
                    default:
572 23
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
573
                }
574
            }
575
        }
576 23
    }
577
578
    /**
579
     * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
580
     *
581
     * @param string $pFilename
582
     * @param Spreadsheet $spreadsheet
583
     *
584
     * @throws Exception
585
     *
586
     * @return Spreadsheet
587
     */
588 21
    public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
589
    {
590
        // Validate
591 21
        if (!$this->canRead($pFilename)) {
592
            throw new Exception($pFilename . ' is an Invalid HTML file.');
593
        }
594
595
        // Create a new DOM object
596 21
        $dom = new DOMDocument();
597
        // Reload the HTML file into the DOM object
598 21
        $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scanFile($pFilename), 'HTML-ENTITIES', 'UTF-8'));
599 21
        if ($loaded === false) {
600
            throw new Exception('Failed to load ' . $pFilename . ' as a DOM Document');
601
        }
602
603 21
        return $this->loadDocument($dom, $spreadsheet);
604
    }
605
606
    /**
607
     * Spreadsheet from content.
608
     *
609
     * @param string $content
610
     * @param null|Spreadsheet $spreadsheet
611
     *
612
     * @return Spreadsheet
613
     */
614 2
    public function loadFromString($content, ?Spreadsheet $spreadsheet = null): Spreadsheet
615
    {
616
        //    Create a new DOM object
617 2
        $dom = new DOMDocument();
618
        //    Reload the HTML file into the DOM object
619 2
        $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scan($content), 'HTML-ENTITIES', 'UTF-8'));
620 2
        if ($loaded === false) {
621
            throw new Exception('Failed to load content as a DOM Document');
622
        }
623
624 2
        return $this->loadDocument($dom, $spreadsheet ?? new Spreadsheet());
625
    }
626
627
    /**
628
     * Loads PhpSpreadsheet from DOMDocument into PhpSpreadsheet instance.
629
     *
630
     * @param DOMDocument $document
631
     * @param Spreadsheet $spreadsheet
632
     *
633
     * @throws \PhpOffice\PhpSpreadsheet\Exception
634
     *
635
     * @return Spreadsheet
636
     */
637 23
    private function loadDocument(DOMDocument $document, Spreadsheet $spreadsheet): Spreadsheet
638
    {
639 23
        while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
640 1
            $spreadsheet->createSheet();
641
        }
642 23
        $spreadsheet->setActiveSheetIndex($this->sheetIndex);
643
644
        // Discard white space
645 23
        $document->preserveWhiteSpace = false;
646
647 23
        $row = 0;
648 23
        $column = 'A';
649 23
        $content = '';
650 23
        $this->rowspan = [];
651 23
        $this->processDomElement($document, $spreadsheet->getActiveSheet(), $row, $column, $content);
652
653
        // Return
654 23
        return $spreadsheet;
655
    }
656
657
    /**
658
     * Get sheet index.
659
     *
660
     * @return int
661
     */
662
    public function getSheetIndex()
663
    {
664
        return $this->sheetIndex;
665
    }
666
667
    /**
668
     * Set sheet index.
669
     *
670
     * @param int $pValue Sheet index
671
     *
672
     * @return HTML
673
     */
674 1
    public function setSheetIndex($pValue)
675
    {
676 1
        $this->sheetIndex = $pValue;
677
678 1
        return $this;
679
    }
680
681
    /**
682
     * Apply inline css inline style.
683
     *
684
     * NOTES :
685
     * Currently only intended for td & th element,
686
     * and only takes 'background-color' and 'color'; property with HEX color
687
     *
688
     * TODO :
689
     * - Implement to other propertie, such as border
690
     *
691
     * @param Worksheet $sheet
692
     * @param int $row
693
     * @param string $column
694
     * @param array $attributeArray
695
     */
696 23
    private function applyInlineStyle(&$sheet, $row, $column, $attributeArray)
697
    {
698 23
        if (!isset($attributeArray['style'])) {
699 20
            return;
700
        }
701
702 9
        $cellStyle = $sheet->getStyle($column . $row);
703
704
        // add color styles (background & text) from dom element,currently support : td & th, using ONLY inline css style with RGB color
705 9
        $styles = explode(';', $attributeArray['style']);
706 9
        foreach ($styles as $st) {
707 9
            $value = explode(':', $st);
708 9
            $styleName = isset($value[0]) ? trim($value[0]) : null;
709 9
            $styleValue = isset($value[1]) ? trim($value[1]) : null;
710
711 9
            if (!$styleName) {
712 6
                continue;
713
            }
714
715
            switch ($styleName) {
716 9
                case 'background':
717 9
                case 'background-color':
718 3
                    $styleColor = $this->getStyleColor($styleValue);
719
720 3
                    if (!$styleColor) {
721
                        continue 2;
722
                    }
723
724 3
                    $cellStyle->applyFromArray(['fill' => ['fillType' => Fill::FILL_SOLID, 'color' => ['rgb' => $styleColor]]]);
725
726 3
                    break;
727 9
                case 'color':
728 3
                    $styleColor = $this->getStyleColor($styleValue);
729
730 3
                    if (!$styleColor) {
731
                        continue 2;
732
                    }
733
734 3
                    $cellStyle->applyFromArray(['font' => ['color' => ['rgb' => $styleColor]]]);
735
736 3
                    break;
737
738 6
                case 'border':
739 1
                    $this->setBorderStyle($cellStyle, $styleValue, 'allBorders');
740
741 1
                    break;
742
743 6
                case 'border-top':
744 1
                    $this->setBorderStyle($cellStyle, $styleValue, 'top');
745
746 1
                    break;
747
748 6
                case 'border-bottom':
749 1
                    $this->setBorderStyle($cellStyle, $styleValue, 'bottom');
750
751 1
                    break;
752
753 6
                case 'border-left':
754 1
                    $this->setBorderStyle($cellStyle, $styleValue, 'left');
755
756 1
                    break;
757
758 6
                case 'border-right':
759 1
                    $this->setBorderStyle($cellStyle, $styleValue, 'right');
760
761 1
                    break;
762
763 5
                case 'font-size':
764 1
                    $cellStyle->getFont()->setSize(
765 1
                        (float) $styleValue
766
                    );
767
768 1
                    break;
769
770 5
                case 'font-weight':
771 1
                    if ($styleValue === 'bold' || $styleValue >= 500) {
772 1
                        $cellStyle->getFont()->setBold(true);
773
                    }
774
775 1
                    break;
776
777 5
                case 'font-style':
778 1
                    if ($styleValue === 'italic') {
779 1
                        $cellStyle->getFont()->setItalic(true);
780
                    }
781
782 1
                    break;
783
784 5
                case 'font-family':
785 1
                    $cellStyle->getFont()->setName(str_replace('\'', '', $styleValue));
786
787 1
                    break;
788
789 5
                case 'text-decoration':
790
                    switch ($styleValue) {
791 1
                        case 'underline':
792 1
                            $cellStyle->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
793
794 1
                            break;
795 1
                        case 'line-through':
796 1
                            $cellStyle->getFont()->setStrikethrough(true);
797
798 1
                            break;
799
                    }
800
801 1
                    break;
802
803 4
                case 'text-align':
804 1
                    $cellStyle->getAlignment()->setHorizontal($styleValue);
805
806 1
                    break;
807
808 4
                case 'vertical-align':
809 2
                    $cellStyle->getAlignment()->setVertical($styleValue);
810
811 2
                    break;
812
813 4
                case 'width':
814 1
                    $sheet->getColumnDimension($column)->setWidth(
815 1
                        str_replace('px', '', $styleValue)
816
                    );
817
818 1
                    break;
819
820 3
                case 'height':
821 1
                    $sheet->getRowDimension($row)->setRowHeight(
822 1
                        str_replace('px', '', $styleValue)
823
                    );
824
825 1
                    break;
826
827 2
                case 'word-wrap':
828 1
                    $cellStyle->getAlignment()->setWrapText(
829 1
                        $styleValue === 'break-word'
830
                    );
831
832 1
                    break;
833
834 2
                case 'text-indent':
835 2
                    $cellStyle->getAlignment()->setIndent(
836 2
                        (int) str_replace(['px'], '', $styleValue)
837
                    );
838
839 2
                    break;
840
            }
841
        }
842 9
    }
843
844
    /**
845
     * Check if has #, so we can get clean hex.
846
     *
847
     * @param $value
848
     *
849
     * @return null|string
850
     */
851 4
    public function getStyleColor($value)
852
    {
853 4
        if (strpos($value, '#') === 0) {
854 4
            return substr($value, 1);
855
        }
856
857
        return null;
858
    }
859
860
    /**
861
     * @param Worksheet $sheet
862
     * @param string    $column
863
     * @param int       $row
864
     * @param array     $attributes
865
     *
866
     * @throws \PhpOffice\PhpSpreadsheet\Exception
867
     */
868 1
    private function insertImage(Worksheet $sheet, $column, $row, array $attributes)
869
    {
870 1
        if (!isset($attributes['src'])) {
871
            return;
872
        }
873
874 1
        $src = urldecode($attributes['src']);
875 1
        $width = isset($attributes['width']) ? (float) $attributes['width'] : null;
876 1
        $height = isset($attributes['height']) ? (float) $attributes['height'] : null;
877 1
        $name = isset($attributes['alt']) ? (float) $attributes['alt'] : null;
878
879 1
        $drawing = new Drawing();
880 1
        $drawing->setPath($src);
881 1
        $drawing->setWorksheet($sheet);
882 1
        $drawing->setCoordinates($column . $row);
883 1
        $drawing->setOffsetX(0);
884 1
        $drawing->setOffsetY(10);
885 1
        $drawing->setResizeProportional(true);
886
887 1
        if ($name) {
888
            $drawing->setName($name);
889
        }
890
891 1
        if ($width) {
892
            $drawing->setWidth((int) $width);
893
        }
894
895 1
        if ($height) {
896
            $drawing->setHeight((int) $height);
897
        }
898
899 1
        $sheet->getColumnDimension($column)->setWidth(
900 1
            $drawing->getWidth() / 6
901
        );
902
903 1
        $sheet->getRowDimension($row)->setRowHeight(
904 1
            $drawing->getHeight() * 0.9
905
        );
906 1
    }
907
908
    /**
909
     * Map html border style to PhpSpreadsheet border style.
910
     *
911
     * @param  string $style
912
     *
913
     * @return null|string
914
     */
915 1
    public function getBorderStyle($style)
916
    {
917
        switch ($style) {
918 1
            case 'solid':
919 1
                return Border::BORDER_THIN;
920
            case 'dashed':
921
                return Border::BORDER_DASHED;
922
            case 'dotted':
923
                return Border::BORDER_DOTTED;
924
            case 'medium':
925
                return Border::BORDER_MEDIUM;
926
            case 'thick':
927
                return Border::BORDER_THICK;
928
            case 'none':
929
                return Border::BORDER_NONE;
930
            case 'dash-dot':
931
                return Border::BORDER_DASHDOT;
932
            case 'dash-dot-dot':
933
                return Border::BORDER_DASHDOTDOT;
934
            case 'double':
935
                return Border::BORDER_DOUBLE;
936
            case 'hair':
937
                return Border::BORDER_HAIR;
938
            case 'medium-dash-dot':
939
                return Border::BORDER_MEDIUMDASHDOT;
940
            case 'medium-dash-dot-dot':
941
                return Border::BORDER_MEDIUMDASHDOTDOT;
942
            case 'medium-dashed':
943
                return Border::BORDER_MEDIUMDASHED;
944
            case 'slant-dash-dot':
945
                return Border::BORDER_SLANTDASHDOT;
946
        }
947
948
        return null;
949
    }
950
951
    /**
952
     * @param Style  $cellStyle
953
     * @param string $styleValue
954
     * @param string $type
955
     */
956 1
    private function setBorderStyle(Style $cellStyle, $styleValue, $type)
957
    {
958 1
        [, $borderStyle, $color] = explode(' ', $styleValue);
959
960 1
        $cellStyle->applyFromArray([
961
            'borders' => [
962
                $type => [
963 1
                    'borderStyle' => $this->getBorderStyle($borderStyle),
964 1
                    'color' => ['rgb' => $this->getStyleColor($color)],
965
                ],
966
            ],
967
        ]);
968 1
    }
969
}
970