Skip to content

Commit 250e206

Browse files
authored
Merge pull request #1775 from oleibman/rtfchanges
Add support for several features for the RTF writer
2 parents e7f70f3 + c52c7ab commit 250e206

File tree

13 files changed

+400
-13
lines changed

13 files changed

+400
-13
lines changed

src/PhpWord/Shared/Converter.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,50 @@ public static function angleToDegree($angle = 1)
272272
return round($angle / self::DEGREE_TO_ANGLE);
273273
}
274274

275+
/**
276+
* Convert colorname as string to RGB
277+
*
278+
* @param string $value color name
279+
* @return string color as hex RGB string, or original value if unknown
280+
*/
281+
public static function stringToRgb($value)
282+
{
283+
switch ($value) {
284+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_YELLOW:
285+
return 'FFFF00';
286+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_LIGHTGREEN:
287+
return '90EE90';
288+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_CYAN:
289+
return '00FFFF';
290+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_MAGENTA:
291+
return 'FF00FF';
292+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_BLUE:
293+
return '0000FF';
294+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_RED:
295+
return 'FF0000';
296+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKBLUE:
297+
return '00008B';
298+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKCYAN:
299+
return '008B8B';
300+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKGREEN:
301+
return '006400';
302+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKMAGENTA:
303+
return '8B008B';
304+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKRED:
305+
return '8B0000';
306+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKYELLOW:
307+
return '8B8B00';
308+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKGRAY:
309+
return 'A9A9A9';
310+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_LIGHTGRAY:
311+
return 'D3D3D3';
312+
case \PhpOffice\PhpWord\Style\Font::FGCOLOR_BLACK:
313+
return '000000';
314+
}
315+
316+
return $value;
317+
}
318+
275319
/**
276320
* Convert HTML hexadecimal to RGB
277321
*
@@ -282,6 +326,8 @@ public static function htmlToRgb($value)
282326
{
283327
if ($value[0] == '#') {
284328
$value = substr($value, 1);
329+
} else {
330+
$value = self::stringToRgb($value);
285331
}
286332

287333
if (strlen($value) == 6) {

src/PhpWord/Writer/RTF/Element/AbstractElement.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ abstract class AbstractElement extends HTMLAbstractElement
4141
*
4242
* @var \PhpOffice\PhpWord\Style\Font
4343
*/
44-
private $fontStyle;
44+
protected $fontStyle;
4545

4646
/**
4747
* Paragraph style
4848
*
4949
* @var \PhpOffice\PhpWord\Style\Paragraph
5050
*/
51-
private $paragraphStyle;
51+
protected $paragraphStyle;
5252

5353
public function __construct(AbstractWriter $parentWriter, Element $element, $withoutP = false)
5454
{

src/PhpWord/Writer/RTF/Element/Table.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public function write()
5858
$content .= $this->writeRow($rows[$i]);
5959
$content .= '\row' . PHP_EOL;
6060
}
61+
$content .= '\pard' . PHP_EOL;
6162
}
6263

6364
return $content;

src/PhpWord/Writer/RTF/Element/TextRun.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class TextRun extends AbstractElement
3232
public function write()
3333
{
3434
$writer = new Container($this->parentWriter, $this->element);
35+
$this->getStyles();
3536

3637
$content = '';
3738
$content .= $this->writeOpening();

src/PhpWord/Writer/RTF/Element/Title.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,69 @@
2424
*/
2525
class Title extends Text
2626
{
27+
protected function getStyles()
28+
{
29+
/** @var \PhpOffice\PhpWord\Element\Title $element Type hint */
30+
$element = $this->element;
31+
$style = $element->getStyle();
32+
$style = str_replace('Heading', 'Heading_', $style);
33+
$style = \PhpOffice\PhpWord\Style::getStyle($style);
34+
if ($style instanceof \PhpOffice\PhpWord\Style\Font) {
35+
$this->fontStyle = $style;
36+
$pstyle = $style->getParagraph();
37+
if ($pstyle instanceof \PhpOffice\PhpWord\Style\Paragraph && $pstyle->hasPageBreakBefore()) {
38+
$sect = $element->getParent();
39+
if ($sect instanceof \PhpOffice\PhpWord\Element\Section) {
40+
$elems = $sect->getElements();
41+
if ($elems[0] === $element) {
42+
$pstyle = clone $pstyle;
43+
$pstyle->setPageBreakBefore(false);
44+
}
45+
}
46+
}
47+
$this->paragraphStyle = $pstyle;
48+
}
49+
}
50+
51+
/**
52+
* Write element
53+
*
54+
* @return string
55+
*/
56+
public function write()
57+
{
58+
/** @var \PhpOffice\PhpWord\Element\Title $element Type hint */
59+
$element = $this->element;
60+
$elementClass = str_replace('\\Writer\\RTF', '', get_class($this));
61+
if (!$element instanceof $elementClass || !is_string($element->getText())) {
62+
return '';
63+
}
64+
65+
$this->getStyles();
66+
67+
$content = '';
68+
69+
$content .= $this->writeOpening();
70+
$endout = '';
71+
$style = $element->getStyle();
72+
if (is_string($style)) {
73+
$style = str_replace('Heading', '', $style);
74+
if (is_numeric($style)) {
75+
$style = (int) $style - 1;
76+
if ($style >= 0 && $style <= 8) {
77+
$content .= '{\\outlinelevel' . $style;
78+
$endout = '}';
79+
}
80+
}
81+
}
82+
83+
$content .= '{';
84+
$content .= $this->writeFontStyle();
85+
$content .= $this->writeText($element->getText());
86+
$content .= '}';
87+
$content .= $this->writeClosing();
88+
$content .= $endout;
89+
90+
return $content;
91+
}
2792
}

src/PhpWord/Writer/RTF/Part/Document.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
namespace PhpOffice\PhpWord\Writer\RTF\Part;
1919

20+
use PhpOffice\PhpWord\Element\Footer;
2021
use PhpOffice\PhpWord\Settings;
2122
use PhpOffice\PhpWord\Writer\RTF\Element\Container;
2223
use PhpOffice\PhpWord\Writer\RTF\Style\Section as SectionStyleWriter;
@@ -105,11 +106,36 @@ private function writeFormatting()
105106
$content .= '\lang' . $langId;
106107
$content .= '\kerning1'; // Point size (in half-points) above which to kern character pairs
107108
$content .= '\fs' . (Settings::getDefaultFontSize() * 2); // Set the font size in half-points
109+
if ($docSettings->hasEvenAndOddHeaders()) {
110+
$content .= '\\facingp';
111+
}
108112
$content .= PHP_EOL;
109113

110114
return $content;
111115
}
112116

117+
/**
118+
* Write titlepg directive if any "f" headers or footers
119+
*
120+
* @param \PhpOffice\PhpWord\Element\Section $section
121+
* @return string
122+
*/
123+
private static function writeTitlepg($section)
124+
{
125+
foreach ($section->getHeaders() as $header) {
126+
if ($header->getType() === Footer::FIRST) {
127+
return '\\titlepg' . PHP_EOL;
128+
}
129+
}
130+
foreach ($section->getFooters() as $header) {
131+
if ($header->getType() === Footer::FIRST) {
132+
return '\\titlepg' . PHP_EOL;
133+
}
134+
}
135+
136+
return '';
137+
}
138+
113139
/**
114140
* Write sections
115141
*
@@ -120,10 +146,53 @@ private function writeSections()
120146
$content = '';
121147

122148
$sections = $this->getParentWriter()->getPhpWord()->getSections();
149+
$evenOdd = $this->getParentWriter()->getPhpWord()->getSettings()->hasEvenAndOddHeaders();
123150
foreach ($sections as $section) {
124151
$styleWriter = new SectionStyleWriter($section->getStyle());
125152
$styleWriter->setParentWriter($this->getParentWriter());
126153
$content .= $styleWriter->write();
154+
$content .= self::writeTitlepg($section);
155+
156+
foreach ($section->getHeaders() as $header) {
157+
$type = $header->getType();
158+
if ($evenOdd || $type !== FOOTER::EVEN) {
159+
$content .= '{\\header';
160+
if ($type === Footer::FIRST) {
161+
$content .= 'f';
162+
} elseif ($evenOdd) {
163+
$content .= ($type === FOOTER::EVEN) ? 'l' : 'r';
164+
}
165+
foreach ($header->getElements() as $element) {
166+
$cl = get_class($element);
167+
$cl2 = str_replace('Element', 'Writer\\RTF\\Element', $cl);
168+
if (class_exists($cl2)) {
169+
$elementWriter = new $cl2($this->getParentWriter(), $element);
170+
$content .= $elementWriter->write();
171+
}
172+
}
173+
$content .= '}' . PHP_EOL;
174+
}
175+
}
176+
foreach ($section->getFooters() as $footer) {
177+
$type = $footer->getType();
178+
if ($evenOdd || $type !== FOOTER::EVEN) {
179+
$content .= '{\\footer';
180+
if ($type === Footer::FIRST) {
181+
$content .= 'f';
182+
} elseif ($evenOdd) {
183+
$content .= ($type === FOOTER::EVEN) ? 'l' : 'r';
184+
}
185+
foreach ($footer->getElements() as $element) {
186+
$cl = get_class($element);
187+
$cl2 = str_replace('Element', 'Writer\\RTF\\Element', $cl);
188+
if (class_exists($cl2)) {
189+
$elementWriter = new $cl2($this->getParentWriter(), $element);
190+
$content .= $elementWriter->write();
191+
}
192+
}
193+
$content .= '}' . PHP_EOL;
194+
}
195+
}
127196

128197
$elementWriter = new Container($this->getParentWriter(), $section);
129198
$content .= $elementWriter->write();

src/PhpWord/Writer/RTF/Style/Font.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public function write()
4949
}
5050

5151
$content = '';
52+
$content .= $this->getValueIf($style->isRTL(), '\rtlch');
5253
$content .= '\cf' . $this->colorIndex;
5354
$content .= '\f' . $this->nameIndex;
5455

src/PhpWord/Writer/RTF/Style/Paragraph.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ public function write()
6767
$content .= $this->writeIndentation($style->getIndentation());
6868
$content .= $this->getValueIf($spaceBefore !== null, '\sb' . round($spaceBefore));
6969
$content .= $this->getValueIf($spaceAfter !== null, '\sa' . round($spaceAfter));
70+
$lineHeight = $style->getLineHeight();
71+
if ($lineHeight) {
72+
$lineHeightAdjusted = (int) ($lineHeight * 240);
73+
$content .= "\\sl$lineHeightAdjusted\\slmult1";
74+
}
75+
if ($style->hasPageBreakBefore()) {
76+
$content .= '\\page';
77+
}
7078

7179
$styles = $style->getStyleValues();
7280
$content .= $this->writeTabs($styles['tabs']);

src/PhpWord/Writer/RTF/Style/Section.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public function write()
5353
$content .= $this->getValueIf($style->getHeaderHeight() !== null, '\headery' . round($style->getHeaderHeight()));
5454
$content .= $this->getValueIf($style->getFooterHeight() !== null, '\footery' . round($style->getFooterHeight()));
5555
$content .= $this->getValueIf($style->getGutter() !== null, '\guttersxn' . round($style->getGutter()));
56+
$content .= $this->getValueIf($style->getPageNumberingStart() !== null, '\pgnstarts' . $style->getPageNumberingStart() . '\pgnrestart');
5657
$content .= ' ';
5758

5859
// Borders

tests/PhpWord/Shared/ConverterTest.php

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -108,17 +108,13 @@ public function testUnitConversions()
108108
*/
109109
public function testHtmlToRGB()
110110
{
111-
// Prepare test values [ original, expected ]
112-
$values = array();
113-
$values[] = array('#FF99DD', array(255, 153, 221)); // With #
114-
$values[] = array('FF99DD', array(255, 153, 221)); // 6 characters
115-
$values[] = array('F9D', array(255, 153, 221)); // 3 characters
116-
$values[] = array('0F9D', false); // 4 characters
117-
// Conduct test
118-
foreach ($values as $value) {
119-
$result = Converter::htmlToRgb($value[0]);
120-
$this->assertEquals($value[1], $result);
121-
}
111+
$flse = false;
112+
$this->assertEquals(array(255, 153, 221), Converter::htmlToRgb('#FF99DD')); // With #
113+
$this->assertEquals(array(224, 170, 29), Converter::htmlToRgb('E0AA1D')); // 6 characters
114+
$this->assertEquals(array(102, 119, 136), Converter::htmlToRgb('678')); // 3 characters
115+
$this->assertEquals($flse, Converter::htmlToRgb('0F9D')); // 4 characters
116+
$this->assertEquals(array(0, 0, 0), Converter::htmlToRgb('unknow')); // 6 characters, invalid
117+
$this->assertEquals(array(139, 0, 139), Converter::htmlToRgb(\PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKMAGENTA)); // Constant
122118
}
123119

124120
/**

tests/PhpWord/Writer/RTF/ElementTest.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,80 @@ public function testIndexField()
8080

8181
$this->assertEquals("{}\\par\n", $this->removeCr($field));
8282
}
83+
84+
public function testTable()
85+
{
86+
$parentWriter = new RTF();
87+
$element = new \PhpOffice\PhpWord\Element\Table();
88+
$width = 100;
89+
$width2 = 2 * $width;
90+
$element->addRow();
91+
$tce = $element->addCell($width);
92+
$tce->addText('1');
93+
$tce = $element->addCell($width);
94+
$tce->addText('2');
95+
$element->addRow();
96+
$tce = $element->addCell($width);
97+
$tce->addText('3');
98+
$tce = $element->addCell($width);
99+
$tce->addText('4');
100+
$table = new \PhpOffice\PhpWord\Writer\RTF\Element\Table($parentWriter, $element);
101+
$expect = implode("\n", array(
102+
'\\pard',
103+
"\\trowd \\cellx$width \\cellx$width2 ",
104+
'\\intbl',
105+
'{\\cf0\\f0 1}\\par',
106+
'\\cell',
107+
'\\intbl',
108+
'{\\cf0\\f0 2}\\par',
109+
'\\cell',
110+
'\\row',
111+
"\\trowd \\cellx$width \\cellx$width2 ",
112+
'\\intbl',
113+
'{\\cf0\\f0 3}\\par',
114+
'\\cell',
115+
'\\intbl',
116+
'{\\cf0\\f0 4}\par',
117+
'\\cell',
118+
'\\row',
119+
'\\pard',
120+
'',
121+
));
122+
123+
$this->assertEquals($expect, $this->removeCr($table));
124+
}
125+
126+
public function testTextRun()
127+
{
128+
$parentWriter = new RTF();
129+
$element = new \PhpOffice\PhpWord\Element\TextRun();
130+
$element->addText('Hello ');
131+
$element->addText('there.');
132+
$textrun = new \PhpOffice\PhpWord\Writer\RTF\Element\TextRun($parentWriter, $element);
133+
$expect = "\\pard\\nowidctlpar {{\\cf0\\f0 Hello }{\\cf0\\f0 there.}}\\par\n";
134+
$this->assertEquals($expect, $this->removeCr($textrun));
135+
}
136+
137+
public function testTextRunParagraphStyle()
138+
{
139+
$parentWriter = new RTF();
140+
$element = new \PhpOffice\PhpWord\Element\TextRun(array('spaceBefore' => 0, 'spaceAfter' => 0));
141+
$element->addText('Hello ');
142+
$element->addText('there.');
143+
$textrun = new \PhpOffice\PhpWord\Writer\RTF\Element\TextRun($parentWriter, $element);
144+
$expect = "\\pard\\nowidctlpar \\sb0\\sa0{{\\cf0\\f0 Hello }{\\cf0\\f0 there.}}\\par\n";
145+
$this->assertEquals($expect, $this->removeCr($textrun));
146+
}
147+
148+
public function testTitle()
149+
{
150+
$parentWriter = new RTF();
151+
$phpWord = new \PhpOffice\PhpWord\PhpWord();
152+
$phpWord->addTitleStyle(1, array(), array('spaceBefore' => 0, 'spaceAfter' => 0));
153+
$section = $phpWord->addSection();
154+
$element = $section->addTitle('First Heading', 1);
155+
$elwrite = new \PhpOffice\PhpWord\Writer\RTF\Element\Title($parentWriter, $element);
156+
$expect = "\\pard\\nowidctlpar \\sb0\\sa0{\\outlinelevel0{\\cf0\\f0 First Heading}\\par\n}";
157+
$this->assertEquals($expect, $this->removeCr($elwrite));
158+
}
83159
}

0 commit comments

Comments
 (0)