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