From ebf5cf784f1f67e09304984486a7ec2faac8cac3 Mon Sep 17 00:00:00 2001 From: owen Date: Tue, 19 Nov 2019 14:24:29 -0800 Subject: [PATCH 1/8] 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/8] 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 11d82be21c8a7eef9a157201fdd833bb2aa736d3 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Thu, 5 Dec 2019 21:04:12 -0800 Subject: [PATCH 3/8] Word2007 Writer - Field Style, RTL, noProof 1. Add support for font styles for fields, and write those to Word docs. 2. Word seems to require explicit inline w:rtl tag even when rtl is specified in a named style. Without this tag, words are placed in ltr order. Allow PhpWord doc to specify rtl in named style and have it display correctly in resulting doc. 3. A recent change incorrectly changed how noProof tag was generated, omitting the third parameter of 4 parameters in the call. There was no test case for this change. The call is now corrected, and a test case has been added. --- docs/elements.rst | 4 +- src/PhpWord/Element/Field.php | 38 +++++++++- src/PhpWord/Writer/Word2007/Element/Field.php | 1 + src/PhpWord/Writer/Word2007/Style/Font.php | 6 +- tests/PhpWord/Writer/Word2007/ElementTest.php | 35 +++++++++ .../Writer/Word2007/Style/FontTest.php | 74 +++++++++++++++++++ 6 files changed, 155 insertions(+), 3 deletions(-) diff --git a/docs/elements.rst b/docs/elements.rst index 9d446b27d0..176de47b61 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -403,7 +403,9 @@ Currently the following fields are supported: .. code-block:: php - $section->addField($fieldType, [$properties], [$options], [$fieldText]) + $section->addField($fieldType, [$properties], [$options], [$fieldText], [$fontStyle]) + +- ``$fontStyle``. See :ref:`font-style`. See ``\PhpOffice\PhpWord\Element\Field`` for list of properties and options available for each field type. Options which are not specifically defined can be added. Those must start with a ``\``. diff --git a/src/PhpWord/Element/Field.php b/src/PhpWord/Element/Field.php index 2efc6b0b9f..e6777d2018 100644 --- a/src/PhpWord/Element/Field.php +++ b/src/PhpWord/Element/Field.php @@ -17,6 +17,8 @@ namespace PhpOffice\PhpWord\Element; +use PhpOffice\PhpWord\Style\Font; + /** * Field element * @@ -119,6 +121,38 @@ class Field extends AbstractElement */ protected $fontStyle; + /** + * Set Font style + * + * @param string|array|\PhpOffice\PhpWord\Style\Font $style + * @return string|\PhpOffice\PhpWord\Style\Font + */ + public function setFontStyle($style = null) + { + if ($style instanceof Font) { + $this->fontStyle = $style; + } elseif (is_array($style)) { + $this->fontStyle = new Font('text'); + $this->fontStyle->setStyleByArray($style); + } elseif (null === $style) { + $this->fontStyle = null; + } else { + $this->fontStyle = $style; + } + + return $this->fontStyle; + } + + /** + * Get Font style + * + * @return string|\PhpOffice\PhpWord\Style\Font + */ + public function getFontStyle() + { + return $this->fontStyle; + } + /** * Create a new Field Element * @@ -126,13 +160,15 @@ class Field extends AbstractElement * @param array $properties * @param array $options * @param TextRun|string|null $text + * @param string|array|\PhpOffice\PhpWord\Style\Font $fontStyle */ - public function __construct($type = null, $properties = array(), $options = array(), $text = null) + public function __construct($type = null, $properties = array(), $options = array(), $text = null, $fontStyle = null) { $this->setType($type); $this->setProperties($properties); $this->setOptions($options); $this->setText($text); + $this->setFontStyle($fontStyle); } /** diff --git a/src/PhpWord/Writer/Word2007/Element/Field.php b/src/PhpWord/Writer/Word2007/Element/Field.php index e79dd24a90..b800fbddc3 100644 --- a/src/PhpWord/Writer/Word2007/Element/Field.php +++ b/src/PhpWord/Writer/Word2007/Element/Field.php @@ -65,6 +65,7 @@ private function writeDefault(\PhpOffice\PhpWord\Element\Field $element) $instruction .= $this->buildPropertiesAndOptions($element); } $xmlWriter->startElement('w:r'); + $this->writeFontStyle(); $xmlWriter->startElement('w:instrText'); $xmlWriter->writeAttribute('xml:space', 'preserve'); $xmlWriter->text($instruction); diff --git a/src/PhpWord/Writer/Word2007/Style/Font.php b/src/PhpWord/Writer/Word2007/Style/Font.php index dd4fac4f0d..2f2218fa57 100644 --- a/src/PhpWord/Writer/Word2007/Style/Font.php +++ b/src/PhpWord/Writer/Word2007/Style/Font.php @@ -44,6 +44,10 @@ public function write() $xmlWriter->startElement('w:rStyle'); $xmlWriter->writeAttribute('w:val', $this->style); $xmlWriter->endElement(); + $style = \PhpOffice\PhpWord\Style::getStyle($this->style); + if ($style instanceof \PhpOffice\PhpWord\Style\Font) { + $xmlWriter->writeElementIf($style->isRTL(), 'w:rtl'); + } $xmlWriter->endElement(); } else { $this->writeStyle(); @@ -139,7 +143,7 @@ private function writeStyle() $xmlWriter->writeElementIf($style->getKerning() !== null, 'w:kern', 'w:val', $style->getKerning() * 2); // noProof - $xmlWriter->writeElementIf($style->isNoProof() !== null, 'w:noProof', $this->writeOnOf($style->isNoProof())); + $xmlWriter->writeElementIf($style->isNoProof() !== null, 'w:noProof', 'w:val', $this->writeOnOf($style->isNoProof())); // Background-Color $shading = $style->getShading(); diff --git a/tests/PhpWord/Writer/Word2007/ElementTest.php b/tests/PhpWord/Writer/Word2007/ElementTest.php index 6a295965f7..e9c1ea5bd7 100644 --- a/tests/PhpWord/Writer/Word2007/ElementTest.php +++ b/tests/PhpWord/Writer/Word2007/ElementTest.php @@ -296,6 +296,41 @@ public function testFieldElement() $this->assertEquals(' INDEX \\c "3" ', $doc->getElement($element)->textContent); } + public function testUnstyledFieldElement() + { + $phpWord = new PhpWord(); + $phpWord->addFontStyle('h1', array('name' => 'Courier New', 'size' => 8)); + $section = $phpWord->addSection(); + + $section->addField('PAGE'); + $doc = TestHelperDOCX::getDocument($phpWord); + + $element = '/w:document/w:body/w:p/w:r[2]/w:instrText'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals(' PAGE ', $doc->getElement($element)->textContent); + $sty = '/w:document/w:body/w:p/w:r[2]/w:rPr'; + $this->assertFalse($doc->elementExists($sty)); + } + + public function testStyledFieldElement() + { + $phpWord = new PhpWord(); + $stnam = 'h1'; + $phpWord->addFontStyle($stnam, array('name' => 'Courier New', 'size' => 8)); + $section = $phpWord->addSection(); + + $fld = $section->addField('PAGE'); + $fld->setFontStyle($stnam); + $doc = TestHelperDOCX::getDocument($phpWord); + + $element = '/w:document/w:body/w:p/w:r[2]/w:instrText'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals(' PAGE ', $doc->getElement($element)->textContent); + $sty = '/w:document/w:body/w:p/w:r[2]/w:rPr'; + $this->assertTrue($doc->elementExists($sty)); + $this->assertEquals($stnam, $doc->getElementAttribute($sty . '/w:rStyle', 'w:val')); + } + public function testFieldElementWithComplexText() { $phpWord = new PhpWord(); diff --git a/tests/PhpWord/Writer/Word2007/Style/FontTest.php b/tests/PhpWord/Writer/Word2007/Style/FontTest.php index ccfffbfb01..41e52ab027 100644 --- a/tests/PhpWord/Writer/Word2007/Style/FontTest.php +++ b/tests/PhpWord/Writer/Word2007/Style/FontTest.php @@ -51,6 +51,80 @@ public function testFontRTL() $this->assertTrue($doc->elementExists($path, $file)); } + public function testFontRTLNamed() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $stnam = 'fstyle'; + $phpWord->addFontStyle($stnam, array( + 'rtl' => true, + 'name' => 'Courier New', + 'size' => 8, + )); + $section = $phpWord->addSection(); + $txt = 'היום יום שני'; // Translation = Today is Monday + $section->addText($txt, $stnam); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $element = '/w:document/w:body/w:p/w:r'; + $txtelem = $element . '/w:t'; + $styelem = $element . '/w:rPr'; + $this->assertTrue($doc->elementExists($txtelem)); + $this->assertEquals($txt, $doc->getElement($txtelem)->textContent); + $this->assertTrue($doc->elementExists($styelem)); + $this->assertTrue($doc->elementExists($styelem . '/w:rStyle')); + $this->assertEquals($stnam, $doc->getElementAttribute($styelem . '/w:rStyle', 'w:val')); + $this->assertTrue($doc->elementExists($styelem . '/w:rtl')); + } + + public function testFontNotRTLNamed() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $stnam = 'fstyle'; + $phpWord->addFontStyle($stnam, array( + //'rtl' => true, + 'name' => 'Courier New', + 'size' => 8, + )); + $section = $phpWord->addSection(); + $txt = 'היום יום שני'; // Translation = Today is Monday + $section->addText($txt, $stnam); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $element = '/w:document/w:body/w:p/w:r'; + $txtelem = $element . '/w:t'; + $styelem = $element . '/w:rPr'; + $this->assertTrue($doc->elementExists($txtelem)); + $this->assertEquals($txt, $doc->getElement($txtelem)->textContent); + $this->assertTrue($doc->elementExists($styelem)); + $this->assertTrue($doc->elementExists($styelem . '/w:rStyle')); + $this->assertEquals($stnam, $doc->getElementAttribute($styelem . '/w:rStyle', 'w:val')); + $this->assertFalse($doc->elementExists($styelem . '/w:rtl')); + } + + public function testNoProof() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $fontStyle = array( + 'noProof' => true, + 'name' => 'Courier New', + 'size' => 8, + ); + $section = $phpWord->addSection(); + $txt = 'spellung error'; + $section->addText($txt, $fontStyle); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $element = '/w:document/w:body/w:p/w:r'; + $txtelem = $element . '/w:t'; + $styelem = $element . '/w:rPr'; + $noproofelem = $styelem . '/w:noProof'; + $this->assertTrue($doc->elementExists($txtelem)); + $this->assertEquals($txt, $doc->getElement($txtelem)->textContent); + $this->assertTrue($doc->elementExists($styelem)); + $this->assertTrue($doc->elementExists($noproofelem)); + $this->assertEquals('1', $doc->getElementAttribute($noproofelem, 'w:val')); + } + /** * Test writing font with language */ From 30e3981ed2a3cae5df4b5902541ac69d0b65b3a9 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Thu, 5 Dec 2019 21:51:09 -0800 Subject: [PATCH 4/8] Documentation Change Changed 1 doc-block comment suggested by Scrutinizer. --- src/PhpWord/Element/Field.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpWord/Element/Field.php b/src/PhpWord/Element/Field.php index e6777d2018..3d1503fe22 100644 --- a/src/PhpWord/Element/Field.php +++ b/src/PhpWord/Element/Field.php @@ -117,7 +117,7 @@ class Field extends AbstractElement /** * Font style * - * @var \PhpOffice\PhpWord\Style\Font + * @var string|\PhpOffice\PhpWord\Style\Font */ protected $fontStyle; From 7657992a83a272c44ec6f50e868173a8a52c233d Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 5 Dec 2019 22:51:00 -0800 Subject: [PATCH 5/8] 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 6/8] 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 9b5483a1e075e93218b437acdd1d0862568ef20e Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Thu, 5 Dec 2019 23:42:54 -0800 Subject: [PATCH 7/8] Incorporating Pull Request 1771 That Pull Request is "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); From 122aaf17b11cbc69afffbfb8a91d79fb4480f79b Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 5 Dec 2019 23:48:44 -0800 Subject: [PATCH 8/8] 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);