Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
82 / 82
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
Table
100.00% covered (success)
100.00%
82 / 82
100.00% covered (success)
100.00%
3 / 3
33
100.00% covered (success)
100.00%
1 / 1
 write
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
1 / 1
20
 getTableStyle
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 calculateCellRowSpan
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
9
1<?php
2/**
3 * This file is part of PHPWord - A pure PHP library for reading and writing
4 * word processing documents.
5 *
6 * PHPWord is free software distributed under the terms of the GNU Lesser
7 * General Public License version 3 as published by the Free Software Foundation.
8 *
9 * For the full copyright and license information, please read the LICENSE
10 * file that was distributed with this source code. For the full list of
11 * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
12 *
13 * @see         https://github.com/PHPOffice/PHPWord
14 *
15 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
16 */
17
18namespace PhpOffice\PhpWord\Writer\HTML\Element;
19
20use PhpOffice\PhpWord\Writer\HTML\Style\Table as TableStyleWriter;
21
22/**
23 * Table element HTML writer.
24 *
25 * @since 0.10.0
26 */
27class Table extends AbstractElement
28{
29    /**
30     * Write table.
31     *
32     * @return string
33     */
34    public function write()
35    {
36        if (!$this->element instanceof \PhpOffice\PhpWord\Element\Table) {
37            return '';
38        }
39
40        $content = '';
41        $rows = $this->element->getRows();
42        $rowCount = count($rows);
43        if ($rowCount > 0) {
44            $content .= '<table' . $this->getTableStyle($this->element->getStyle()) . '>' . PHP_EOL;
45
46            for ($i = 0; $i < $rowCount; ++$i) {
47                /** @var \PhpOffice\PhpWord\Element\Row $row Type hint */
48                $rowStyle = $rows[$i]->getStyle();
49                // $height = $row->getHeight();
50                $tblHeader = $rowStyle->isTblHeader();
51                $content .= '<tr>' . PHP_EOL;
52                $rowCells = $rows[$i]->getCells();
53                $rowCellCount = count($rowCells);
54                for ($j = 0; $j < $rowCellCount; ++$j) {
55                    $cellStyle = $rowCells[$j]->getStyle();
56                    $cellStyleCss = $this->getTableStyle($cellStyle);
57                    $cellBgColor = $cellStyle->getBgColor();
58                    $cellFgColor = null;
59                    if ($cellBgColor && $cellBgColor !== 'auto') {
60                        $red = hexdec(substr($cellBgColor, 0, 2));
61                        $green = hexdec(substr($cellBgColor, 2, 2));
62                        $blue = hexdec(substr($cellBgColor, 4, 2));
63                        $cellFgColor = (($red * 0.299 + $green * 0.587 + $blue * 0.114) > 186) ? null : 'ffffff';
64                    }
65                    $cellColSpan = $cellStyle->getGridSpan();
66                    $cellRowSpan = 1;
67                    $cellVMerge = $cellStyle->getVMerge();
68                    // If this is the first cell of the vertical merge, find out how many rows it spans
69                    if ($cellVMerge === 'restart') {
70                        $cellRowSpan = $this->calculateCellRowSpan($rows, $i, $j);
71                    }
72                    // Ignore cells that are merged vertically with previous rows
73                    if ($cellVMerge !== 'continue') {
74                        $cellTag = $tblHeader ? 'th' : 'td';
75                        $cellColSpanAttr = (is_numeric($cellColSpan) && ($cellColSpan > 1) ? " colspan=\"{$cellColSpan}\"" : '');
76                        $cellRowSpanAttr = ($cellRowSpan > 1 ? " rowspan=\"{$cellRowSpan}\"" : '');
77                        $cellBgColorAttr = (empty($cellBgColor) ? '' : " bgcolor=\"#{$cellBgColor}\"");
78                        $cellFgColorAttr = (empty($cellFgColor) ? '' : " color=\"#{$cellFgColor}\"");
79                        $content .= "<{$cellTag}{$cellStyleCss}{$cellColSpanAttr}{$cellRowSpanAttr}{$cellBgColorAttr}{$cellFgColorAttr}>" . PHP_EOL;
80                        $writer = new Container($this->parentWriter, $rowCells[$j]);
81                        $content .= $writer->write();
82                        if ($cellRowSpan > 1) {
83                            // There shouldn't be any content in the subsequent merged cells, but lets check anyway
84                            for ($k = $i + 1; $k < $rowCount; ++$k) {
85                                $kRowCells = $rows[$k]->getCells();
86                                if (isset($kRowCells[$j]) && $kRowCells[$j]->getStyle()->getVMerge() === 'continue') {
87                                    $writer = new Container($this->parentWriter, $kRowCells[$j]);
88                                    $content .= $writer->write();
89                                } else {
90                                    break;
91                                }
92                            }
93                        }
94                        $content .= "</{$cellTag}>" . PHP_EOL;
95                    }
96                }
97                $content .= '</tr>' . PHP_EOL;
98            }
99            $content .= '</table>' . PHP_EOL;
100        }
101
102        return $content;
103    }
104
105    /**
106     * Translates Table style in CSS equivalent.
107     *
108     * @param null|\PhpOffice\PhpWord\Style\Cell|\PhpOffice\PhpWord\Style\Table|string $tableStyle
109     */
110    private function getTableStyle($tableStyle = null): string
111    {
112        if ($tableStyle == null) {
113            return '';
114        }
115        if (is_string($tableStyle)) {
116            return ' class="' . $tableStyle . '"';
117        }
118
119        $styleWriter = new TableStyleWriter($tableStyle);
120        $style = $styleWriter->write();
121        if ($style === '') {
122            return '';
123        }
124
125        return ' style="' . $style . '"';
126    }
127
128    /**
129     * Calculates cell rowspan.
130     *
131     * @param \PhpOffice\PhpWord\Element\Row[] $rows
132     */
133    private function calculateCellRowSpan(array $rows, int $rowIndex, int $colIndex): int
134    {
135        $currentRow = $rows[$rowIndex];
136        $currentRowCells = $currentRow->getCells();
137        $shiftedColIndex = 0;
138
139        foreach ($currentRowCells as $cell) {
140            if ($cell === $currentRowCells[$colIndex]) {
141                break;
142            }
143
144            $colSpan = 1;
145
146            if ($cell->getStyle()->getGridSpan() !== null) {
147                $colSpan = $cell->getStyle()->getGridSpan();
148            }
149
150            $shiftedColIndex += $colSpan;
151        }
152
153        $rowCount = count($rows);
154        $rowSpan = 1;
155
156        for ($i = $rowIndex + 1; $i < $rowCount; ++$i) {
157            $rowCells = $rows[$i]->getCells();
158            $colIndex = 0;
159
160            foreach ($rowCells as $cell) {
161                if ($colIndex === $shiftedColIndex) {
162                    if ($cell->getStyle()->getVMerge() === 'continue') {
163                        ++$rowSpan;
164                    }
165
166                    break;
167                }
168
169                $colSpan = 1;
170
171                if ($cell->getStyle()->getGridSpan() !== null) {
172                    $colSpan = $cell->getStyle()->getGridSpan();
173                }
174
175                $colIndex += $colSpan;
176            }
177        }
178
179        return $rowSpan;
180    }
181}