#483. Output escaping for RTF.

This commit is contained in:
Roman Syroeshko 2016-07-08 22:56:59 +04:00
parent 649da97a42
commit 3f1e0ac4a7
11 changed files with 172 additions and 31 deletions

View File

@ -13,6 +13,7 @@ Place announcement text here.
- Introduced writer for the "Paragraph Alignment" element (see `\PhpOffice\PhpWord\Writer\Word2007\Element\ParagraphAlignment`). - @RomanSyroeshko
- Introduced writer for the "Table Alignment" element (see `\PhpOffice\PhpWord\Writer\Word2007\Element\TableAlignment`). - @RomanSyroeshko
- Supported indexed arrays in arguments of `TemplateProcessor::setValue()`. - @RomanSyroeshko #618
- Introduced automatic output escaping for OOXML, ODF, HTML, and RTF. To turn the feature on use `phpword.ini` or `\PhpOffice\PhpWord\Settings`. - @RomanSyroeshko #483
### Changed
- Improved error message for the case when `autoload.php` is not found. - @RomanSyroeshko #371

View File

@ -25,22 +25,22 @@ namespace PhpOffice\PhpWord\Escaper;
abstract class AbstractEscaper implements EscaperInterface
{
/**
* @param string $subject
* @param string $input
*
* @return string
*/
abstract protected function escapeSingleValue($subject);
abstract protected function escapeSingleValue($input);
public function escape($subject)
public function escape($input)
{
if (is_array($subject)) {
foreach ($subject as &$item) {
if (is_array($input)) {
foreach ($input as &$item) {
$item = $this->escapeSingleValue($item);
}
} else {
$subject = $this->escapeSingleValue($subject);
$input = $this->escapeSingleValue($input);
}
return $subject;
return $input;
}
}

View File

@ -25,9 +25,9 @@ namespace PhpOffice\PhpWord\Escaper;
interface EscaperInterface
{
/**
* @param mixed $subject
* @param mixed $input
*
* @return mixed
*/
public function escape($subject);
public function escape($input);
}

View File

@ -26,8 +26,8 @@ class RegExp extends AbstractEscaper
{
const REG_EXP_DELIMITER = '/';
protected function escapeSingleValue($subject)
protected function escapeSingleValue($input)
{
return self::REG_EXP_DELIMITER . preg_quote($subject, self::REG_EXP_DELIMITER) . self::REG_EXP_DELIMITER . 'u';
return self::REG_EXP_DELIMITER . preg_quote($input, self::REG_EXP_DELIMITER) . self::REG_EXP_DELIMITER . 'u';
}
}

View File

@ -0,0 +1,88 @@
<?php
/**
* This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
*
* @link https://github.com/PHPOffice/PHPWord
* @copyright 2010-2016 PHPWord contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
namespace PhpOffice\PhpWord\Escaper;
/**
* @since 0.13.0
*
* @codeCoverageIgnore
*/
class Rtf extends AbstractEscaper
{
protected function escapeAsciiCharacter($code) {
if (20 > $code || $code >= 80) {
return '{\u' . $code . '}';
} else {
return chr($code);
}
}
protected function escapeMultibyteCharacter($code) {
return '\uc0{\u' . $code . '}';
}
/**
* @see http://www.randomchaos.com/documents/?source=php_and_unicode
*/
protected function escapeSingleValue($input)
{
$escapedValue = '';
$numberOfBytes = 1;
$bytes = array();
for ($i = 0; $i < strlen($input); ++$i) {
$character = $input[$i];
$asciiCode = ord($character);
if ($asciiCode < 128) {
$escapedValue .= $this->escapeAsciiCharacter($asciiCode);
} else {
if (0 == count($bytes)) {
if ($asciiCode < 224) {
$numberOfBytes = 2;
} else if ($asciiCode < 240) {
$numberOfBytes = 3;
} else if ($asciiCode < 248) {
$numberOfBytes = 4;
}
}
$bytes[] = $asciiCode;
if ($numberOfBytes == count($bytes)) {
if (4 == $numberOfBytes) {
$multibyteCode = ($bytes[0] % 8) * 262144 + ($bytes[1] % 64) * 4096 + ($bytes[2] % 64) * 64 + ($bytes[3] % 64);
} elseif (3 == $numberOfBytes) {
$multibyteCode = ($bytes[0] % 16) * 4096 + ($bytes[1] % 64) * 64 + ($bytes[2] % 64);
} else {
$multibyteCode = ($bytes[0] % 32) * 64 + ($bytes[1] % 64);
}
if (65279 != $multibyteCode) {
$escapedValue .= $multibyteCode < 128 ? $this->escapeAsciiCharacter($multibyteCode) : $this->escapeMultibyteCharacter($multibyteCode);
}
$numberOfBytes = 1;
$bytes = array();
}
}
}
return $escapedValue;
}
}

View File

@ -24,9 +24,9 @@ namespace PhpOffice\PhpWord\Escaper;
*/
class Xml extends AbstractEscaper
{
protected function escapeSingleValue($subject)
protected function escapeSingleValue($input)
{
// todo: omit encoding parameter after migration onto PHP 5.4
return htmlspecialchars($subject, ENT_QUOTES, 'UTF-8');
return htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
}
}

View File

@ -22,15 +22,11 @@ use PhpOffice\PhpWord\Writer\AbstractWriter;
use Zend\Escaper\Escaper;
/**
* Abstract HTML part writer
*
* @since 0.11.0
*/
abstract class AbstractPart
{
/**
* Parent writer
*
* @var \PhpOffice\PhpWord\Writer\AbstractWriter
*/
private $parentWriter;
@ -46,16 +42,13 @@ abstract class AbstractPart
}
/**
* Write part
*
* @return string
*/
abstract public function write();
/**
* Set parent writer.
*
* @param \PhpOffice\PhpWord\Writer\AbstractWriter $writer
*
* @return void
*/
public function setParentWriter(AbstractWriter $writer = null)
@ -64,8 +57,6 @@ abstract class AbstractPart
}
/**
* Get parent writer
*
* @return \PhpOffice\PhpWord\Writer\AbstractWriter
*
* @throws \PhpOffice\PhpWord\Exception\Exception

View File

@ -18,9 +18,13 @@
namespace PhpOffice\PhpWord\Writer\RTF\Element;
use PhpOffice\Common\Text as CommonText;
use PhpOffice\PhpWord\Element\AbstractElement as Element;
use PhpOffice\PhpWord\Escaper\Rtf;
use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Style\Font as FontStyle;
use PhpOffice\PhpWord\Style\Paragraph as ParagraphStyle;
use PhpOffice\PhpWord\Writer\AbstractWriter;
use PhpOffice\PhpWord\Writer\HTML\Element\AbstractElement as HTMLAbstractElement;
use PhpOffice\PhpWord\Writer\RTF\Style\Font as FontStyleWriter;
use PhpOffice\PhpWord\Writer\RTF\Style\Paragraph as ParagraphStyleWriter;
@ -46,6 +50,13 @@ abstract class AbstractElement extends HTMLAbstractElement
*/
private $paragraphStyle;
public function __construct(AbstractWriter $parentWriter, Element $element, $withoutP)
{
parent::__construct($parentWriter, $element, $withoutP);
$this->escaper = new Rtf();
}
/**
* Get font and paragraph styles.
*
@ -112,7 +123,11 @@ abstract class AbstractElement extends HTMLAbstractElement
*/
protected function writeText($text)
{
return CommonText::toUnicode($text);
if (Settings::isOutputEscapingEnabled()) {
return $this->escaper->escape($text);
} else {
return CommonText::toUnicode($text);
}
}
/**

View File

@ -17,13 +17,56 @@
namespace PhpOffice\PhpWord\Writer\RTF\Part;
use PhpOffice\PhpWord\Writer\HTML\Part\AbstractPart as HTMLAbstractPart;
use PhpOffice\PhpWord\Escaper\Rtf;
use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\Writer\AbstractWriter;
/**
* Abstract RTF part writer
*
* @since 0.11.0
*/
abstract class AbstractPart extends HTMLAbstractPart
abstract class AbstractPart
{
/**
* @var \PhpOffice\PhpWord\Writer\AbstractWriter
*/
private $parentWriter;
/**
* @var \PhpOffice\PhpWord\Escaper\EscaperInterface
*/
protected $escaper;
public function __construct()
{
$this->escaper = new Rtf();
}
/**
* @return string
*/
abstract public function write();
/**
* @param \PhpOffice\PhpWord\Writer\AbstractWriter $writer
*
* @return void
*/
public function setParentWriter(AbstractWriter $writer = null)
{
$this->parentWriter = $writer;
}
/**
* @return \PhpOffice\PhpWord\Writer\AbstractWriter
*
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function getParentWriter()
{
if ($this->parentWriter !== null) {
return $this->parentWriter;
} else {
throw new Exception('No parent WriterInterface assigned.');
}
}
}

View File

@ -65,7 +65,11 @@ class Document extends AbstractPart
$content .= '\info';
foreach ($properties as $property) {
$method = 'get' . (isset($mapping[$property]) ? $mapping[$property] : $property);
$value = $docProps->$method();
if (!in_array($property, $dateFields) && Settings::isOutputEscapingEnabled()) {
$value = $this->escaper->escape($docProps->$method());
} else {
$value = $docProps->$method();
}
$value = in_array($property, $dateFields) ? $this->getDateValue($value) : $value;
$content .= "{\\{$property} {$value}}";
}
@ -105,7 +109,6 @@ class Document extends AbstractPart
*/
private function writeSections()
{
$content = '';
$sections = $this->getParentWriter()->getPhpWord()->getSections();

View File

@ -173,7 +173,7 @@ class Header extends AbstractPart
{
$content = '';
$content .= '{\*\generator PhpWord;}'; // Set the generator
$content .= '{\*\generator PHPWord;}'; // Set the generator
$content .= PHP_EOL;
return $content;