Skip to content

Commit

Permalink
Merge pull request #1775 from oleibman/rtfchanges
Browse files Browse the repository at this point in the history
Add support for several features for the RTF writer
  • Loading branch information
troosan authored Jul 6, 2020
2 parents e7f70f3 + c52c7ab commit 250e206
Show file tree
Hide file tree
Showing 13 changed files with 400 additions and 13 deletions.
46 changes: 46 additions & 0 deletions src/PhpWord/Shared/Converter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -282,6 +326,8 @@ public static function htmlToRgb($value)
{
if ($value[0] == '#') {
$value = substr($value, 1);
} else {
$value = self::stringToRgb($value);
}

if (strlen($value) == 6) {
Expand Down
4 changes: 2 additions & 2 deletions src/PhpWord/Writer/RTF/Element/AbstractElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
1 change: 1 addition & 0 deletions src/PhpWord/Writer/RTF/Element/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public function write()
$content .= $this->writeRow($rows[$i]);
$content .= '\row' . PHP_EOL;
}
$content .= '\pard' . PHP_EOL;
}

return $content;
Expand Down
1 change: 1 addition & 0 deletions src/PhpWord/Writer/RTF/Element/TextRun.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class TextRun extends AbstractElement
public function write()
{
$writer = new Container($this->parentWriter, $this->element);
$this->getStyles();

$content = '';
$content .= $this->writeOpening();
Expand Down
65 changes: 65 additions & 0 deletions src/PhpWord/Writer/RTF/Element/Title.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,69 @@
*/
class Title extends Text
{
protected function getStyles()
{
/** @var \PhpOffice\PhpWord\Element\Title $element Type hint */
$element = $this->element;
$style = $element->getStyle();
$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\Title $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;
}
}
69 changes: 69 additions & 0 deletions src/PhpWord/Writer/RTF/Part/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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\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
*
Expand All @@ -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();
Expand Down
1 change: 1 addition & 0 deletions src/PhpWord/Writer/RTF/Style/Font.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public function write()
}

$content = '';
$content .= $this->getValueIf($style->isRTL(), '\rtlch');
$content .= '\cf' . $this->colorIndex;
$content .= '\f' . $this->nameIndex;

Expand Down
8 changes: 8 additions & 0 deletions src/PhpWord/Writer/RTF/Style/Paragraph.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,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) {
$lineHeightAdjusted = (int) ($lineHeight * 240);
$content .= "\\sl$lineHeightAdjusted\\slmult1";
}
if ($style->hasPageBreakBefore()) {
$content .= '\\page';
}

$styles = $style->getStyleValues();
$content .= $this->writeTabs($styles['tabs']);
Expand Down
1 change: 1 addition & 0 deletions src/PhpWord/Writer/RTF/Style/Section.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 7 additions & 11 deletions tests/PhpWord/Shared/ConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +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
// 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
}

/**
Expand Down
76 changes: 76 additions & 0 deletions tests/PhpWord/Writer/RTF/ElementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
Loading

0 comments on commit 250e206

Please sign in to comment.