Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.91% covered (success)
98.91%
91 / 92
85.71% covered (warning)
85.71%
6 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Table
98.91% covered (success)
98.91%
91 / 92
85.71% covered (warning)
85.71%
6 / 7
50
0.00% covered (danger)
0.00%
0 / 1
 write
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
8
 writeRowDef
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
5
 writeRow
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 writeCell
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 writeCellStyle
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
25
 writeCellBorder
93.33% covered (success)
93.33%
14 / 15
0.00% covered (danger)
0.00%
0 / 1
6.01
 getVMerge
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
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\RTF\Element;
19
20use PhpOffice\PhpWord\Element\Cell as CellElement;
21use PhpOffice\PhpWord\Element\Row as RowElement;
22use PhpOffice\PhpWord\Element\Table as TableElement;
23use PhpOffice\PhpWord\Settings;
24use PhpOffice\PhpWord\SimpleType\Border;
25use PhpOffice\PhpWord\Style;
26use PhpOffice\PhpWord\Style\Cell as CellStyle;
27use PhpOffice\PhpWord\Style\Table as TableStyle;
28
29/**
30 * Table element RTF writer.
31 *
32 * @since 0.11.0
33 */
34class Table extends AbstractElement
35{
36    /**
37     * @var TableElement
38     */
39    protected $element;
40
41    /**
42     * Write element.
43     *
44     * @return string
45     */
46    public function write()
47    {
48        if (!$this->element instanceof TableElement) {
49            return '';
50        }
51        $element = $this->element;
52        // No nesting table for now
53        if ($element->getNestedLevel() >= 1) {
54            return '';
55        }
56
57        $content = '';
58        $style = $this->element->getStyle();
59        $bidiStyle = (is_object($style) && method_exists($style, 'isBidiVisual')) ? $style->isBidiVisual() : Settings::isDefaultRtl();
60        $bidi = $bidiStyle ? '\rtlrow' : '';
61        $rows = $element->getRows();
62        $rowCount = count($rows);
63
64        if ($rowCount > 0) {
65            $content .= '\pard' . PHP_EOL;
66
67            for ($i = 0; $i < $rowCount; ++$i) {
68                $content .= "\\trowd$bidi ";
69                $content .= $this->writeRowDef($rows[$i]);
70                $content .= PHP_EOL;
71                $content .= $this->writeRow($rows[$i]);
72                $content .= '\row' . PHP_EOL;
73            }
74            $content .= '\pard' . PHP_EOL;
75        }
76
77        return $content;
78    }
79
80    /**
81     * Write column.
82     *
83     * @return string
84     */
85    private function writeRowDef(RowElement $row)
86    {
87        $content = '';
88        $tableStyle = $this->element->getStyle();
89        if (is_string($tableStyle)) {
90            $tableStyle = Style::getStyle($tableStyle);
91            if (!($tableStyle instanceof TableStyle)) {
92                $tableStyle = null;
93            }
94        }
95
96        $rightMargin = 0;
97        foreach ($row->getCells() as $cell) {
98            $content .= $this->writeCellStyle($cell->getStyle(), $tableStyle);
99
100            $width = $cell->getWidth();
101            $vMerge = $this->getVMerge($cell->getStyle()->getVMerge());
102            if ($width === null) {
103                $width = 720; // Arbitrary default width
104            }
105            $rightMargin += $width;
106            $content .= "{$vMerge}\\cellx{$rightMargin} ";
107        }
108
109        return $content;
110    }
111
112    /**
113     * Write row.
114     *
115     * @return string
116     */
117    private function writeRow(RowElement $row)
118    {
119        $content = '';
120
121        // Write cells
122        foreach ($row->getCells() as $cell) {
123            $content .= $this->writeCell($cell);
124        }
125
126        return $content;
127    }
128
129    /**
130     * Write cell.
131     *
132     * @return string
133     */
134    private function writeCell(CellElement $cell)
135    {
136        $content = '\intbl' . PHP_EOL;
137
138        // Write content
139        $writer = new Container($this->parentWriter, $cell);
140        $content .= $writer->write();
141
142        $content .= '\cell' . PHP_EOL;
143
144        return $content;
145    }
146
147    private function writeCellStyle(CellStyle $cell, ?TableStyle $table): string
148    {
149        $content = $this->writeCellBorder(
150            't',
151            $cell->getBorderTopStyle() ?: ($table ? $table->getBorderTopStyle() : null),
152            (int) round($cell->getBorderTopSize() ?: ($table ? ($table->getBorderTopSize() ?: 0) : 0)),
153            $cell->getBorderTopColor() ?? ($table ? $table->getBorderTopColor() : null)
154        );
155        $content .= $this->writeCellBorder(
156            'l',
157            $cell->getBorderLeftStyle() ?: ($table ? $table->getBorderLeftStyle() : null),
158            (int) round($cell->getBorderLeftSize() ?: ($table ? ($table->getBorderLeftSize() ?: 0) : 0)),
159            $cell->getBorderLeftColor() ?? ($table ? $table->getBorderLeftColor() : null)
160        );
161        $content .= $this->writeCellBorder(
162            'b',
163            $cell->getBorderBottomStyle() ?: ($table ? $table->getBorderBottomStyle() : null),
164            (int) round($cell->getBorderBottomSize() ?: ($table ? ($table->getBorderBottomSize() ?: 0) : 0)),
165            $cell->getBorderBottomColor() ?? ($table ? $table->getBorderBottomColor() : null)
166        );
167        $content .= $this->writeCellBorder(
168            'r',
169            $cell->getBorderRightStyle() ?: ($table ? $table->getBorderRightStyle() : null),
170            (int) round($cell->getBorderRightSize() ?: ($table ? ($table->getBorderRightSize() ?: 0) : 0)),
171            $cell->getBorderRightColor() ?? ($table ? $table->getBorderRightColor() : null)
172        );
173
174        return $content;
175    }
176
177    private function writeCellBorder(string $prefix, ?string $borderStyle, int $borderSize, ?string $borderColor): string
178    {
179        if ($borderSize == 0) {
180            return '';
181        }
182
183        $content = '\clbrdr' . $prefix;
184        /**
185         * \brdrs     Single-thickness border.
186         * \brdrth     Double-thickness border.
187         * \brdrsh     Shadowed border.
188         * \brdrdb     Double border.
189         * \brdrdot     Dotted border.
190         * \brdrdash     Dashed border.
191         * \brdrhair     Hairline border.
192         * \brdrinset     Inset border.
193         * \brdrdashsm     Dash border (small).
194         * \brdrdashd     Dot dash border.
195         * \brdrdashdd     Dot dot dash border.
196         * \brdroutset     Outset border.
197         * \brdrtriple     Triple border.
198         * \brdrtnthsg     Thick thin border (small).
199         * \brdrthtnsg     Thin thick border (small).
200         * \brdrtnthtnsg     Thin thick thin border (small).
201         * \brdrtnthmg     Thick thin border (medium).
202         * \brdrthtnmg     Thin thick border (medium).
203         * \brdrtnthtnmg     Thin thick thin border (medium).
204         * \brdrtnthlg     Thick thin border (large).
205         * \brdrthtnlg     Thin thick border (large).
206         * \brdrtnthtnlg     Thin thick thin border (large).
207         * \brdrwavy     Wavy border.
208         * \brdrwavydb     Double wavy border.
209         * \brdrdashdotstr     Striped border.
210         * \brdremboss     Emboss border.
211         * \brdrengrave     Engrave border.
212         */
213        switch ($borderStyle) {
214            case Border::DOTTED:
215                $content .= '\brdrdot';
216
217                break;
218            case Border::SINGLE:
219            default:
220                $content .= '\brdrs';
221
222                break;
223        }
224
225        // \brdrwN     N is the width in twips (1/20 pt) of the pen used to draw the paragraph border line.
226        //          N cannot be greater than 75.
227        //          To obtain a larger border width, the \brdth control word can be used to obtain a width double that of N.
228        // $borderSize is in eights of a point, i.e. 4 / 8 = .5pt
229        // 1/20 pt => 1/8 / 2.5
230        $content .= '\brdrw' . (int) ($borderSize / 2.5);
231
232        // \brdrcfN     N is the color of the paragraph border, specified as an index into the color table in the RTF header.
233        $colorIndex = 0;
234        $index = array_search($borderColor, $this->parentWriter->getColorTable());
235        if ($index !== false) {
236            $colorIndex = (int) $index + 1;
237        }
238        $content .= '\brdrcf' . $colorIndex;
239        $content .= PHP_EOL;
240
241        return $content;
242    }
243
244    /**
245     * Get vertical merge style.
246     *
247     * @param string $value
248     *
249     * @return string
250     *
251     * @todo Move to style
252     */
253    private function getVMerge($value)
254    {
255        $style = '';
256        if ($value == 'restart') {
257            $style = '\clvmgf';
258        } elseif ($value == 'continue') {
259            $style = '\clvmrg';
260        }
261
262        return $style;
263    }
264}