Skip to content

Commit 3f1e0ac

Browse files
author
Roman Syroeshko
committed
#483. Output escaping for RTF.
1 parent 649da97 commit 3f1e0ac

File tree

11 files changed

+172
-31
lines changed

11 files changed

+172
-31
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Place announcement text here.
1313
- Introduced writer for the "Paragraph Alignment" element (see `\PhpOffice\PhpWord\Writer\Word2007\Element\ParagraphAlignment`). - @RomanSyroeshko
1414
- Introduced writer for the "Table Alignment" element (see `\PhpOffice\PhpWord\Writer\Word2007\Element\TableAlignment`). - @RomanSyroeshko
1515
- Supported indexed arrays in arguments of `TemplateProcessor::setValue()`. - @RomanSyroeshko #618
16+
- Introduced automatic output escaping for OOXML, ODF, HTML, and RTF. To turn the feature on use `phpword.ini` or `\PhpOffice\PhpWord\Settings`. - @RomanSyroeshko #483
1617

1718
### Changed
1819
- Improved error message for the case when `autoload.php` is not found. - @RomanSyroeshko #371

src/PhpWord/Escaper/AbstractEscaper.php

+7-7
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,22 @@
2525
abstract class AbstractEscaper implements EscaperInterface
2626
{
2727
/**
28-
* @param string $subject
28+
* @param string $input
2929
*
3030
* @return string
3131
*/
32-
abstract protected function escapeSingleValue($subject);
32+
abstract protected function escapeSingleValue($input);
3333

34-
public function escape($subject)
34+
public function escape($input)
3535
{
36-
if (is_array($subject)) {
37-
foreach ($subject as &$item) {
36+
if (is_array($input)) {
37+
foreach ($input as &$item) {
3838
$item = $this->escapeSingleValue($item);
3939
}
4040
} else {
41-
$subject = $this->escapeSingleValue($subject);
41+
$input = $this->escapeSingleValue($input);
4242
}
4343

44-
return $subject;
44+
return $input;
4545
}
4646
}

src/PhpWord/Escaper/EscaperInterface.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
interface EscaperInterface
2626
{
2727
/**
28-
* @param mixed $subject
28+
* @param mixed $input
2929
*
3030
* @return mixed
3131
*/
32-
public function escape($subject);
32+
public function escape($input);
3333
}

src/PhpWord/Escaper/RegExp.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ class RegExp extends AbstractEscaper
2626
{
2727
const REG_EXP_DELIMITER = '/';
2828

29-
protected function escapeSingleValue($subject)
29+
protected function escapeSingleValue($input)
3030
{
31-
return self::REG_EXP_DELIMITER . preg_quote($subject, self::REG_EXP_DELIMITER) . self::REG_EXP_DELIMITER . 'u';
31+
return self::REG_EXP_DELIMITER . preg_quote($input, self::REG_EXP_DELIMITER) . self::REG_EXP_DELIMITER . 'u';
3232
}
3333
}

src/PhpWord/Escaper/Rtf.php

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
* @link https://github.com/PHPOffice/PHPWord
14+
* @copyright 2010-2016 PHPWord contributors
15+
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
16+
*/
17+
18+
namespace PhpOffice\PhpWord\Escaper;
19+
20+
/**
21+
* @since 0.13.0
22+
*
23+
* @codeCoverageIgnore
24+
*/
25+
class Rtf extends AbstractEscaper
26+
{
27+
protected function escapeAsciiCharacter($code) {
28+
if (20 > $code || $code >= 80) {
29+
return '{\u' . $code . '}';
30+
} else {
31+
return chr($code);
32+
}
33+
}
34+
35+
protected function escapeMultibyteCharacter($code) {
36+
return '\uc0{\u' . $code . '}';
37+
}
38+
39+
/**
40+
* @see http://www.randomchaos.com/documents/?source=php_and_unicode
41+
*/
42+
protected function escapeSingleValue($input)
43+
{
44+
$escapedValue = '';
45+
46+
$numberOfBytes = 1;
47+
$bytes = array();
48+
for ($i = 0; $i < strlen($input); ++$i) {
49+
$character = $input[$i];
50+
$asciiCode = ord($character);
51+
52+
if ($asciiCode < 128) {
53+
$escapedValue .= $this->escapeAsciiCharacter($asciiCode);
54+
} else {
55+
if (0 == count($bytes)) {
56+
if ($asciiCode < 224) {
57+
$numberOfBytes = 2;
58+
} else if ($asciiCode < 240) {
59+
$numberOfBytes = 3;
60+
} else if ($asciiCode < 248) {
61+
$numberOfBytes = 4;
62+
}
63+
}
64+
65+
$bytes[] = $asciiCode;
66+
67+
if ($numberOfBytes == count($bytes)) {
68+
if (4 == $numberOfBytes) {
69+
$multibyteCode = ($bytes[0] % 8) * 262144 + ($bytes[1] % 64) * 4096 + ($bytes[2] % 64) * 64 + ($bytes[3] % 64);
70+
} elseif (3 == $numberOfBytes) {
71+
$multibyteCode = ($bytes[0] % 16) * 4096 + ($bytes[1] % 64) * 64 + ($bytes[2] % 64);
72+
} else {
73+
$multibyteCode = ($bytes[0] % 32) * 64 + ($bytes[1] % 64);
74+
}
75+
76+
if (65279 != $multibyteCode) {
77+
$escapedValue .= $multibyteCode < 128 ? $this->escapeAsciiCharacter($multibyteCode) : $this->escapeMultibyteCharacter($multibyteCode);
78+
}
79+
80+
$numberOfBytes = 1;
81+
$bytes = array();
82+
}
83+
}
84+
}
85+
86+
return $escapedValue;
87+
}
88+
}

src/PhpWord/Escaper/Xml.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
*/
2525
class Xml extends AbstractEscaper
2626
{
27-
protected function escapeSingleValue($subject)
27+
protected function escapeSingleValue($input)
2828
{
2929
// todo: omit encoding parameter after migration onto PHP 5.4
30-
return htmlspecialchars($subject, ENT_QUOTES, 'UTF-8');
30+
return htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
3131
}
3232
}

src/PhpWord/Writer/HTML/Part/AbstractPart.php

+1-10
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,11 @@
2222
use Zend\Escaper\Escaper;
2323

2424
/**
25-
* Abstract HTML part writer
26-
*
2725
* @since 0.11.0
2826
*/
2927
abstract class AbstractPart
3028
{
3129
/**
32-
* Parent writer
33-
*
3430
* @var \PhpOffice\PhpWord\Writer\AbstractWriter
3531
*/
3632
private $parentWriter;
@@ -46,16 +42,13 @@ public function __construct()
4642
}
4743

4844
/**
49-
* Write part
50-
*
5145
* @return string
5246
*/
5347
abstract public function write();
5448

5549
/**
56-
* Set parent writer.
57-
*
5850
* @param \PhpOffice\PhpWord\Writer\AbstractWriter $writer
51+
*
5952
* @return void
6053
*/
6154
public function setParentWriter(AbstractWriter $writer = null)
@@ -64,8 +57,6 @@ public function setParentWriter(AbstractWriter $writer = null)
6457
}
6558

6659
/**
67-
* Get parent writer
68-
*
6960
* @return \PhpOffice\PhpWord\Writer\AbstractWriter
7061
*
7162
* @throws \PhpOffice\PhpWord\Exception\Exception

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

+16-1
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@
1818
namespace PhpOffice\PhpWord\Writer\RTF\Element;
1919

2020
use PhpOffice\Common\Text as CommonText;
21+
use PhpOffice\PhpWord\Element\AbstractElement as Element;
22+
use PhpOffice\PhpWord\Escaper\Rtf;
23+
use PhpOffice\PhpWord\Settings;
2124
use PhpOffice\PhpWord\Style;
2225
use PhpOffice\PhpWord\Style\Font as FontStyle;
2326
use PhpOffice\PhpWord\Style\Paragraph as ParagraphStyle;
27+
use PhpOffice\PhpWord\Writer\AbstractWriter;
2428
use PhpOffice\PhpWord\Writer\HTML\Element\AbstractElement as HTMLAbstractElement;
2529
use PhpOffice\PhpWord\Writer\RTF\Style\Font as FontStyleWriter;
2630
use PhpOffice\PhpWord\Writer\RTF\Style\Paragraph as ParagraphStyleWriter;
@@ -46,6 +50,13 @@ abstract class AbstractElement extends HTMLAbstractElement
4650
*/
4751
private $paragraphStyle;
4852

53+
public function __construct(AbstractWriter $parentWriter, Element $element, $withoutP)
54+
{
55+
parent::__construct($parentWriter, $element, $withoutP);
56+
57+
$this->escaper = new Rtf();
58+
}
59+
4960
/**
5061
* Get font and paragraph styles.
5162
*
@@ -112,7 +123,11 @@ protected function writeOpening()
112123
*/
113124
protected function writeText($text)
114125
{
115-
return CommonText::toUnicode($text);
126+
if (Settings::isOutputEscapingEnabled()) {
127+
return $this->escaper->escape($text);
128+
} else {
129+
return CommonText::toUnicode($text);
130+
}
116131
}
117132

118133
/**

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

+47-4
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,56 @@
1717

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

20-
use PhpOffice\PhpWord\Writer\HTML\Part\AbstractPart as HTMLAbstractPart;
20+
use PhpOffice\PhpWord\Escaper\Rtf;
21+
use PhpOffice\PhpWord\Exception\Exception;
22+
use PhpOffice\PhpWord\Writer\AbstractWriter;
2123

2224
/**
23-
* Abstract RTF part writer
24-
*
2525
* @since 0.11.0
2626
*/
27-
abstract class AbstractPart extends HTMLAbstractPart
27+
abstract class AbstractPart
2828
{
29+
/**
30+
* @var \PhpOffice\PhpWord\Writer\AbstractWriter
31+
*/
32+
private $parentWriter;
33+
34+
/**
35+
* @var \PhpOffice\PhpWord\Escaper\EscaperInterface
36+
*/
37+
protected $escaper;
38+
39+
public function __construct()
40+
{
41+
$this->escaper = new Rtf();
42+
}
43+
44+
/**
45+
* @return string
46+
*/
47+
abstract public function write();
48+
49+
/**
50+
* @param \PhpOffice\PhpWord\Writer\AbstractWriter $writer
51+
*
52+
* @return void
53+
*/
54+
public function setParentWriter(AbstractWriter $writer = null)
55+
{
56+
$this->parentWriter = $writer;
57+
}
58+
59+
/**
60+
* @return \PhpOffice\PhpWord\Writer\AbstractWriter
61+
*
62+
* @throws \PhpOffice\PhpWord\Exception\Exception
63+
*/
64+
public function getParentWriter()
65+
{
66+
if ($this->parentWriter !== null) {
67+
return $this->parentWriter;
68+
} else {
69+
throw new Exception('No parent WriterInterface assigned.');
70+
}
71+
}
2972
}

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,11 @@ private function writeInfo()
6565
$content .= '\info';
6666
foreach ($properties as $property) {
6767
$method = 'get' . (isset($mapping[$property]) ? $mapping[$property] : $property);
68-
$value = $docProps->$method();
68+
if (!in_array($property, $dateFields) && Settings::isOutputEscapingEnabled()) {
69+
$value = $this->escaper->escape($docProps->$method());
70+
} else {
71+
$value = $docProps->$method();
72+
}
6973
$value = in_array($property, $dateFields) ? $this->getDateValue($value) : $value;
7074
$content .= "{\\{$property} {$value}}";
7175
}
@@ -105,7 +109,6 @@ private function writeFormatting()
105109
*/
106110
private function writeSections()
107111
{
108-
109112
$content = '';
110113

111114
$sections = $this->getParentWriter()->getPhpWord()->getSections();

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ private function writeGenerator()
173173
{
174174
$content = '';
175175

176-
$content .= '{\*\generator PhpWord;}'; // Set the generator
176+
$content .= '{\*\generator PHPWord;}'; // Set the generator
177177
$content .= PHP_EOL;
178178

179179
return $content;

0 commit comments

Comments
 (0)