Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Roman Syroeshko committed Apr 23, 2016
1 parent 1c5bb3a commit d579736
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 31 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Place announcement text here.
- Introduced the `\PhpOffice\PhpWord\SimpleType\JcTable` simple type. - @RomanSyroeshko
- 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

### Changed
- Improved error message for the case when `autoload.php` is not found. - @RomanSyroeshko #371
Expand Down Expand Up @@ -39,7 +40,7 @@ so installation via ZIP-archive download is not an option anymore. To install PH

### Fixed
- `Undefined property` error while reading MS-DOC documents. - @jaberu #610
- Corrupted OOXML template issue in case when its macro is broken immediately after `$` sign.
- Corrupted OOXML template issue in case when its names is broken immediately after `$` sign.
That case wasn't taken into account in implementation of `TemplateProcessor::fixBrokenMacros()`. - @RomanSyroeshko @d-damien #548


Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
"require": {
"php": ">=5.3.3",
"ext-xml": "*",
"zendframework/zend-validator": "2.5.*",
"zendframework/zend-stdlib": "~2.5",
"zendframework/zend-validator": "2.5.*",
"phpoffice/common": "0.2.*"
},
"require-dev": {
Expand All @@ -47,8 +47,8 @@
"dompdf/dompdf":"0.6.*",
"tecnickcom/tcpdf": "6.*",
"mpdf/mpdf": "5.*",
"zendframework/zend-validator": "2.5.*",
"zendframework/zend-stdlib": "~2.5",
"zendframework/zend-validator": "2.5.*",
"phpoffice/common": "0.2.*"
},
"suggest": {
Expand Down
6 changes: 3 additions & 3 deletions docs/templates-processing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Templates processing
====================

You can create a .docx document template with included search-patterns which can be replaced by any value you wish. Only single-line values can be replaced.
You can create an OOXML document template with included search-patterns (macros) which can be replaced by any value you wish. Only single-line values can be replaced.

To deal with a template file, use ``new TemplateProcessor`` statement. After TemplateProcessor instance creation the document template is copied into the temporary directory. Then you can use ``TemplateProcessor::setValue`` method to change the value of a search pattern. The search-pattern model is: ``${search-pattern}``.

Expand All @@ -12,8 +12,8 @@ Example:
.. code-block:: php
$templateProcessor = new TemplateProcessor('Template.docx');
$templateProcessor->setValue('Name', 'Somebody someone');
$templateProcessor->setValue('Street', 'Coming-Undone-Street 32');
$templateProcessor->setValue('Name', 'John Doe');
$templateProcessor->setValue(array('City', 'Street'), array('Detroit', '12th Street'));
It is not possible to directly add new OOXML elements to the template file being processed, but it is possible to transform main document part of the template using XSLT (see ``TemplateProcessor::applyXslStyleSheet``).

Expand Down
31 changes: 31 additions & 0 deletions src/PhpWord/Escaper/EscaperInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?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:https://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/

namespace PhpOffice\PhpWord\Escaper;

/**
* @since 0.13.0
*/
interface EscaperInterface
{
/**
* @param mixed $subject
*
* @return mixed
*/
public static function escape($subject);
}
49 changes: 49 additions & 0 deletions src/PhpWord/Escaper/RegExp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?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:https://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/

namespace PhpOffice\PhpWord\Escaper;

/**
* @since 0.13.0
*/
class RegExp implements EscaperInterface
{
const REG_EXP_DELIMITER = '/';

/**
* @param string $subject
*
* @return string
*/
protected static function escapeSingleItem($subject)
{
return self::REG_EXP_DELIMITER . preg_quote($subject, self::REG_EXP_DELIMITER) . self::REG_EXP_DELIMITER . 'u';
}

public static function escape($subject)
{
if (is_array($subject)) {
foreach ($subject as &$item) {
$item = self::escapeSingleItem($item);
}
} else {
$subject = self::escapeSingleItem($subject);
}

return $subject;
}
}
67 changes: 48 additions & 19 deletions src/PhpWord/TemplateProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

namespace PhpOffice\PhpWord;

use PhpOffice\PhpWord\Escaper\RegExp;
use PhpOffice\PhpWord\Exception\CopyFileException;
use PhpOffice\PhpWord\Exception\CreateTemporaryFileException;
use PhpOffice\PhpWord\Exception\Exception;
Expand Down Expand Up @@ -136,31 +137,61 @@ public function applyXslStyleSheet($xslDOMDocument, $xslOptions = array(), $xslO
}

/**
* @param mixed $macro
* @param mixed $replace
* @param integer $limit
* @param string $macro
*
* @return void
* @return string
*/
public function setValue($macro, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT)
protected static function ensureMacroCompleted($macro)
{
if (substr($macro, 0, 2) !== '${' && substr($macro, -1) !== '}') {
$macro = '${' . $macro . '}';
}

if (!StringUtils::isValidUtf8($replace)) {
$replace = utf8_encode($replace);
}
return $macro;
}

foreach ($this->tempDocumentHeaders as $index => $headerXML) {
$this->tempDocumentHeaders[$index] = $this->setValueForPart($this->tempDocumentHeaders[$index], $macro, $replace, $limit);
/**
* @param string $subject
*
* @return string
*/
protected static function ensureUtf8Encoded($subject)
{
if (!StringUtils::isValidUtf8($subject)) {
$subject = utf8_encode($subject);
}

$this->tempDocumentMainPart = $this->setValueForPart($this->tempDocumentMainPart, $macro, $replace, $limit);
return $subject;
}

foreach ($this->tempDocumentFooters as $index => $headerXML) {
$this->tempDocumentFooters[$index] = $this->setValueForPart($this->tempDocumentFooters[$index], $macro, $replace, $limit);
/**
* @param mixed $search
* @param mixed $replace
* @param integer $limit
*
* @return void
*/
public function setValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT)
{
if (is_array($search)) {
foreach ($search as &$item) {
$item = self::ensureMacroCompleted($item);
}
} else {
$search = self::ensureMacroCompleted($search);
}

if (is_array($replace)) {
foreach ($replace as &$item) {
$item = self::ensureUtf8Encoded($item);
}
} else {
$replace = self::ensureUtf8Encoded($replace);
}

$this->tempDocumentHeaders = $this->setValueForPart($search, $replace, $this->tempDocumentHeaders, $limit);
$this->tempDocumentMainPart = $this->setValueForPart($search, $replace, $this->tempDocumentMainPart, $limit);
$this->tempDocumentFooters = $this->setValueForPart($search, $replace, $this->tempDocumentFooters, $limit);
}

/**
Expand Down Expand Up @@ -398,22 +429,20 @@ function ($match) {
/**
* Find and replace macros in the given XML section.
*
* @param mixed $search
* @param mixed $replace
* @param string $documentPartXML
* @param string $search
* @param string $replace
* @param integer $limit
*
* @return string
*/
protected function setValueForPart($documentPartXML, $search, $replace, $limit)
protected function setValueForPart($search, $replace, $documentPartXML, $limit)
{
// Note: we can't use the same function for both cases here, because of performance considerations.
if (self::MAXIMUM_REPLACEMENTS_DEFAULT === $limit) {
return str_replace($search, $replace, $documentPartXML);
} else {
$regExpDelim = '/';
$escapedSearch = preg_quote($search, $regExpDelim);
return preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $documentPartXML, $limit);
return preg_replace(RegExp::escape($search), $replace, $documentPartXML, $limit);
}
}

Expand Down
14 changes: 8 additions & 6 deletions tests/PhpWord/TemplateProcessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,17 @@ public function testMacrosCanBeReplacedInHeaderAndFooter()
{
$templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/header-footer.docx');

$this->assertEquals(
array('documentContent', 'headerValue', 'footerValue'),
$templateProcessor->getVariables()
$this->assertEquals(array('documentContent', 'headerValue', 'footerValue'), $templateProcessor->getVariables());

$macroNames = array('headerValue', 'documentContent', 'footerValue');
$macroValues = array(
htmlspecialchars('Header Value', ENT_COMPAT, 'UTF-8'),
htmlspecialchars('Document text.', ENT_COMPAT, 'UTF-8'),
htmlspecialchars('Footer Value', ENT_COMPAT, 'UTF-8')
);
$templateProcessor->setValue($macroNames, $macroValues);

$docName = 'header-footer-test-result.docx';
$templateProcessor->setValue('headerValue', htmlspecialchars('Header Value', ENT_COMPAT, 'UTF-8'));
$templateProcessor->setValue('documentContent', htmlspecialchars('Document text.', ENT_COMPAT, 'UTF-8'));
$templateProcessor->setValue('footerValue', htmlspecialchars('Footer Value', ENT_COMPAT, 'UTF-8'));
$templateProcessor->saveAs($docName);
$docFound = file_exists($docName);
unlink($docName);
Expand Down

0 comments on commit d579736

Please sign in to comment.