From ebf5cf784f1f67e09304984486a7ec2faac8cac3 Mon Sep 17 00:00:00 2001 From: owen Date: Tue, 19 Nov 2019 14:24:29 -0800 Subject: [PATCH 1/5] Convert named constant colors to RGB in Shared/Converter. Otherwise, colors will not be as expected for RTF and ODT. --- src/PhpWord/Shared/Converter.php | 45 ++++++++++++++++++++++++++ tests/PhpWord/Shared/ConverterTest.php | 2 ++ 2 files changed, 47 insertions(+) diff --git a/src/PhpWord/Shared/Converter.php b/src/PhpWord/Shared/Converter.php index 7008ac5d1d..7c3048fcee 100644 --- a/src/PhpWord/Shared/Converter.php +++ b/src/PhpWord/Shared/Converter.php @@ -272,6 +272,50 @@ public static function angleToDegree($angle = 1) return round($angle / self::DEGREE_TO_ANGLE); } + /** + * Convert colorname as string to RGB + * + * @param string $value color name + * @return string color as hex RGB string, or original value if unknown + */ + public static function stringToRgb($value) + { + switch ($value) { + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_YELLOW: + return 'FFFF00'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_LIGHTGREEN: + return '90EE90'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_CYAN: + return '00FFFF'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_MAGENTA: + return 'FF00FF'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_BLUE: + return '0000FF'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_RED: + return 'FF0000'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKBLUE: + return '00008B'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKCYAN: + return '008B8B'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKGREEN: + return '006400'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKMAGENTA: + return '8B008B'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKRED: + return '8B0000'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKYELLOW: + return '8B8B00'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKGRAY: + return 'A9A9A9'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_LIGHTGRAY: + return 'D3D3D3'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_BLACK: + return '000000'; + } + + return $value; + } + /** * Convert HTML hexadecimal to RGB * @@ -283,6 +327,7 @@ public static function htmlToRgb($value) if ($value[0] == '#') { $value = substr($value, 1); } + $value = self::stringToRgb($value); if (strlen($value) == 6) { list($red, $green, $blue) = array($value[0] . $value[1], $value[2] . $value[3], $value[4] . $value[5]); diff --git a/tests/PhpWord/Shared/ConverterTest.php b/tests/PhpWord/Shared/ConverterTest.php index 15be8ec12a..122385a9b4 100644 --- a/tests/PhpWord/Shared/ConverterTest.php +++ b/tests/PhpWord/Shared/ConverterTest.php @@ -114,6 +114,8 @@ public function testHtmlToRGB() $values[] = array('FF99DD', array(255, 153, 221)); // 6 characters $values[] = array('F9D', array(255, 153, 221)); // 3 characters $values[] = array('0F9D', false); // 4 characters + $values[] = array(\PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKMAGENTA, array(139, 0, 139)); + $values[] = array('unknow', array(0, 0, 0)); // 6 characters, invalid // Conduct test foreach ($values as $value) { $result = Converter::htmlToRgb($value[0]); From ecfafd757667c2857586d5841dffad6d67b65b70 Mon Sep 17 00:00:00 2001 From: owen Date: Tue, 3 Dec 2019 07:46:16 -0800 Subject: [PATCH 2/5] RTF Changes 1. Converter is currently expecting colors as strings of hex digits, but PhpWord allows specification of colors by named constant, so result is random when one of those is used. This change handles all the named colors. 2. Table needs \pard at end; formatting may be wrong without it. 3. RTF writer will no longer ignore paragraph style for TextRun. 4. RTF writer will no longer ignore paragraph and font style for Title. 5. Add support for RTF headers and footers. 6. Add support for right-to-left in font. 7. Add support for PageBreakBefore and LineHeight for paragraphs. 8. Add support for PageNumberingStart for sections. There are test cases for all of these changes. --- src/PhpWord/Shared/Converter.php | 3 +- .../Writer/RTF/Element/AbstractElement.php | 4 +- src/PhpWord/Writer/RTF/Element/Table.php | 1 + src/PhpWord/Writer/RTF/Element/TextRun.php | 1 + src/PhpWord/Writer/RTF/Element/Title.php | 67 ++++++++++++++++ src/PhpWord/Writer/RTF/Part/Document.php | 69 ++++++++++++++++ src/PhpWord/Writer/RTF/Style/Font.php | 1 + src/PhpWord/Writer/RTF/Style/Paragraph.php | 10 +++ src/PhpWord/Writer/RTF/Style/Section.php | 1 + tests/PhpWord/Shared/ConverterTest.php | 20 ++--- tests/PhpWord/Writer/RTF/ElementTest.php | 76 ++++++++++++++++++ tests/PhpWord/Writer/RTF/HeaderFooterTest.php | 78 +++++++++++++++++++ tests/PhpWord/Writer/RTF/StyleTest.php | 45 +++++++++++ 13 files changed, 360 insertions(+), 16 deletions(-) create mode 100644 tests/PhpWord/Writer/RTF/HeaderFooterTest.php diff --git a/src/PhpWord/Shared/Converter.php b/src/PhpWord/Shared/Converter.php index 7c3048fcee..9206a3bcd3 100644 --- a/src/PhpWord/Shared/Converter.php +++ b/src/PhpWord/Shared/Converter.php @@ -326,8 +326,9 @@ public static function htmlToRgb($value) { if ($value[0] == '#') { $value = substr($value, 1); + } else { + $value = self::stringToRgb($value); } - $value = self::stringToRgb($value); if (strlen($value) == 6) { list($red, $green, $blue) = array($value[0] . $value[1], $value[2] . $value[3], $value[4] . $value[5]); diff --git a/src/PhpWord/Writer/RTF/Element/AbstractElement.php b/src/PhpWord/Writer/RTF/Element/AbstractElement.php index cf1aa391c5..132890e6ec 100644 --- a/src/PhpWord/Writer/RTF/Element/AbstractElement.php +++ b/src/PhpWord/Writer/RTF/Element/AbstractElement.php @@ -41,14 +41,14 @@ abstract class AbstractElement extends HTMLAbstractElement * * @var \PhpOffice\PhpWord\Style\Font */ - private $fontStyle; + protected $fontStyle; /** * Paragraph style * * @var \PhpOffice\PhpWord\Style\Paragraph */ - private $paragraphStyle; + protected $paragraphStyle; public function __construct(AbstractWriter $parentWriter, Element $element, $withoutP = false) { diff --git a/src/PhpWord/Writer/RTF/Element/Table.php b/src/PhpWord/Writer/RTF/Element/Table.php index 8154aa7cf5..63c3e6a31f 100644 --- a/src/PhpWord/Writer/RTF/Element/Table.php +++ b/src/PhpWord/Writer/RTF/Element/Table.php @@ -58,6 +58,7 @@ public function write() $content .= $this->writeRow($rows[$i]); $content .= '\row' . PHP_EOL; } + $content .= '\pard' . PHP_EOL; } return $content; diff --git a/src/PhpWord/Writer/RTF/Element/TextRun.php b/src/PhpWord/Writer/RTF/Element/TextRun.php index bfd161f08a..e2865d827e 100644 --- a/src/PhpWord/Writer/RTF/Element/TextRun.php +++ b/src/PhpWord/Writer/RTF/Element/TextRun.php @@ -32,6 +32,7 @@ class TextRun extends AbstractElement public function write() { $writer = new Container($this->parentWriter, $this->element); + $this->getStyles(); $content = ''; $content .= $this->writeOpening(); diff --git a/src/PhpWord/Writer/RTF/Element/Title.php b/src/PhpWord/Writer/RTF/Element/Title.php index a9940ca99a..3d5e08cad7 100644 --- a/src/PhpWord/Writer/RTF/Element/Title.php +++ b/src/PhpWord/Writer/RTF/Element/Title.php @@ -24,4 +24,71 @@ */ class Title extends Text { + protected function getStyles() + { + /** @var \PhpOffice\PhpWord\Element\Text $element Type hint */ + $element = $this->element; + $style = $element->getStyle(); + if (is_string($style)) { + $style = str_replace('Heading', 'Heading_', $style); + $style = \PhpOffice\PhpWord\Style::getStyle($style); + if ($style instanceof \PhpOffice\PhpWord\Style\Font) { + $this->fontStyle = $style; + $pstyle = $style->getParagraph(); + if ($pstyle instanceof \PhpOffice\PhpWord\Style\Paragraph && $pstyle->hasPageBreakBefore()) { + $sect = $element->getParent(); + if ($sect instanceof \PhpOffice\PhpWord\Element\Section) { + $elems = $sect->getElements(); + if ($elems[0] === $element) { + $pstyle = clone $pstyle; + $pstyle->setPageBreakBefore(false); + } + } + } + $this->paragraphStyle = $pstyle; + } + } + } + + /** + * Write element + * + * @return string + */ + public function write() + { + /** @var \PhpOffice\PhpWord\Element\Text $element Type hint */ + $element = $this->element; + $elementClass = str_replace('\\Writer\\RTF', '', get_class($this)); + if (!$element instanceof $elementClass || !is_string($element->getText())) { + return ''; + } + + $this->getStyles(); + + $content = ''; + + $content .= $this->writeOpening(); + $endout = ''; + $style = $element->getStyle(); + if (is_string($style)) { + $style = str_replace('Heading', '', $style); + if (is_numeric($style)) { + $style = (int) $style - 1; + if ($style >= 0 && $style <= 8) { + $content .= '{\\outlinelevel' . $style; + $endout = '}'; + } + } + } + + $content .= '{'; + $content .= $this->writeFontStyle(); + $content .= $this->writeText($element->getText()); + $content .= '}'; + $content .= $this->writeClosing(); + $content .= $endout; + + return $content; + } } diff --git a/src/PhpWord/Writer/RTF/Part/Document.php b/src/PhpWord/Writer/RTF/Part/Document.php index d4bfadb4c5..5d4d23d97b 100644 --- a/src/PhpWord/Writer/RTF/Part/Document.php +++ b/src/PhpWord/Writer/RTF/Part/Document.php @@ -17,6 +17,7 @@ namespace PhpOffice\PhpWord\Writer\RTF\Part; +use PhpOffice\PhpWord\Element\Footer; use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Writer\RTF\Element\Container; use PhpOffice\PhpWord\Writer\RTF\Style\Section as SectionStyleWriter; @@ -105,11 +106,36 @@ private function writeFormatting() $content .= '\lang' . $langId; $content .= '\kerning1'; // Point size (in half-points) above which to kern character pairs $content .= '\fs' . (Settings::getDefaultFontSize() * 2); // Set the font size in half-points + if ($docSettings->hasEvenAndOddHeaders()) { + $content .= '\\facingp'; + } $content .= PHP_EOL; return $content; } + /** + * Write titlepg directive if any "f" headers or footers + * + * @param \PhpOffice\PhpWord\PhpWord\Element\Section $section + * @return string + */ + private static function writeTitlepg($section) + { + foreach ($section->getHeaders() as $header) { + if ($header->getType() === Footer::FIRST) { + return '\\titlepg' . PHP_EOL; + } + } + foreach ($section->getFooters() as $header) { + if ($header->getType() === Footer::FIRST) { + return '\\titlepg' . PHP_EOL; + } + } + + return ''; + } + /** * Write sections * @@ -120,10 +146,53 @@ private function writeSections() $content = ''; $sections = $this->getParentWriter()->getPhpWord()->getSections(); + $evenOdd = $this->getParentWriter()->getPhpWord()->getSettings()->hasEvenAndOddHeaders(); foreach ($sections as $section) { $styleWriter = new SectionStyleWriter($section->getStyle()); $styleWriter->setParentWriter($this->getParentWriter()); $content .= $styleWriter->write(); + $content .= self::writeTitlepg($section); + + foreach ($section->getHeaders() as $header) { + $type = $header->getType(); + if ($evenOdd || $type !== FOOTER::EVEN) { + $content .= '{\\header'; + if ($type === Footer::FIRST) { + $content .= 'f'; + } elseif ($evenOdd) { + $content .= ($type === FOOTER::EVEN) ? 'l' : 'r'; + } + foreach ($header->getElements() as $element) { + $cl = get_class($element); + $cl2 = str_replace('Element', 'Writer\\RTF\\Element', $cl); + if (class_exists($cl2)) { + $elementWriter = new $cl2($this->getParentWriter(), $element); + $content .= $elementWriter->write(); + } + } + $content .= '}' . PHP_EOL; + } + } + foreach ($section->getFooters() as $footer) { + $type = $footer->getType(); + if ($evenOdd || $type !== FOOTER::EVEN) { + $content .= '{\\footer'; + if ($type === Footer::FIRST) { + $content .= 'f'; + } elseif ($evenOdd) { + $content .= ($type === FOOTER::EVEN) ? 'l' : 'r'; + } + foreach ($footer->getElements() as $element) { + $cl = get_class($element); + $cl2 = str_replace('Element', 'Writer\\RTF\\Element', $cl); + if (class_exists($cl2)) { + $elementWriter = new $cl2($this->getParentWriter(), $element); + $content .= $elementWriter->write(); + } + } + $content .= '}' . PHP_EOL; + } + } $elementWriter = new Container($this->getParentWriter(), $section); $content .= $elementWriter->write(); diff --git a/src/PhpWord/Writer/RTF/Style/Font.php b/src/PhpWord/Writer/RTF/Style/Font.php index b9001ea0ea..34f6c1afae 100644 --- a/src/PhpWord/Writer/RTF/Style/Font.php +++ b/src/PhpWord/Writer/RTF/Style/Font.php @@ -49,6 +49,7 @@ public function write() } $content = ''; + $content .= $this->getValueIf($style->isRTL(), '\rtlch'); $content .= '\cf' . $this->colorIndex; $content .= '\f' . $this->nameIndex; diff --git a/src/PhpWord/Writer/RTF/Style/Paragraph.php b/src/PhpWord/Writer/RTF/Style/Paragraph.php index 8ef3e146d9..3394f9d43f 100644 --- a/src/PhpWord/Writer/RTF/Style/Paragraph.php +++ b/src/PhpWord/Writer/RTF/Style/Paragraph.php @@ -52,6 +52,8 @@ public function write() Jc::END => '\qr', Jc::CENTER => '\qc', Jc::BOTH => '\qj', + Jc::LEFT => '\ql', + Jc::RIGHT => '\qr', ); $spaceAfter = $style->getSpaceAfter(); @@ -67,6 +69,14 @@ public function write() $content .= $this->writeIndentation($style->getIndentation()); $content .= $this->getValueIf($spaceBefore !== null, '\sb' . round($spaceBefore)); $content .= $this->getValueIf($spaceAfter !== null, '\sa' . round($spaceAfter)); + $lineHeight = $style->getLineHeight(); + if ($lineHeight !== null) { + $lineHeightAdjusted = (int) ($lineHeight * 240); + $content .= "\\sl$lineHeightAdjusted\\slmult1"; + } + if ($style->getPageBreakBefore()) { + $content .= '\\page'; + } $styles = $style->getStyleValues(); $content .= $this->writeTabs($styles['tabs']); diff --git a/src/PhpWord/Writer/RTF/Style/Section.php b/src/PhpWord/Writer/RTF/Style/Section.php index 190bb67036..c680194763 100644 --- a/src/PhpWord/Writer/RTF/Style/Section.php +++ b/src/PhpWord/Writer/RTF/Style/Section.php @@ -53,6 +53,7 @@ public function write() $content .= $this->getValueIf($style->getHeaderHeight() !== null, '\headery' . round($style->getHeaderHeight())); $content .= $this->getValueIf($style->getFooterHeight() !== null, '\footery' . round($style->getFooterHeight())); $content .= $this->getValueIf($style->getGutter() !== null, '\guttersxn' . round($style->getGutter())); + $content .= $this->getValueIf($style->getPageNumberingStart() !== null, '\pgnstarts' . $style->getPageNumberingStart() . '\pgnrestart'); $content .= ' '; // Borders diff --git a/tests/PhpWord/Shared/ConverterTest.php b/tests/PhpWord/Shared/ConverterTest.php index 122385a9b4..0d10dc4936 100644 --- a/tests/PhpWord/Shared/ConverterTest.php +++ b/tests/PhpWord/Shared/ConverterTest.php @@ -108,19 +108,13 @@ public function testUnitConversions() */ public function testHtmlToRGB() { - // Prepare test values [ original, expected ] - $values = array(); - $values[] = array('#FF99DD', array(255, 153, 221)); // With # - $values[] = array('FF99DD', array(255, 153, 221)); // 6 characters - $values[] = array('F9D', array(255, 153, 221)); // 3 characters - $values[] = array('0F9D', false); // 4 characters - $values[] = array(\PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKMAGENTA, array(139, 0, 139)); - $values[] = array('unknow', array(0, 0, 0)); // 6 characters, invalid - // Conduct test - foreach ($values as $value) { - $result = Converter::htmlToRgb($value[0]); - $this->assertEquals($value[1], $result); - } + $flse = false; + $this->assertEquals(array(255, 153, 221), Converter::htmlToRgb('#FF99DD')); // With # + $this->assertEquals(array(224, 170, 29), Converter::htmlToRgb('E0AA1D')); // 6 characters + $this->assertEquals(array(102, 119, 136), Converter::htmlToRgb('678')); // 3 characters + $this->assertEquals($flse, Converter::htmlToRgb('0F9D')); // 4 characters + $this->assertEquals(array(0, 0, 0), Converter::htmlToRgb('unknow')); // 6 characters, invalid + $this->assertEquals(array(139, 0, 139), Converter::htmlToRgb(\PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKMAGENTA)); // Constant } /** diff --git a/tests/PhpWord/Writer/RTF/ElementTest.php b/tests/PhpWord/Writer/RTF/ElementTest.php index 3e9c235d13..44287d7171 100644 --- a/tests/PhpWord/Writer/RTF/ElementTest.php +++ b/tests/PhpWord/Writer/RTF/ElementTest.php @@ -80,4 +80,80 @@ public function testIndexField() $this->assertEquals("{}\\par\n", $this->removeCr($field)); } + + public function testTable() + { + $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 \PhpOffice\PhpWord\Writer\RTF\Element\Table($parentWriter, $element); + $expect = implode("\n", array( + '\\pard', + "\\trowd \\cellx$width \\cellx$width2 ", + '\\intbl', + '{\\cf0\\f0 1}\\par', + '\\cell', + '\\intbl', + '{\\cf0\\f0 2}\\par', + '\\cell', + '\\row', + "\\trowd \\cellx$width \\cellx$width2 ", + '\\intbl', + '{\\cf0\\f0 3}\\par', + '\\cell', + '\\intbl', + '{\\cf0\\f0 4}\par', + '\\cell', + '\\row', + '\\pard', + '', + )); + + $this->assertEquals($expect, $this->removeCr($table)); + } + + public function testTextRun() + { + $parentWriter = new RTF(); + $element = new \PhpOffice\PhpWord\Element\TextRun(); + $element->addText('Hello '); + $element->addText('there.'); + $textrun = new \PhpOffice\PhpWord\Writer\RTF\Element\TextRun($parentWriter, $element); + $expect = "\\pard\\nowidctlpar {{\\cf0\\f0 Hello }{\\cf0\\f0 there.}}\\par\n"; + $this->assertEquals($expect, $this->removeCr($textrun)); + } + + public function testTextRunParagraphStyle() + { + $parentWriter = new RTF(); + $element = new \PhpOffice\PhpWord\Element\TextRun(array('spaceBefore' => 0, 'spaceAfter' => 0)); + $element->addText('Hello '); + $element->addText('there.'); + $textrun = new \PhpOffice\PhpWord\Writer\RTF\Element\TextRun($parentWriter, $element); + $expect = "\\pard\\nowidctlpar \\sb0\\sa0{{\\cf0\\f0 Hello }{\\cf0\\f0 there.}}\\par\n"; + $this->assertEquals($expect, $this->removeCr($textrun)); + } + + public function testTitle() + { + $parentWriter = new RTF(); + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $phpWord->addTitleStyle(1, array(), array('spaceBefore' => 0, 'spaceAfter' => 0)); + $section = $phpWord->addSection(); + $element = $section->addTitle('First Heading', 1); + $elwrite = new \PhpOffice\PhpWord\Writer\RTF\Element\Title($parentWriter, $element); + $expect = "\\pard\\nowidctlpar \\sb0\\sa0{\\outlinelevel0{\\cf0\\f0 First Heading}\\par\n}"; + $this->assertEquals($expect, $this->removeCr($elwrite)); + } } diff --git a/tests/PhpWord/Writer/RTF/HeaderFooterTest.php b/tests/PhpWord/Writer/RTF/HeaderFooterTest.php new file mode 100644 index 0000000000..81c589f4b3 --- /dev/null +++ b/tests/PhpWord/Writer/RTF/HeaderFooterTest.php @@ -0,0 +1,78 @@ +addSection(); + $section->addText('Doc without header or footer'); + $contents = $parentWriter->getWriterPart('Document')->write(); + $this->assertEquals(0, preg_match('/\\\\header[rlf]?\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\footer[rlf]?\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\titlepg\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\facingp\\b/', $contents)); + } + + public function testNoHeaderYesFooter() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $parentWriter = new RTF($phpWord); + $section = $phpWord->addSection(); + $footer = $section->addFooter(); + $footer->addText('Auto footer'); + $section->addText('Doc without header but with footer'); + $contents = $parentWriter->getWriterPart('Document')->write(); + $this->assertEquals(0, preg_match('/\\\\header[rlf]?\\b/', $contents)); + $this->assertEquals(1, preg_match('/\\\\footer[rlf]?\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\titlepg\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\facingp\\b/', $contents)); + } + + public function testEvenHeaderFirstFooter() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $phpWord->getSettings()->setEvenAndOddHeaders(true); + $parentWriter = new RTF($phpWord); + $section = $phpWord->addSection(); + $footer = $section->addFooter(Footer::FIRST); + $footer->addText('First footer'); + $footer = $section->addHeader(Footer::EVEN); + $footer->addText('Even footer'); + $footer = $section->addHeader(Footer::AUTO); + $footer->addText('Odd footer'); + $section->addText('Doc with even/odd header and first footer'); + $contents = $parentWriter->getWriterPart('Document')->write(); + $this->assertEquals(1, preg_match('/\\\\headerr\\b/', $contents)); + $this->assertEquals(1, preg_match('/\\\\headerl\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\header[f]?\\b/', $contents)); + $this->assertEquals(1, preg_match('/\\\\footerf\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\footer[rl]?\\b/', $contents)); + $this->assertEquals(1, preg_match('/\\\\titlepg\\b/', $contents)); + $this->assertEquals(1, preg_match('/\\\\facingp\\b/', $contents)); + } +} diff --git a/tests/PhpWord/Writer/RTF/StyleTest.php b/tests/PhpWord/Writer/RTF/StyleTest.php index 317014c610..6624f5ee4d 100644 --- a/tests/PhpWord/Writer/RTF/StyleTest.php +++ b/tests/PhpWord/Writer/RTF/StyleTest.php @@ -26,6 +26,11 @@ */ class StyleTest extends \PHPUnit\Framework\TestCase { + public function removeCr($field) + { + return str_replace("\r\n", "\n", $field->write()); + } + /** * Test empty styles */ @@ -108,4 +113,44 @@ public function testDecimalTab() Assert::assertEquals('\tqdec\tx0', $result); } + + public function testRTL() + { + $parentWriter = new RTF(); + $element = new \PhpOffice\PhpWord\Element\Text('אב גד', array('RTL'=> true)); + $text = new \PhpOffice\PhpWord\Writer\RTF\Element\Text($parentWriter, $element); + $expect = "\\pard\\nowidctlpar {\\rtlch\\cf0\\f0 \\uc0{\\u1488}\\uc0{\\u1489} \\uc0{\\u1490}\\uc0{\\u1491}}\\par\n"; + $this->assertEquals($expect, $this->removeCr($text)); + } + + public function testPageBreakLineHeight() + { + $parentWriter = new RTF(); + $element = new \PhpOffice\PhpWord\Element\Text('New page', null, array('lineHeight' => 1.08, 'pageBreakBefore' => true)); + $text = new \PhpOffice\PhpWord\Writer\RTF\Element\Text($parentWriter, $element); + $expect = "\\pard\\nowidctlpar \\sl259\\slmult1\\page{\\cf0\\f0 New page}\\par\n"; + $this->assertEquals($expect, $this->removeCr($text)); + } + + public function testPageNumberRestart() + { + //$parentWriter = new RTF(); + $phpword = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpword->addSection(array('pageNumberingStart' => 5)); + $styleWriter = new \PhpOffice\PhpWord\Writer\RTF\Style\Section($section->getStyle()); + $wstyle = $this->removeCr($styleWriter); + // following have default values which might change so don't use them + $wstyle = preg_replace('/\\\\pgwsxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\pghsxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\margtsxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\margrsxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\margbsxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\marglsxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\headery\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\footery\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\guttersxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/ +/', ' ', $wstyle); + $expect = "\\sectd \\pgnstarts5\\pgnrestart \n"; + $this->assertEquals($expect, $wstyle); + } } From 7657992a83a272c44ec6f50e868173a8a52c233d Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 5 Dec 2019 22:51:00 -0800 Subject: [PATCH 3/5] Scrutinizer-suggested changes Changes to doc-blocks and code suggested by Scrutinizer. --- src/PhpWord/Writer/RTF/Element/Title.php | 2 +- src/PhpWord/Writer/RTF/Part/Document.php | 2 +- src/PhpWord/Writer/RTF/Style/Paragraph.php | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/PhpWord/Writer/RTF/Element/Title.php b/src/PhpWord/Writer/RTF/Element/Title.php index 3d5e08cad7..d5c8d455b3 100644 --- a/src/PhpWord/Writer/RTF/Element/Title.php +++ b/src/PhpWord/Writer/RTF/Element/Title.php @@ -26,7 +26,7 @@ class Title extends Text { protected function getStyles() { - /** @var \PhpOffice\PhpWord\Element\Text $element Type hint */ + /** @var \PhpOffice\PhpWord\Element\Title $element Type hint */ $element = $this->element; $style = $element->getStyle(); if (is_string($style)) { diff --git a/src/PhpWord/Writer/RTF/Part/Document.php b/src/PhpWord/Writer/RTF/Part/Document.php index 5d4d23d97b..14d900944e 100644 --- a/src/PhpWord/Writer/RTF/Part/Document.php +++ b/src/PhpWord/Writer/RTF/Part/Document.php @@ -117,7 +117,7 @@ private function writeFormatting() /** * Write titlepg directive if any "f" headers or footers * - * @param \PhpOffice\PhpWord\PhpWord\Element\Section $section + * @param \PhpOffice\PhpWord\Element\Section $section * @return string */ private static function writeTitlepg($section) diff --git a/src/PhpWord/Writer/RTF/Style/Paragraph.php b/src/PhpWord/Writer/RTF/Style/Paragraph.php index 3394f9d43f..a9c060ac45 100644 --- a/src/PhpWord/Writer/RTF/Style/Paragraph.php +++ b/src/PhpWord/Writer/RTF/Style/Paragraph.php @@ -52,8 +52,8 @@ public function write() Jc::END => '\qr', Jc::CENTER => '\qc', Jc::BOTH => '\qj', - Jc::LEFT => '\ql', - Jc::RIGHT => '\qr', + "left" => '\ql', + "right" => '\qr', ); $spaceAfter = $style->getSpaceAfter(); @@ -70,11 +70,11 @@ public function write() $content .= $this->getValueIf($spaceBefore !== null, '\sb' . round($spaceBefore)); $content .= $this->getValueIf($spaceAfter !== null, '\sa' . round($spaceAfter)); $lineHeight = $style->getLineHeight(); - if ($lineHeight !== null) { + if ($lineHeight) { $lineHeightAdjusted = (int) ($lineHeight * 240); $content .= "\\sl$lineHeightAdjusted\\slmult1"; } - if ($style->getPageBreakBefore()) { + if ($style->hasPageBreakBefore()) { $content .= '\\page'; } From 5e64b264512265f8bdd3510b86f758be9cffd210 Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 5 Dec 2019 23:24:03 -0800 Subject: [PATCH 4/5] Additional Scrutinizer Recommendations Some more editorial changes. --- src/PhpWord/Writer/RTF/Element/Title.php | 30 ++++++++++------------ src/PhpWord/Writer/RTF/Style/Paragraph.php | 2 -- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/PhpWord/Writer/RTF/Element/Title.php b/src/PhpWord/Writer/RTF/Element/Title.php index d5c8d455b3..77ebff1769 100644 --- a/src/PhpWord/Writer/RTF/Element/Title.php +++ b/src/PhpWord/Writer/RTF/Element/Title.php @@ -29,24 +29,22 @@ protected function getStyles() /** @var \PhpOffice\PhpWord\Element\Title $element Type hint */ $element = $this->element; $style = $element->getStyle(); - if (is_string($style)) { - $style = str_replace('Heading', 'Heading_', $style); - $style = \PhpOffice\PhpWord\Style::getStyle($style); - if ($style instanceof \PhpOffice\PhpWord\Style\Font) { - $this->fontStyle = $style; - $pstyle = $style->getParagraph(); - if ($pstyle instanceof \PhpOffice\PhpWord\Style\Paragraph && $pstyle->hasPageBreakBefore()) { - $sect = $element->getParent(); - if ($sect instanceof \PhpOffice\PhpWord\Element\Section) { - $elems = $sect->getElements(); - if ($elems[0] === $element) { - $pstyle = clone $pstyle; - $pstyle->setPageBreakBefore(false); - } + $style = str_replace('Heading', 'Heading_', $style); + $style = \PhpOffice\PhpWord\Style::getStyle($style); + if ($style instanceof \PhpOffice\PhpWord\Style\Font) { + $this->fontStyle = $style; + $pstyle = $style->getParagraph(); + if ($pstyle instanceof \PhpOffice\PhpWord\Style\Paragraph && $pstyle->hasPageBreakBefore()) { + $sect = $element->getParent(); + if ($sect instanceof \PhpOffice\PhpWord\Element\Section) { + $elems = $sect->getElements(); + if ($elems[0] === $element) { + $pstyle = clone $pstyle; + $pstyle->setPageBreakBefore(false); } } - $this->paragraphStyle = $pstyle; } + $this->paragraphStyle = $pstyle; } } @@ -57,7 +55,7 @@ protected function getStyles() */ public function write() { - /** @var \PhpOffice\PhpWord\Element\Text $element Type hint */ + /** @var \PhpOffice\PhpWord\Element\Title $element Type hint */ $element = $this->element; $elementClass = str_replace('\\Writer\\RTF', '', get_class($this)); if (!$element instanceof $elementClass || !is_string($element->getText())) { diff --git a/src/PhpWord/Writer/RTF/Style/Paragraph.php b/src/PhpWord/Writer/RTF/Style/Paragraph.php index a9c060ac45..9f8cf9dff0 100644 --- a/src/PhpWord/Writer/RTF/Style/Paragraph.php +++ b/src/PhpWord/Writer/RTF/Style/Paragraph.php @@ -52,8 +52,6 @@ public function write() Jc::END => '\qr', Jc::CENTER => '\qc', Jc::BOTH => '\qj', - "left" => '\ql', - "right" => '\qr', ); $spaceAfter = $style->getSpaceAfter(); From 122aaf17b11cbc69afffbfb8a91d79fb4480f79b Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 5 Dec 2019 23:48:44 -0800 Subject: [PATCH 5/5] Incorporate Pull Request 1771 Fix PHPUnit tests on develop branch --- .../AbstractWebServerEmbeddedTest.php | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php index 9316a9fe61..5d1e3c1219 100644 --- a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php +++ b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php @@ -26,7 +26,25 @@ abstract class AbstractWebServerEmbeddedTest extends \PHPUnit\Framework\TestCase public static function setUpBeforeClass() { if (self::isBuiltinServerSupported()) { - self::$httpServer = new Process('php -S localhost:8080 -t tests/PhpWord/_files'); + $commandLine = 'php -S localhost:8080 -t tests/PhpWord/_files'; + /* + * Make sure to invoke \Symfony\Component\Process\Process correctly + * regardless of PHP version used. + * + * In Process version >= 5 / PHP >= 7.2.5, the constructor requires + * an array, while in version < 3.3 / PHP < 5.5.9 it requires a string. + * In between, it can accept both. + * + * Process::fromShellCommandLine() was introduced in version 4.2.0, + * to enable recent versions of Process to parse a command string, + * so if it is not available it means it is still possible to pass + * a string to the constructor. + */ + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandLine')) { + self::$httpServer = Process::fromShellCommandline($commandLine); + } else { + self::$httpServer = new Process($commandLine); + } self::$httpServer->start(); while (!self::$httpServer->isRunning()) { usleep(1000);