From de666b953d5dff2e7c745c67e0727a269855b767 Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Tue, 13 Aug 2024 10:23:39 +0200 Subject: [PATCH] RTF Writer : Support for Table Border Style --- docs/changes/2.x/2.0.0.md | 1 + src/PhpWord/Style/Border.php | 28 +-- src/PhpWord/Writer/HTML/Element/Table.php | 4 +- .../Writer/RTF/Element/AbstractElement.php | 6 +- src/PhpWord/Writer/RTF/Element/Table.php | 114 ++++++++++++ src/PhpWord/Writer/RTF/Part/Header.php | 9 + .../Writer/Word2007/Style/MarginBorder.php | 2 +- .../Writer/RTF/Element/TableTest.php | 169 ++++++++++++++++++ .../PhpWordTests/Writer/RTF/Element2Test.php | 46 +---- 9 files changed, 313 insertions(+), 66 deletions(-) create mode 100644 tests/PhpWordTests/Writer/RTF/Element/TableTest.php diff --git a/docs/changes/2.x/2.0.0.md b/docs/changes/2.x/2.0.0.md index 6638178d91..219db0c939 100644 --- a/docs/changes/2.x/2.0.0.md +++ b/docs/changes/2.x/2.0.0.md @@ -9,6 +9,7 @@ - Word2007 Reader: Support for Paragraph Border Style by [@damienfa](https://github.com/damienfa) in [#2651](https://github.com/PHPOffice/PHPWord/pull/2651) - Word2007 Writer: Support for field REF by [@crystoline](https://github.com/crystoline) in [#2652](https://github.com/PHPOffice/PHPWord/pull/2652) - Word2007 Reader : Support for FormFields by [@vincentKool](https://github.com/vincentKool) in [#2653](https://github.com/PHPOffice/PHPWord/pull/2653) +- RTF Writer : Support for Table Border Style fixing [#345](https://github.com/PHPOffice/PHPWord/issues/345) by [@Progi1984](https://github.com/Progi1984) in [#2656](https://github.com/PHPOffice/PHPWord/pull/2656) ### Bug fixes diff --git a/src/PhpWord/Style/Border.php b/src/PhpWord/Style/Border.php index 28e340c040..e2a56c5d21 100644 --- a/src/PhpWord/Style/Border.php +++ b/src/PhpWord/Style/Border.php @@ -34,7 +34,7 @@ class Border extends AbstractStyle /** * Border Top Color. * - * @var string + * @var null|string */ protected $borderTopColor; @@ -55,7 +55,7 @@ class Border extends AbstractStyle /** * Border Left Color. * - * @var string + * @var null|string */ protected $borderLeftColor; @@ -76,7 +76,7 @@ class Border extends AbstractStyle /** * Border Right Color. * - * @var string + * @var null|string */ protected $borderRightColor; @@ -97,7 +97,7 @@ class Border extends AbstractStyle /** * Border Bottom Color. * - * @var string + * @var null|string */ protected $borderBottomColor; @@ -171,7 +171,7 @@ public function setBorderSize($value = null) /** * Get border color. * - * @return string[] + * @return array */ public function getBorderColor() { @@ -186,7 +186,7 @@ public function getBorderColor() /** * Set border color. * - * @param string $value + * @param null|string $value * * @return self */ @@ -259,7 +259,7 @@ public function setBorderTopSize($value = null) /** * Get border top color. * - * @return string + * @return null|string */ public function getBorderTopColor() { @@ -269,7 +269,7 @@ public function getBorderTopColor() /** * Set border top color. * - * @param string $value + * @param null|string $value * * @return self */ @@ -331,7 +331,7 @@ public function setBorderLeftSize($value = null) /** * Get border left color. * - * @return string + * @return null|string */ public function getBorderLeftColor() { @@ -341,7 +341,7 @@ public function getBorderLeftColor() /** * Set border left color. * - * @param string $value + * @param null|string $value * * @return self */ @@ -403,7 +403,7 @@ public function setBorderRightSize($value = null) /** * Get border right color. * - * @return string + * @return null|string */ public function getBorderRightColor() { @@ -413,7 +413,7 @@ public function getBorderRightColor() /** * Set border right color. * - * @param string $value + * @param null|string $value * * @return self */ @@ -475,7 +475,7 @@ public function setBorderBottomSize($value = null) /** * Get border bottom color. * - * @return string + * @return null|string */ public function getBorderBottomColor() { @@ -485,7 +485,7 @@ public function getBorderBottomColor() /** * Set border bottom color. * - * @param string $value + * @param null|string $value * * @return self */ diff --git a/src/PhpWord/Writer/HTML/Element/Table.php b/src/PhpWord/Writer/HTML/Element/Table.php index c7a23d2fe1..f4acc8cdf4 100644 --- a/src/PhpWord/Writer/HTML/Element/Table.php +++ b/src/PhpWord/Writer/HTML/Element/Table.php @@ -120,9 +120,7 @@ private function getTableStyle($tableStyle = null): string return ''; } if (is_string($tableStyle)) { - $style = ' class="' . $tableStyle; - - return $style . '"'; + return ' class="' . $tableStyle . '"'; } $styleWriter = new TableStyleWriter($tableStyle); diff --git a/src/PhpWord/Writer/RTF/Element/AbstractElement.php b/src/PhpWord/Writer/RTF/Element/AbstractElement.php index dedbc8bfa1..5c33868a8b 100644 --- a/src/PhpWord/Writer/RTF/Element/AbstractElement.php +++ b/src/PhpWord/Writer/RTF/Element/AbstractElement.php @@ -24,7 +24,7 @@ use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Font as FontStyle; use PhpOffice\PhpWord\Style\Paragraph as ParagraphStyle; -use PhpOffice\PhpWord\Writer\AbstractWriter; +use PhpOffice\PhpWord\Writer\RTF as WriterRTF; use PhpOffice\PhpWord\Writer\RTF\Style\Font as FontStyleWriter; use PhpOffice\PhpWord\Writer\RTF\Style\Paragraph as ParagraphStyleWriter; @@ -38,7 +38,7 @@ abstract class AbstractElement /** * Parent writer. * - * @var \PhpOffice\PhpWord\Writer\AbstractWriter + * @var WriterRTF */ protected $parentWriter; @@ -82,7 +82,7 @@ abstract public function write(); */ protected $escaper; - public function __construct(AbstractWriter $parentWriter, Element $element, bool $withoutP = false) + public function __construct(WriterRTF $parentWriter, Element $element, bool $withoutP = false) { $this->parentWriter = $parentWriter; $this->element = $element; diff --git a/src/PhpWord/Writer/RTF/Element/Table.php b/src/PhpWord/Writer/RTF/Element/Table.php index b7b370bd5a..f2d5f9fb5a 100644 --- a/src/PhpWord/Writer/RTF/Element/Table.php +++ b/src/PhpWord/Writer/RTF/Element/Table.php @@ -21,7 +21,10 @@ use PhpOffice\PhpWord\Element\Row as RowElement; use PhpOffice\PhpWord\Element\Table as TableElement; use PhpOffice\PhpWord\Settings; +use PhpOffice\PhpWord\SimpleType\Border; use PhpOffice\PhpWord\Style; +use PhpOffice\PhpWord\Style\Cell as CellStyle; +use PhpOffice\PhpWord\Style\Table as TableStyle; /** * Table element RTF writer. @@ -30,6 +33,11 @@ */ class Table extends AbstractElement { + /** + * @var TableElement + */ + protected $element; + /** * Write element. * @@ -77,9 +85,18 @@ public function write() private function writeRowDef(RowElement $row) { $content = ''; + $tableStyle = $this->element->getStyle(); + if (is_string($tableStyle)) { + $tableStyle = Style::getStyle($tableStyle); + if (!($tableStyle instanceof TableStyle)) { + $tableStyle = null; + } + } $rightMargin = 0; foreach ($row->getCells() as $cell) { + $content .= $this->writeCellStyle($cell->getStyle(), $tableStyle); + $width = $cell->getWidth(); $vMerge = $this->getVMerge($cell->getStyle()->getVMerge()); if ($width === null) { @@ -127,6 +144,103 @@ private function writeCell(CellElement $cell) return $content; } + private function writeCellStyle(CellStyle $cell, ?TableStyle $table): string + { + $content = $this->writeCellBorder( + 't', + $cell->getBorderTopStyle() ?: ($table ? $table->getBorderTopStyle() : null), + (int) round($cell->getBorderTopSize() ?: ($table ? ($table->getBorderTopSize() ?: 0) : 0)), + $cell->getBorderTopColor() ?? ($table ? $table->getBorderTopColor() : null) + ); + $content .= $this->writeCellBorder( + 'l', + $cell->getBorderLeftStyle() ?: ($table ? $table->getBorderLeftStyle() : null), + (int) round($cell->getBorderLeftSize() ?: ($table ? ($table->getBorderLeftSize() ?: 0) : 0)), + $cell->getBorderLeftColor() ?? ($table ? $table->getBorderLeftColor() : null) + ); + $content .= $this->writeCellBorder( + 'b', + $cell->getBorderBottomStyle() ?: ($table ? $table->getBorderBottomStyle() : null), + (int) round($cell->getBorderBottomSize() ?: ($table ? ($table->getBorderBottomSize() ?: 0) : 0)), + $cell->getBorderBottomColor() ?? ($table ? $table->getBorderBottomColor() : null) + ); + $content .= $this->writeCellBorder( + 'r', + $cell->getBorderRightStyle() ?: ($table ? $table->getBorderRightStyle() : null), + (int) round($cell->getBorderRightSize() ?: ($table ? ($table->getBorderRightSize() ?: 0) : 0)), + $cell->getBorderRightColor() ?? ($table ? $table->getBorderRightColor() : null) + ); + + return $content; + } + + private function writeCellBorder(string $prefix, ?string $borderStyle, int $borderSize, ?string $borderColor): string + { + if ($borderSize == 0) { + return ''; + } + + $content = '\clbrdr' . $prefix; + /** + * \brdrs Single-thickness border. + * \brdrth Double-thickness border. + * \brdrsh Shadowed border. + * \brdrdb Double border. + * \brdrdot Dotted border. + * \brdrdash Dashed border. + * \brdrhair Hairline border. + * \brdrinset Inset border. + * \brdrdashsm Dash border (small). + * \brdrdashd Dot dash border. + * \brdrdashdd Dot dot dash border. + * \brdroutset Outset border. + * \brdrtriple Triple border. + * \brdrtnthsg Thick thin border (small). + * \brdrthtnsg Thin thick border (small). + * \brdrtnthtnsg Thin thick thin border (small). + * \brdrtnthmg Thick thin border (medium). + * \brdrthtnmg Thin thick border (medium). + * \brdrtnthtnmg Thin thick thin border (medium). + * \brdrtnthlg Thick thin border (large). + * \brdrthtnlg Thin thick border (large). + * \brdrtnthtnlg Thin thick thin border (large). + * \brdrwavy Wavy border. + * \brdrwavydb Double wavy border. + * \brdrdashdotstr Striped border. + * \brdremboss Emboss border. + * \brdrengrave Engrave border. + */ + switch ($borderStyle) { + case Border::DOTTED: + $content .= '\brdrdot'; + + break; + case Border::SINGLE: + default: + $content .= '\brdrs'; + + break; + } + + // \brdrwN N is the width in twips (1/20 pt) of the pen used to draw the paragraph border line. + // N cannot be greater than 75. + // To obtain a larger border width, the \brdth control word can be used to obtain a width double that of N. + // $borderSize is in eights of a point, i.e. 4 / 8 = .5pt + // 1/20 pt => 1/8 / 2.5 + $content .= '\brdrw' . (int) ($borderSize / 2.5); + + // \brdrcfN N is the color of the paragraph border, specified as an index into the color table in the RTF header. + $colorIndex = 0; + $index = array_search($borderColor, $this->parentWriter->getColorTable()); + if ($index !== false) { + $colorIndex = (int) $index + 1; + } + $content .= '\brdrcf' . $colorIndex; + $content .= PHP_EOL; + + return $content; + } + /** * Get vertical merge style. * diff --git a/src/PhpWord/Writer/RTF/Part/Header.php b/src/PhpWord/Writer/RTF/Part/Header.php index e4ad4bee0b..7f8cc84b97 100644 --- a/src/PhpWord/Writer/RTF/Part/Header.php +++ b/src/PhpWord/Writer/RTF/Part/Header.php @@ -21,6 +21,7 @@ use PhpOffice\PhpWord\Shared\Converter; use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Font; +use PhpOffice\PhpWord\Style\Table; /** * RTF header part writer. @@ -236,6 +237,14 @@ private function registerFontItems($style): void $this->registerTableItem($this->fontTable, $style->getName(), $defaultFont); $this->registerTableItem($this->colorTable, $style->getColor(), $defaultColor); $this->registerTableItem($this->colorTable, $style->getFgColor(), $defaultColor); + + return; + } + if ($style instanceof Table) { + $this->registerTableItem($this->colorTable, $style->getBorderTopColor(), $defaultColor); + $this->registerTableItem($this->colorTable, $style->getBorderRightColor(), $defaultColor); + $this->registerTableItem($this->colorTable, $style->getBorderLeftColor(), $defaultColor); + $this->registerTableItem($this->colorTable, $style->getBorderBottomColor(), $defaultColor); } } diff --git a/src/PhpWord/Writer/Word2007/Style/MarginBorder.php b/src/PhpWord/Writer/Word2007/Style/MarginBorder.php index 8d08eec3cc..ce250e54d4 100644 --- a/src/PhpWord/Writer/Word2007/Style/MarginBorder.php +++ b/src/PhpWord/Writer/Word2007/Style/MarginBorder.php @@ -120,7 +120,7 @@ public function setSizes($value): void /** * Set colors. * - * @param string[] $value + * @param array $value */ public function setColors($value): void { diff --git a/tests/PhpWordTests/Writer/RTF/Element/TableTest.php b/tests/PhpWordTests/Writer/RTF/Element/TableTest.php new file mode 100644 index 0000000000..35cdfbec28 --- /dev/null +++ b/tests/PhpWordTests/Writer/RTF/Element/TableTest.php @@ -0,0 +1,169 @@ +write()); + } + + public function testTable(): void + { + Settings::setDefaultRtl(false); + $parentWriter = new RTF(); + $element = new \PhpOffice\PhpWord\Element\Table(); + $width = 100; + $width2 = 2 * $width; + $element->addRow(); + $tce = $element->addCell($width); + $tce->addText('1'); + $tce = $element->addCell($width); + $tce->addText('2'); + $element->addRow(); + $tce = $element->addCell($width); + $tce->addText('3'); + $tce = $element->addCell($width); + $tce->addText('4'); + $table = new WriterTable($parentWriter, $element); + $expect = implode("\n", [ + '\\pard', + "\\trowd \\cellx$width \\cellx$width2 ", + '\\intbl', + '\\ql{\\cf0\\f0 1}\\par', + '\\cell', + '\\intbl', + '{\\cf0\\f0 2}\\par', + '\\cell', + '\\row', + "\\trowd \\cellx$width \\cellx$width2 ", + '\\intbl', + '\\ql{\\cf0\\f0 3}\\par', + '\\cell', + '\\intbl', + '{\\cf0\\f0 4}\par', + '\\cell', + '\\row', + '\\pard', + '', + ]); + + self::assertEquals($expect, $this->removeCr($table)); + } + + public function testTableStyle(): void + { + $width = 100; + + Settings::setDefaultRtl(false); + $parentWriter = new RTF(); + + Style::addTableStyle('TableStyle', ['borderSize' => 6, 'borderColor' => '006699']); + + $element = new Table('TableStyle'); + $element->addRow(); + $elementCell = $element->addCell($width); + $elementCell->addText('1'); + + $expect = implode("\n", [ + '\\pard', + '\\trowd \\clbrdrt\\brdrs\\brdrw2\\brdrcf0', + '\\clbrdrl\\brdrs\\brdrw2\\brdrcf0', + '\\clbrdrb\\brdrs\\brdrw2\\brdrcf0', + '\\clbrdrr\\brdrs\\brdrw2\\brdrcf0', + "\\cellx$width ", + '\\intbl', + '\\ql{\\cf0\\f0 1}\\par', + '\\cell', + '\\row', + '\\pard', + '', + ]); + + self::assertEquals($expect, $this->removeCr(new WriterTable($parentWriter, $element))); + } + + public function testTableStyleNotExisting(): void + { + $width = 100; + + Settings::setDefaultRtl(false); + $parentWriter = new RTF(); + + $element = new Table('TableStyleNotExisting'); + $element->addRow(); + $elementCell = $element->addCell($width); + $elementCell->addText('1'); + + $expect = implode("\n", [ + '\\pard', + "\\trowd \\cellx$width ", + '\\intbl', + '\\ql{\\cf0\\f0 1}\\par', + '\\cell', + '\\row', + '\\pard', + '', + ]); + + self::assertEquals($expect, $this->removeCr(new WriterTable($parentWriter, $element))); + } + + public function testTableCellStyle(): void + { + $width = 100; + + Settings::setDefaultRtl(false); + $parentWriter = new RTF(); + + $element = new Table(); + $element->addRow(); + $elementCell = $element->addCell($width, ['borderSize' => 6, 'borderColor' => '006699', 'borderStyle' => Border::DOTTED]); + $elementCell->addText('1'); + + $expect = implode("\n", [ + '\\pard', + '\\trowd \\clbrdrt\\brdrdot\\brdrw2\\brdrcf0', + '\\clbrdrl\\brdrdot\\brdrw2\\brdrcf0', + '\\clbrdrb\\brdrdot\\brdrw2\\brdrcf0', + '\\clbrdrr\\brdrdot\\brdrw2\\brdrcf0', + "\\cellx$width ", + '\\intbl', + '\\ql{\\cf0\\f0 1}\\par', + '\\cell', + '\\row', + '\\pard', + '', + ]); + + self::assertEquals($expect, $this->removeCr(new WriterTable($parentWriter, $element))); + } +} diff --git a/tests/PhpWordTests/Writer/RTF/Element2Test.php b/tests/PhpWordTests/Writer/RTF/Element2Test.php index d82a3095d7..7d36fac869 100644 --- a/tests/PhpWordTests/Writer/RTF/Element2Test.php +++ b/tests/PhpWordTests/Writer/RTF/Element2Test.php @@ -19,7 +19,6 @@ use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Writer\RTF; -use PhpOffice\PhpWord\Writer\RTF\Element\Table as WriterTable; use PhpOffice\PhpWord\Writer\RTF\Element\TextRun as WriterTextRun; use PhpOffice\PhpWord\Writer\RTF\Element\Title as WriterTitle; @@ -33,55 +32,12 @@ protected function tearDown(): void Settings::setDefaultRtl(null); } - /** @param WriterTable|WriterTextRun|WriterTitle $field */ + /** @param WriterTextRun|WriterTitle $field */ public function removeCr($field): string { return str_replace("\r\n", "\n", $field->write()); } - public function testTable(): void - { - Settings::setDefaultRtl(false); - $parentWriter = new RTF(); - $element = new \PhpOffice\PhpWord\Element\Table(); - $width = 100; - $width2 = 2 * $width; - $element->addRow(); - $tce = $element->addCell($width); - $tce->addText('1'); - $tce = $element->addCell($width); - $tce->addText('2'); - $element->addRow(); - $tce = $element->addCell($width); - $tce->addText('3'); - $tce = $element->addCell($width); - $tce->addText('4'); - $table = new WriterTable($parentWriter, $element); - $expect = implode("\n", [ - '\\pard', - "\\trowd \\cellx$width \\cellx$width2 ", - '\\intbl', - '\\ql{\\cf0\\f0 1}\\par', - '\\cell', - '\\intbl', - '{\\cf0\\f0 2}\\par', - '\\cell', - '\\row', - "\\trowd \\cellx$width \\cellx$width2 ", - '\\intbl', - '\\ql{\\cf0\\f0 3}\\par', - '\\cell', - '\\intbl', - '{\\cf0\\f0 4}\par', - '\\cell', - '\\row', - '\\pard', - '', - ]); - - self::assertEquals($expect, $this->removeCr($table)); - } - public function testTextRun(): void { Settings::setDefaultRtl(false);