Skip to content

Commit a10fe82

Browse files
Owen LeibmanOwen Leibman
authored andcommitted
Errors in RTF Escaping
1. Codes meant to be in hex are specified in decimal. Consequently characters which don't need escaping are escaped. 2. Special handling (prepend backslash) needed for {, }, and \. RTF docs generated with those characters cannot be opened in Word. 3. Tab character needs to be escaped as \tab. RTF docs drop these characters. While running test suite, found that Writer/RTF/ElementTest was coded only for Unix line endings, and fails on Windows. Changed so that it would work on either.
1 parent b8346af commit a10fe82

File tree

3 files changed

+97
-7
lines changed

3 files changed

+97
-7
lines changed

src/PhpWord/Escaper/Rtf.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,22 @@ class Rtf extends AbstractEscaper
2626
{
2727
protected function escapeAsciiCharacter($code)
2828
{
29-
if (20 > $code || $code >= 80) {
30-
return '{\u' . $code . '}';
29+
if ($code == 9) {
30+
return '{\\tab}';
31+
}
32+
if (0x20 > $code || $code >= 0x80) {
33+
return '{\\u' . $code . '}';
34+
}
35+
if ($code == 123 || $code == 125 || $code == 92) { // open or close brace or backslash
36+
return '\\' . chr($code);
3137
}
3238

3339
return chr($code);
3440
}
3541

3642
protected function escapeMultibyteCharacter($code)
3743
{
38-
return '\uc0{\u' . $code . '}';
44+
return '\\uc0{\\u' . $code . '}';
3945
}
4046

4147
/**
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
/**
3+
* This file is part of PHPWord - A pure PHP library for reading and writing
4+
* word processing documents.
5+
*
6+
* PHPWord is free software distributed under the terms of the GNU Lesser
7+
* General Public License version 3 as published by the Free Software Foundation.
8+
*
9+
* For the full copyright and license information, please read the LICENSE
10+
* file that was distributed with this source code. For the full list of
11+
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
12+
*
13+
* @see https://github.com/PHPOffice/PHPWord
14+
* @copyright 2010-2018 PHPWord contributors
15+
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
16+
*/
17+
18+
/**
19+
* Test class for PhpOffice\PhpWord\Writer\RTF\Style subnamespace
20+
*/
21+
class RtfEscaperTest extends \PHPUnit\Framework\TestCase
22+
{
23+
const HEADER = '\\pard\\nowidctlpar {\\cf0\\f0 ';
24+
const TRAILER = '}\\par';
25+
26+
public function escapestring($str)
27+
{
28+
\PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(true);
29+
$parentWriter = new \PhpOffice\PhpWord\Writer\RTF();
30+
$element = new \PhpOffice\PhpWord\Element\Text($str);
31+
$txt = new \PhpOffice\PhpWord\Writer\RTF\Element\Text($parentWriter, $element);
32+
$txt2 = trim($txt->write());
33+
34+
return $txt2;
35+
}
36+
37+
public function expect($str)
38+
{
39+
return self::HEADER . $str . self::TRAILER;
40+
}
41+
42+
/**
43+
* Test special characters which require escaping
44+
*/
45+
public function testSpecial()
46+
{
47+
$str = 'Special characters { open brace } close brace \\ backslash';
48+
$expect = $this->expect('Special characters \\{ open brace \\} close brace \\\\ backslash');
49+
$this->assertEquals($expect, $this->escapestring($str));
50+
}
51+
52+
/**
53+
* Test accented character
54+
*/
55+
public function testAccent()
56+
{
57+
$str = 'Voilà - string with accented char';
58+
$expect = $this->expect('Voil\\uc0{\\u224} - string with accented char');
59+
$this->assertEquals($expect, $this->escapestring($str));
60+
}
61+
62+
/**
63+
* Test Hebrew
64+
*/
65+
public function testHebrew()
66+
{
67+
$str = 'Hebrew - שלום';
68+
$expect = $this->expect('Hebrew - \\uc0{\\u1513}\\uc0{\\u1500}\\uc0{\\u1493}\\uc0{\\u1501}');
69+
$this->assertEquals($expect, $this->escapestring($str));
70+
}
71+
72+
/**
73+
* Test tab
74+
*/
75+
public function testTab()
76+
{
77+
$str = "Here's a tab\tfollowed by more characters.";
78+
$expect = $this->expect("Here's a tab{\\tab}followed by more characters.");
79+
$this->assertEquals($expect, $this->escapestring($str));
80+
}
81+
}

tests/PhpWord/Writer/RTF/ElementTest.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
*/
2525
class ElementTest extends \PHPUnit\Framework\TestCase
2626
{
27+
public function removeCr($field) {
28+
return str_replace("\r\n", "\n", $field->write());
29+
}
2730
/**
2831
* Test unmatched elements
2932
*/
@@ -46,7 +49,7 @@ public function testPageField()
4649
$element = new \PhpOffice\PhpWord\Element\Field('PAGE');
4750
$field = new \PhpOffice\PhpWord\Writer\RTF\Element\Field($parentWriter, $element);
4851

49-
$this->assertEquals("{\\field{\\*\\fldinst PAGE}{\\fldrslt}}\\par\n", $field->write());
52+
$this->assertEquals("{\\field{\\*\\fldinst PAGE}{\\fldrslt}}\\par\n", $this->removeCr($field));
5053
}
5154

5255
public function testNumpageField()
@@ -55,7 +58,7 @@ public function testNumpageField()
5558
$element = new \PhpOffice\PhpWord\Element\Field('NUMPAGES');
5659
$field = new \PhpOffice\PhpWord\Writer\RTF\Element\Field($parentWriter, $element);
5760

58-
$this->assertEquals("{\\field{\\*\\fldinst NUMPAGES}{\\fldrslt}}\\par\n", $field->write());
61+
$this->assertEquals("{\\field{\\*\\fldinst NUMPAGES}{\\fldrslt}}\\par\n", $this->removeCr($field));
5962
}
6063

6164
public function testDateField()
@@ -64,7 +67,7 @@ public function testDateField()
6467
$element = new \PhpOffice\PhpWord\Element\Field('DATE', array('dateformat' => 'd MM yyyy H:mm:ss'));
6568
$field = new \PhpOffice\PhpWord\Writer\RTF\Element\Field($parentWriter, $element);
6669

67-
$this->assertEquals("{\\field{\\*\\fldinst DATE \\\\@ \"d MM yyyy H:mm:ss\"}{\\fldrslt}}\\par\n", $field->write());
70+
$this->assertEquals("{\\field{\\*\\fldinst DATE \\\\@ \"d MM yyyy H:mm:ss\"}{\\fldrslt}}\\par\n", $this->removeCr($field));
6871
}
6972

7073
public function testIndexField()
@@ -73,6 +76,6 @@ public function testIndexField()
7376
$element = new \PhpOffice\PhpWord\Element\Field('INDEX');
7477
$field = new \PhpOffice\PhpWord\Writer\RTF\Element\Field($parentWriter, $element);
7578

76-
$this->assertEquals("{}\\par\n", $field->write());
79+
$this->assertEquals("{}\\par\n", $this->removeCr($field));
7780
}
7881
}

0 commit comments

Comments
 (0)