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