Merge branch 'develop' of https://github.com/PHPOffice/PHPWord into develop

This commit is contained in:
Progi1984 2014-08-16 13:25:58 +02:00
commit 8da62eb7b2
13 changed files with 622 additions and 598 deletions

View File

@ -40,6 +40,8 @@ This release added form fields (textinput, checkbox, and dropdown), drawing shap
- `Element\Section::getSettings()` and `Element\Section::setSettings()` replaced by `Element\Section::getStyle()` and `Element\Section::setStyle()` - `Element\Section::getSettings()` and `Element\Section::setSettings()` replaced by `Element\Section::getStyle()` and `Element\Section::setStyle()`
- `Shared\Drawing` and `Shared\Font` merged into `Shared\Converter` - `Shared\Drawing` and `Shared\Font` merged into `Shared\Converter`
- `DocumentProperties` replaced by `Metadata\DocInfo` - `DocumentProperties` replaced by `Metadata\DocInfo`
- `Template` replaced by `TemplateProcessor`
- `PhpWord->loadTemplate($filename)`
### Miscellaneous ### Miscellaneous
@ -50,6 +52,7 @@ This release added form fields (textinput, checkbox, and dropdown), drawing shap
- Element: Refactor elements to move set relation Id from container to element - @ivanlanin - Element: Refactor elements to move set relation Id from container to element - @ivanlanin
- Introduced CreateTemporaryFileException, CopyFileException - @RomanSyroeshko - Introduced CreateTemporaryFileException, CopyFileException - @RomanSyroeshko
- Settings: added method to set user defined temporary directory - @RomanSyroeshko GH-310 - Settings: added method to set user defined temporary directory - @RomanSyroeshko GH-310
- Renamed `Template` into `TemplateProcessor` - @RomanSyroeshko GH-216
## 0.11.1 - 2 June 2014 ## 0.11.1 - 2 June 2014

View File

@ -2,7 +2,7 @@
"name": "phpoffice/phpword", "name": "phpoffice/phpword",
"description": "PHPWord - A pure PHP library for reading and writing word processing documents (DOCX, ODT, RTF, HTML, PDF)", "description": "PHPWord - A pure PHP library for reading and writing word processing documents (DOCX, ODT, RTF, HTML, PDF)",
"keywords": [ "keywords": [
"PHP", "PhpOffice", "office", "PhpWord", "word", "template", "reader", "writer", "PHP", "PhpOffice", "office", "PhpWord", "word", "template", "template processor", "reader", "writer",
"docx", "OOXML", "OpenXML", "Office Open XML", "ISO IEC 29500", "WordprocessingML", "docx", "OOXML", "OpenXML", "Office Open XML", "ISO IEC 29500", "WordprocessingML",
"RTF", "Rich Text Format", "doc", "odt", "OpenDocument", "PDF", "HTML" "RTF", "Rich Text Format", "doc", "odt", "OpenDocument", "PDF", "HTML"
], ],
@ -50,7 +50,7 @@
"ext-zip": "Used to write DOCX and ODT", "ext-zip": "Used to write DOCX and ODT",
"ext-gd2": "Used to add images", "ext-gd2": "Used to add images",
"ext-xmlwriter": "Used to write DOCX and ODT", "ext-xmlwriter": "Used to write DOCX and ODT",
"ext-xsl": "Used to apply XSL style sheet to template part", "ext-xsl": "Used to apply XSL style sheet to main document part of OOXML template",
"dompdf/dompdf": "Used to write PDF" "dompdf/dompdf": "Used to write PDF"
}, },
"autoload": { "autoload": {

View File

@ -23,7 +23,7 @@ Format (RTF).
containers containers
elements elements
styles styles
templates templates-processing
writersreaders writersreaders
recipes recipes
faq faq

View File

@ -44,7 +44,7 @@ Don't forget to change `code::` directive to `code-block::` in the resulting rst
- [Font](#font) - [Font](#font)
- [Paragraph](#paragraph) - [Paragraph](#paragraph)
- [Table](#table) - [Table](#table)
- [Templates](#templates) - [Templates processing](#templates-processing)
- [Writers & readers](#writers-readers) - [Writers & readers](#writers-readers)
- [OOXML](#ooxml) - [OOXML](#ooxml)
- [OpenDocument](#opendocument) - [OpenDocument](#opendocument)
@ -873,21 +873,25 @@ Available image styles:
- `font` Font name - `font` Font name
- `hint` See font style - `hint` See font style
# Templates # Templates processing
You can create a docx template with included search-patterns that can be replaced by any value you wish. Only single-line values can be replaced. To load a template file, use the `loadTemplate` method. After loading the docx template, you can use the `setValue` method to change the value of a search pattern. The search-pattern model is: `${search-pattern}`. It is not possible to add new PHPWord elements to a loaded template file. 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.
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}`.
Example: Example:
```php ```php
$template = $phpWord->loadTemplate('Template.docx'); $templateProcessor = new TemplateProcessor('Template.docx');
$template->setValue('Name', 'Somebody someone'); $templateProcessor->setValue('Name', 'Somebody someone');
$template->setValue('Street', 'Coming-Undone-Street 32'); $templateProcessor->setValue('Street', 'Coming-Undone-Street 32');
``` ```
See `Sample_07_TemplateCloneRow.php` for example on how to create multirow from a single row in a template by using `cloneRow`. 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`).
See `Sample_23_TemplateBlock.php` for example on how to clone a block of text using `cloneBlock` and delete a block of text using `deleteBlock`. See `Sample_07_TemplateCloneRow.php` for example on how to create multirow from a single row in a template by using `TemplateProcessor::cloneRow`.
See `Sample_23_TemplateBlock.php` for example on how to clone a block of text using `TemplateProcessor::cloneBlock` and delete a block of text using `TemplateProcessor::deleteBlock`.
# Writers & readers # Writers & readers

View File

@ -0,0 +1,25 @@
.. _templates-processing:
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.
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}``.
Example:
.. code-block:: php
$templateProcessor = new TemplateProcessor('Template.docx');
$templateProcessor->setValue('Name', 'Somebody someone');
$templateProcessor->setValue('Street', 'Coming-Undone-Street 32');
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``).
See ``Sample_07_TemplateCloneRow.php`` for example on how to create
multirow from a single row in a template by using ``TemplateProcessor::cloneRow``.
See ``Sample_23_TemplateBlock.php`` for example on how to clone a block
of text using ``TemplateProcessor::cloneBlock`` and delete a block of text using
``TemplateProcessor::deleteBlock``.

View File

@ -1,27 +0,0 @@
.. _templates:
Templates
=========
You can create a docx template with included search-patterns that can be
replaced by any value you wish. Only single-line values can be replaced.
To load a template file, use the ``loadTemplate`` method. After loading
the docx template, you can use the ``setValue`` method to change the
value of a search pattern. The search-pattern model is:
``${search-pattern}``. It is not possible to add new PHPWord elements to
a loaded template file.
Example:
.. code-block:: php
$template = $phpWord->loadTemplate('Template.docx');
$template->setValue('Name', 'Somebody someone');
$template->setValue('Street', 'Coming-Undone-Street 32');
See ``Sample_07_TemplateCloneRow.php`` for example on how to create
multirow from a single row in a template by using ``cloneRow``.
See ``Sample_23_TemplateBlock.php`` for example on how to clone a block
of text using ``cloneBlock`` and delete a block of text using
``deleteBlock``.

View File

@ -1,64 +1,60 @@
<?php <?php
include_once 'Sample_Header.php'; include_once 'Sample_Header.php';
// New Word document // Template processor instance creation
echo date('H:i:s') , " Create new PhpWord object" , EOL; echo date('H:i:s') , ' Creating new TemplateProcessor instance...' , EOL;
$phpWord = new \PhpOffice\PhpWord\PhpWord(); $templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('resources/Sample_07_TemplateCloneRow.docx');
$document = $phpWord->loadTemplate('resources/Sample_07_TemplateCloneRow.docx');
// Variables on different parts of document // Variables on different parts of document
$document->setValue('weekday', date('l')); // On section/content $templateProcessor->setValue('weekday', date('l')); // On section/content
$document->setValue('time', date('H:i')); // On footer $templateProcessor->setValue('time', date('H:i')); // On footer
$document->setValue('serverName', realpath(__DIR__)); // On header $templateProcessor->setValue('serverName', realpath(__DIR__)); // On header
// Simple table // Simple table
$document->cloneRow('rowValue', 10); $templateProcessor->cloneRow('rowValue', 10);
$document->setValue('rowValue#1', 'Sun'); $templateProcessor->setValue('rowValue#1', 'Sun');
$document->setValue('rowValue#2', 'Mercury'); $templateProcessor->setValue('rowValue#2', 'Mercury');
$document->setValue('rowValue#3', 'Venus'); $templateProcessor->setValue('rowValue#3', 'Venus');
$document->setValue('rowValue#4', 'Earth'); $templateProcessor->setValue('rowValue#4', 'Earth');
$document->setValue('rowValue#5', 'Mars'); $templateProcessor->setValue('rowValue#5', 'Mars');
$document->setValue('rowValue#6', 'Jupiter'); $templateProcessor->setValue('rowValue#6', 'Jupiter');
$document->setValue('rowValue#7', 'Saturn'); $templateProcessor->setValue('rowValue#7', 'Saturn');
$document->setValue('rowValue#8', 'Uranus'); $templateProcessor->setValue('rowValue#8', 'Uranus');
$document->setValue('rowValue#9', 'Neptun'); $templateProcessor->setValue('rowValue#9', 'Neptun');
$document->setValue('rowValue#10', 'Pluto'); $templateProcessor->setValue('rowValue#10', 'Pluto');
$document->setValue('rowNumber#1', '1'); $templateProcessor->setValue('rowNumber#1', '1');
$document->setValue('rowNumber#2', '2'); $templateProcessor->setValue('rowNumber#2', '2');
$document->setValue('rowNumber#3', '3'); $templateProcessor->setValue('rowNumber#3', '3');
$document->setValue('rowNumber#4', '4'); $templateProcessor->setValue('rowNumber#4', '4');
$document->setValue('rowNumber#5', '5'); $templateProcessor->setValue('rowNumber#5', '5');
$document->setValue('rowNumber#6', '6'); $templateProcessor->setValue('rowNumber#6', '6');
$document->setValue('rowNumber#7', '7'); $templateProcessor->setValue('rowNumber#7', '7');
$document->setValue('rowNumber#8', '8'); $templateProcessor->setValue('rowNumber#8', '8');
$document->setValue('rowNumber#9', '9'); $templateProcessor->setValue('rowNumber#9', '9');
$document->setValue('rowNumber#10', '10'); $templateProcessor->setValue('rowNumber#10', '10');
// Table with a spanned cell // Table with a spanned cell
$document->cloneRow('userId', 3); $templateProcessor->cloneRow('userId', 3);
$document->setValue('userId#1', '1'); $templateProcessor->setValue('userId#1', '1');
$document->setValue('userFirstName#1', 'James'); $templateProcessor->setValue('userFirstName#1', 'James');
$document->setValue('userName#1', 'Taylor'); $templateProcessor->setValue('userName#1', 'Taylor');
$document->setValue('userPhone#1', '+1 428 889 773'); $templateProcessor->setValue('userPhone#1', '+1 428 889 773');
$document->setValue('userId#2', '2'); $templateProcessor->setValue('userId#2', '2');
$document->setValue('userFirstName#2', 'Robert'); $templateProcessor->setValue('userFirstName#2', 'Robert');
$document->setValue('userName#2', 'Bell'); $templateProcessor->setValue('userName#2', 'Bell');
$document->setValue('userPhone#2', '+1 428 889 774'); $templateProcessor->setValue('userPhone#2', '+1 428 889 774');
$document->setValue('userId#3', '3'); $templateProcessor->setValue('userId#3', '3');
$document->setValue('userFirstName#3', 'Michael'); $templateProcessor->setValue('userFirstName#3', 'Michael');
$document->setValue('userName#3', 'Ray'); $templateProcessor->setValue('userName#3', 'Ray');
$document->setValue('userPhone#3', '+1 428 889 775'); $templateProcessor->setValue('userPhone#3', '+1 428 889 775');
$name = 'Sample_07_TemplateCloneRow.docx'; echo date('H:i:s'), ' Saving the result document...', EOL;
echo date('H:i:s'), " Write to Word2007 format", EOL; $templateProcessor->saveAs('results/Sample_07_TemplateCloneRow.docx');
$document->saveAs($name);
rename($name, "results/{$name}");
echo getEndingNotes(array('Word2007' => 'docx')); echo getEndingNotes(array('Word2007' => 'docx'));
if (!CLI) { if (!CLI) {

View File

@ -1,22 +1,18 @@
<?php <?php
include_once 'Sample_Header.php'; include_once 'Sample_Header.php';
// New Word document // Template processor instance creation
echo date('H:i:s') , " Create new PhpWord object" , EOL; echo date('H:i:s') , ' Creating new TemplateProcessor instance...' , EOL;
$phpWord = new \PhpOffice\PhpWord\PhpWord(); $templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('resources/Sample_23_TemplateBlock.docx');
$document = $phpWord->loadTemplate('resources/Sample_23_TemplateBlock.docx');
// Will clone everything between ${tag} and ${/tag}, the number of times. By default, 1. // Will clone everything between ${tag} and ${/tag}, the number of times. By default, 1.
$document->cloneBlock('CLONEME', 3); $templateProcessor->cloneBlock('CLONEME', 3);
// Everything between ${tag} and ${/tag}, will be deleted/erased. // Everything between ${tag} and ${/tag}, will be deleted/erased.
$document->deleteBlock('DELETEME'); $templateProcessor->deleteBlock('DELETEME');
$name = 'Sample_23_TemplateBlock.docx'; echo date('H:i:s'), ' Saving the result document...', EOL;
echo date('H:i:s'), " Write to Word2007 format", EOL; $templateProcessor->saveAs('results/Sample_23_TemplateBlock.docx');
$document->saveAs($name);
rename($name, "results/{$name}");
echo getEndingNotes(array('Word2007' => 'docx')); echo getEndingNotes(array('Word2007' => 'docx'));
if (!CLI) { if (!CLI) {

View File

@ -264,14 +264,16 @@ class PhpWord
/** /**
* Load template by filename * Load template by filename
* *
* @deprecated 0.12.0 Use `new TemplateProcessor($documentTemplate)` instead.
*
* @param string $filename Fully qualified filename. * @param string $filename Fully qualified filename.
* @return Template * @return TemplateProcessor
* @throws \PhpOffice\PhpWord\Exception\Exception * @throws \PhpOffice\PhpWord\Exception\Exception
*/ */
public function loadTemplate($filename) public function loadTemplate($filename)
{ {
if (file_exists($filename)) { if (file_exists($filename)) {
return new Template($filename); return new TemplateProcessor($filename);
} else { } else {
throw new Exception("Template file {$filename} not found."); throw new Exception("Template file {$filename} not found.");
} }

View File

@ -17,449 +17,10 @@
namespace PhpOffice\PhpWord; namespace PhpOffice\PhpWord;
use PhpOffice\PhpWord\Exception\CopyFileException;
use PhpOffice\PhpWord\Exception\CreateTemporaryFileException;
use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\Shared\String;
use PhpOffice\PhpWord\Shared\ZipArchive;
/** /**
* Template * @deprecated 0.12.0 Use \PhpOffice\PhpWord\TemplateProcessor instead.
*/ */
class Template class Template extends TemplateProcessor
{ {
/**
* ZipArchive object.
*
* @var mixed
*/
private $zipClass;
/** }
* Temporary file name.
*
* @var string
*/
private $tempFileName;
/**
* Document XML.
*
* @var string
*/
private $documentXML;
/**
* Document header XML.
*
* @var string[]
*/
private $headerXMLs = array();
/**
* Create a new Template Object.
*
* @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception.
*
* @param string $fileName The fully qualified template file name.
* @throws \PhpOffice\PhpWord\Exception\CreateTemporaryFileException
* @throws \PhpOffice\PhpWord\Exception\CopyFileException
*/
public function __construct($fileName)
{
$this->tempFileName = tempnam(Settings::getTempDir(), 'PhpWord');
if (false === $this->tempFileName) {
throw new CreateTemporaryFileException();
}
// Copy the source File to the temp File
if (false === copy($fileName, $this->tempFileName)) {
throw new CopyFileException($fileName, $this->tempFileName);
}
$this->zipClass = new ZipArchive();
$this->zipClass->open($this->tempFileName);
// Find and load headers and footers
$index = 1;
while ($this->zipClass->locateName($this->getHeaderName($index)) !== false) {
$this->headerXMLs[$index] = $this->zipClass->getFromName($this->getHeaderName($index));
$index++;
}
$index = 1;
while ($this->zipClass->locateName($this->getFooterName($index)) !== false) {
$this->footerXMLs[$index] = $this->zipClass->getFromName($this->getFooterName($index));
$index++;
}
$this->documentXML = $this->zipClass->getFromName('word/document.xml');
}
/**
* Document footer XML.
*
* @var string[]
*/
private $footerXMLs = array();
/**
* Applies XSL style sheet to template's parts.
*
* @param \DOMDocument $xslDOMDocument
* @param array $xslOptions
* @param string $xslOptionsURI
* @return void
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function applyXslStyleSheet($xslDOMDocument, $xslOptions = array(), $xslOptionsURI = '')
{
$processor = new \XSLTProcessor();
$processor->importStylesheet($xslDOMDocument);
if (false === $processor->setParameter($xslOptionsURI, $xslOptions)) {
throw new Exception('Could not set values for the given XSL style sheet parameters.');
}
$xmlDOMDocument = new \DOMDocument();
if (false === $xmlDOMDocument->loadXML($this->documentXML)) {
throw new Exception('Could not load XML from the given template.');
}
$xmlTransformed = $processor->transformToXml($xmlDOMDocument);
if (false === $xmlTransformed) {
throw new Exception('Could not transform the given XML document.');
}
$this->documentXML = $xmlTransformed;
}
/**
* Set a Template value.
*
* @param mixed $search
* @param mixed $replace
* @param integer $limit
* @return void
*/
public function setValue($search, $replace, $limit = -1)
{
foreach ($this->headerXMLs as $index => $headerXML) {
$this->headerXMLs[$index] = $this->setValueForPart($this->headerXMLs[$index], $search, $replace, $limit);
}
$this->documentXML = $this->setValueForPart($this->documentXML, $search, $replace, $limit);
foreach ($this->footerXMLs as $index => $headerXML) {
$this->footerXMLs[$index] = $this->setValueForPart($this->footerXMLs[$index], $search, $replace, $limit);
}
}
/**
* Returns array of all variables in template.
*
* @return string[]
*/
public function getVariables()
{
$variables = $this->getVariablesForPart($this->documentXML);
foreach ($this->headerXMLs as $headerXML) {
$variables = array_merge($variables, $this->getVariablesForPart($headerXML));
}
foreach ($this->footerXMLs as $footerXML) {
$variables = array_merge($variables, $this->getVariablesForPart($footerXML));
}
return array_unique($variables);
}
/**
* Clone a table row in a template document.
*
* @param string $search
* @param integer $numberOfClones
* @return void
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function cloneRow($search, $numberOfClones)
{
if (substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
$search = '${' . $search . '}';
}
$tagPos = strpos($this->documentXML, $search);
if (!$tagPos) {
throw new Exception("Can not clone row, template variable not found or variable contains markup.");
}
$rowStart = $this->findRowStart($tagPos);
$rowEnd = $this->findRowEnd($tagPos);
$xmlRow = $this->getSlice($rowStart, $rowEnd);
// Check if there's a cell spanning multiple rows.
if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) {
// $extraRowStart = $rowEnd;
$extraRowEnd = $rowEnd;
while (true) {
$extraRowStart = $this->findRowStart($extraRowEnd + 1);
$extraRowEnd = $this->findRowEnd($extraRowEnd + 1);
// If extraRowEnd is lower then 7, there was no next row found.
if ($extraRowEnd < 7) {
break;
}
// If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
$tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd);
if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) &&
!preg_match('#<w:vMerge w:val="continue" />#', $tmpXmlRow)) {
break;
}
// This row was a spanned row, update $rowEnd and search for the next row.
$rowEnd = $extraRowEnd;
}
$xmlRow = $this->getSlice($rowStart, $rowEnd);
}
$result = $this->getSlice(0, $rowStart);
for ($i = 1; $i <= $numberOfClones; $i++) {
$result .= preg_replace('/\$\{(.*?)\}/', '\${\\1#' . $i . '}', $xmlRow);
}
$result .= $this->getSlice($rowEnd);
$this->documentXML = $result;
}
/**
* Clone a block.
*
* @param string $blockname
* @param integer $clones
* @param boolean $replace
* @return string|null
*/
public function cloneBlock($blockname, $clones = 1, $replace = true)
{
$xmlBlock = null;
preg_match(
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
$this->documentXML,
$matches
);
if (isset($matches[3])) {
$xmlBlock = $matches[3];
$cloned = array();
for ($i = 1; $i <= $clones; $i++) {
$cloned[] = $xmlBlock;
}
if ($replace) {
$this->documentXML = str_replace(
$matches[2] . $matches[3] . $matches[4],
implode('', $cloned),
$this->documentXML
);
}
}
return $xmlBlock;
}
/**
* Replace a block.
*
* @param string $blockname
* @param string $replacement
* @return void
*/
public function replaceBlock($blockname, $replacement)
{
preg_match(
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
$this->documentXML,
$matches
);
if (isset($matches[3])) {
$this->documentXML = str_replace(
$matches[2] . $matches[3] . $matches[4],
$replacement,
$this->documentXML
);
}
}
/**
* Delete a block of text.
*
* @param string $blockname
* @return void
*/
public function deleteBlock($blockname)
{
$this->replaceBlock($blockname, '');
}
/**
* Save XML to temporary file.
*
* @return string
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function save()
{
foreach ($this->headerXMLs as $index => $headerXML) {
$this->zipClass->addFromString($this->getHeaderName($index), $this->headerXMLs[$index]);
}
$this->zipClass->addFromString('word/document.xml', $this->documentXML);
foreach ($this->footerXMLs as $index => $headerXML) {
$this->zipClass->addFromString($this->getFooterName($index), $this->footerXMLs[$index]);
}
// Close zip file
if (false === $this->zipClass->close()) {
throw new Exception('Could not close zip file.');
}
return $this->tempFileName;
}
/**
* Save XML to defined name.
*
* @since 0.8.0
*
* @param string $fileName
* @return void
*/
public function saveAs($fileName)
{
$tempFileName = $this->save();
if (file_exists($fileName)) {
unlink($fileName);
}
rename($tempFileName, $fileName);
}
/**
* Find and replace placeholders in the given XML section.
*
* @param string $documentPartXML
* @param string $search
* @param string $replace
* @param integer $limit
* @return string
*/
protected function setValueForPart($documentPartXML, $search, $replace, $limit)
{
$pattern = '|\$\{([^\}]+)\}|U';
preg_match_all($pattern, $documentPartXML, $matches);
foreach ($matches[0] as $value) {
$valueCleaned = preg_replace('/<[^>]+>/', '', $value);
$valueCleaned = preg_replace('/<\/[^>]+>/', '', $valueCleaned);
$documentPartXML = str_replace($value, $valueCleaned, $documentPartXML);
}
if (substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
$search = '${' . $search . '}';
}
if (!String::isUTF8($replace)) {
$replace = utf8_encode($replace);
}
$replace = htmlspecialchars($replace);
$regExpDelim = '/';
$escapedSearch = preg_quote($search, $regExpDelim);
return preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $documentPartXML, $limit);
}
/**
* Find all variables in $documentPartXML.
*
* @param string $documentPartXML
* @return string[]
*/
protected function getVariablesForPart($documentPartXML)
{
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
return $matches[1];
}
/**
* Get the name of the footer file for $index.
*
* @param integer $index
* @return string
*/
private function getFooterName($index)
{
return sprintf('word/footer%d.xml', $index);
}
/**
* Get the name of the header file for $index.
*
* @param integer $index
* @return string
*/
private function getHeaderName($index)
{
return sprintf('word/header%d.xml', $index);
}
/**
* Find the start position of the nearest table row before $offset.
*
* @param integer $offset
* @return integer
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
private function findRowStart($offset)
{
$rowStart = strrpos($this->documentXML, "<w:tr ", ((strlen($this->documentXML) - $offset) * -1));
if (!$rowStart) {
$rowStart = strrpos($this->documentXML, "<w:tr>", ((strlen($this->documentXML) - $offset) * -1));
}
if (!$rowStart) {
throw new Exception("Can not find the start position of the row to clone.");
}
return $rowStart;
}
/**
* Find the end position of the nearest table row after $offset.
*
* @param integer $offset
* @return integer
*/
private function findRowEnd($offset)
{
$rowEnd = strpos($this->documentXML, "</w:tr>", $offset) + 7;
return $rowEnd;
}
/**
* Get a slice of a string.
*
* @param integer $startPosition
* @param integer $endPosition
* @return string
*/
private function getSlice($startPosition, $endPosition = 0)
{
if (!$endPosition) {
$endPosition = strlen($this->documentXML);
}
return substr($this->documentXML, $startPosition, ($endPosition - $startPosition));
}
}

View File

@ -0,0 +1,456 @@
<?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-2014 PHPWord contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
namespace PhpOffice\PhpWord;
use PhpOffice\PhpWord\Exception\CopyFileException;
use PhpOffice\PhpWord\Exception\CreateTemporaryFileException;
use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\Shared\String;
use PhpOffice\PhpWord\Shared\ZipArchive;
class TemplateProcessor
{
/**
* ZipArchive object.
*
* @var mixed
*/
private $zipClass;
/**
* @var string Temporary document filename (with path).
*/
private $temporaryDocumentFilename;
/**
* Content of main document part (in XML format) of the temporary document.
*
* @var string
*/
private $temporaryDocumentMainPart;
/**
* Content of headers (in XML format) of the temporary document.
*
* @var string[]
*/
private $temporaryDocumentHeaders = array();
/**
* Content of footers (in XML format) of the temporary document.
*
* @var string[]
*/
private $temporaryDocumentFooters = array();
/**
* @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception.
*
* @param string $documentTemplate The fully qualified template filename.
* @throws \PhpOffice\PhpWord\Exception\CreateTemporaryFileException
* @throws \PhpOffice\PhpWord\Exception\CopyFileException
*/
public function __construct($documentTemplate)
{
// Temporary document filename initialization
$this->temporaryDocumentFilename = tempnam(Settings::getTempDir(), 'PhpWord');
if (false === $this->temporaryDocumentFilename) {
throw new CreateTemporaryFileException();
}
// Template file cloning
if (false === copy($documentTemplate, $this->temporaryDocumentFilename)) {
throw new CopyFileException($documentTemplate, $this->temporaryDocumentFilename);
}
// Temporary document content extraction
$this->zipClass = new ZipArchive();
$this->zipClass->open($this->temporaryDocumentFilename);
$index = 1;
while ($this->zipClass->locateName($this->getHeaderName($index)) !== false) {
$this->temporaryDocumentHeaders[$index] = $this->zipClass->getFromName($this->getHeaderName($index));
$index++;
}
$index = 1;
while ($this->zipClass->locateName($this->getFooterName($index)) !== false) {
$this->temporaryDocumentFooters[$index] = $this->zipClass->getFromName($this->getFooterName($index));
$index++;
}
$this->temporaryDocumentMainPart = $this->zipClass->getFromName('word/document.xml');
}
/**
* Applies XSL style sheet to template's parts.
*
* @param \DOMDocument $xslDOMDocument
* @param array $xslOptions
* @param string $xslOptionsURI
* @return void
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function applyXslStyleSheet($xslDOMDocument, $xslOptions = array(), $xslOptionsURI = '')
{
$xsltProcessor = new \XSLTProcessor();
$xsltProcessor->importStylesheet($xslDOMDocument);
if (false === $xsltProcessor->setParameter($xslOptionsURI, $xslOptions)) {
throw new Exception('Could not set values for the given XSL style sheet parameters.');
}
$xmlDOMDocument = new \DOMDocument();
if (false === $xmlDOMDocument->loadXML($this->temporaryDocumentMainPart)) {
throw new Exception('Could not load XML from the given template.');
}
$xmlTransformed = $xsltProcessor->transformToXml($xmlDOMDocument);
if (false === $xmlTransformed) {
throw new Exception('Could not transform the given XML document.');
}
$this->temporaryDocumentMainPart = $xmlTransformed;
}
/**
* @param mixed $search
* @param mixed $replace
* @param integer $limit
* @return void
*/
public function setValue($search, $replace, $limit = -1)
{
foreach ($this->temporaryDocumentHeaders as $index => $headerXML) {
$this->temporaryDocumentHeaders[$index] = $this->setValueForPart($this->temporaryDocumentHeaders[$index], $search, $replace, $limit);
}
$this->temporaryDocumentMainPart = $this->setValueForPart($this->temporaryDocumentMainPart, $search, $replace, $limit);
foreach ($this->temporaryDocumentFooters as $index => $headerXML) {
$this->temporaryDocumentFooters[$index] = $this->setValueForPart($this->temporaryDocumentFooters[$index], $search, $replace, $limit);
}
}
/**
* Returns array of all variables in template.
*
* @return string[]
*/
public function getVariables()
{
$variables = $this->getVariablesForPart($this->temporaryDocumentMainPart);
foreach ($this->temporaryDocumentHeaders as $headerXML) {
$variables = array_merge($variables, $this->getVariablesForPart($headerXML));
}
foreach ($this->temporaryDocumentFooters as $footerXML) {
$variables = array_merge($variables, $this->getVariablesForPart($footerXML));
}
return array_unique($variables);
}
/**
* Clone a table row in a template document.
*
* @param string $search
* @param integer $numberOfClones
* @return void
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function cloneRow($search, $numberOfClones)
{
if (substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
$search = '${' . $search . '}';
}
$tagPos = strpos($this->temporaryDocumentMainPart, $search);
if (!$tagPos) {
throw new Exception("Can not clone row, template variable not found or variable contains markup.");
}
$rowStart = $this->findRowStart($tagPos);
$rowEnd = $this->findRowEnd($tagPos);
$xmlRow = $this->getSlice($rowStart, $rowEnd);
// Check if there's a cell spanning multiple rows.
if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) {
// $extraRowStart = $rowEnd;
$extraRowEnd = $rowEnd;
while (true) {
$extraRowStart = $this->findRowStart($extraRowEnd + 1);
$extraRowEnd = $this->findRowEnd($extraRowEnd + 1);
// If extraRowEnd is lower then 7, there was no next row found.
if ($extraRowEnd < 7) {
break;
}
// If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
$tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd);
if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) &&
!preg_match('#<w:vMerge w:val="continue" />#', $tmpXmlRow)) {
break;
}
// This row was a spanned row, update $rowEnd and search for the next row.
$rowEnd = $extraRowEnd;
}
$xmlRow = $this->getSlice($rowStart, $rowEnd);
}
$result = $this->getSlice(0, $rowStart);
for ($i = 1; $i <= $numberOfClones; $i++) {
$result .= preg_replace('/\$\{(.*?)\}/', '\${\\1#' . $i . '}', $xmlRow);
}
$result .= $this->getSlice($rowEnd);
$this->temporaryDocumentMainPart = $result;
}
/**
* Clone a block.
*
* @param string $blockname
* @param integer $clones
* @param boolean $replace
* @return string|null
*/
public function cloneBlock($blockname, $clones = 1, $replace = true)
{
$xmlBlock = null;
preg_match(
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
$this->temporaryDocumentMainPart,
$matches
);
if (isset($matches[3])) {
$xmlBlock = $matches[3];
$cloned = array();
for ($i = 1; $i <= $clones; $i++) {
$cloned[] = $xmlBlock;
}
if ($replace) {
$this->temporaryDocumentMainPart = str_replace(
$matches[2] . $matches[3] . $matches[4],
implode('', $cloned),
$this->temporaryDocumentMainPart
);
}
}
return $xmlBlock;
}
/**
* Replace a block.
*
* @param string $blockname
* @param string $replacement
* @return void
*/
public function replaceBlock($blockname, $replacement)
{
preg_match(
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
$this->temporaryDocumentMainPart,
$matches
);
if (isset($matches[3])) {
$this->temporaryDocumentMainPart = str_replace(
$matches[2] . $matches[3] . $matches[4],
$replacement,
$this->temporaryDocumentMainPart
);
}
}
/**
* Delete a block of text.
*
* @param string $blockname
* @return void
*/
public function deleteBlock($blockname)
{
$this->replaceBlock($blockname, '');
}
/**
* Saves the result document.
*
* @return string
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function save()
{
foreach ($this->temporaryDocumentHeaders as $index => $headerXML) {
$this->zipClass->addFromString($this->getHeaderName($index), $this->temporaryDocumentHeaders[$index]);
}
$this->zipClass->addFromString('word/document.xml', $this->temporaryDocumentMainPart);
foreach ($this->temporaryDocumentFooters as $index => $headerXML) {
$this->zipClass->addFromString($this->getFooterName($index), $this->temporaryDocumentFooters[$index]);
}
// Close zip file
if (false === $this->zipClass->close()) {
throw new Exception('Could not close zip file.');
}
return $this->temporaryDocumentFilename;
}
/**
* Saves the result document to the user defined file.
*
* @since 0.8.0
*
* @param string $fileName
* @return void
*/
public function saveAs($fileName)
{
$tempFileName = $this->save();
if (file_exists($fileName)) {
unlink($fileName);
}
rename($tempFileName, $fileName);
}
/**
* Find and replace placeholders in the given XML section.
*
* @param string $documentPartXML
* @param string $search
* @param string $replace
* @param integer $limit
* @return string
*/
protected function setValueForPart($documentPartXML, $search, $replace, $limit)
{
$pattern = '|\$\{([^\}]+)\}|U';
preg_match_all($pattern, $documentPartXML, $matches);
foreach ($matches[0] as $value) {
$valueCleaned = preg_replace('/<[^>]+>/', '', $value);
$valueCleaned = preg_replace('/<\/[^>]+>/', '', $valueCleaned);
$documentPartXML = str_replace($value, $valueCleaned, $documentPartXML);
}
if (substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
$search = '${' . $search . '}';
}
if (!String::isUTF8($replace)) {
$replace = utf8_encode($replace);
}
$replace = htmlspecialchars($replace);
$regExpDelim = '/';
$escapedSearch = preg_quote($search, $regExpDelim);
return preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $documentPartXML, $limit);
}
/**
* Find all variables in $documentPartXML.
*
* @param string $documentPartXML
* @return string[]
*/
protected function getVariablesForPart($documentPartXML)
{
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
return $matches[1];
}
/**
* Get the name of the footer file for $index.
*
* @param integer $index
* @return string
*/
private function getFooterName($index)
{
return sprintf('word/footer%d.xml', $index);
}
/**
* Get the name of the header file for $index.
*
* @param integer $index
* @return string
*/
private function getHeaderName($index)
{
return sprintf('word/header%d.xml', $index);
}
/**
* Find the start position of the nearest table row before $offset.
*
* @param integer $offset
* @return integer
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
private function findRowStart($offset)
{
$rowStart = strrpos($this->temporaryDocumentMainPart, '<w:tr ', ((strlen($this->temporaryDocumentMainPart) - $offset) * -1));
if (!$rowStart) {
$rowStart = strrpos($this->temporaryDocumentMainPart, '<w:tr>', ((strlen($this->temporaryDocumentMainPart) - $offset) * -1));
}
if (!$rowStart) {
throw new Exception('Can not find the start position of the row to clone.');
}
return $rowStart;
}
/**
* Find the end position of the nearest table row after $offset.
*
* @param integer $offset
* @return integer
*/
private function findRowEnd($offset)
{
return strpos($this->temporaryDocumentMainPart, '</w:tr>', $offset) + 7;
}
/**
* Get a slice of a string.
*
* @param integer $startPosition
* @param integer $endPosition
* @return string
*/
private function getSlice($startPosition, $endPosition = 0)
{
if (!$endPosition) {
$endPosition = strlen($this->temporaryDocumentMainPart);
}
return substr($this->temporaryDocumentMainPart, $startPosition, ($endPosition - $startPosition));
}
}

View File

@ -119,6 +119,8 @@ class PhpWordTest extends \PHPUnit_Framework_TestCase
/** /**
* Test load template * Test load template
*
* @deprecated 0.12.0
*/ */
public function testLoadTemplate() public function testLoadTemplate()
{ {
@ -126,7 +128,7 @@ class PhpWordTest extends \PHPUnit_Framework_TestCase
$phpWord = new PhpWord(); $phpWord = new PhpWord();
$this->assertInstanceOf( $this->assertInstanceOf(
'PhpOffice\\PhpWord\\Template', 'PhpOffice\\PhpWord\\TemplateProcessor',
$phpWord->loadTemplate($templateFqfn) $phpWord->loadTemplate($templateFqfn)
); );
} }
@ -134,6 +136,8 @@ class PhpWordTest extends \PHPUnit_Framework_TestCase
/** /**
* Test load template exception * Test load template exception
* *
* @deprecated 0.12.0
*
* @expectedException \PhpOffice\PhpWord\Exception\Exception * @expectedException \PhpOffice\PhpWord\Exception\Exception
*/ */
public function testLoadTemplateException() public function testLoadTemplateException()

View File

@ -17,35 +17,33 @@
namespace PhpOffice\PhpWord\Tests; namespace PhpOffice\PhpWord\Tests;
use PhpOffice\PhpWord\Template; use PhpOffice\PhpWord\TemplateProcessor;
/** /**
* Test class for PhpOffice\PhpWord\Template * @covers \PhpOffice\PhpWord\TemplateProcessor
* * @coversDefaultClass \PhpOffice\PhpWord\TemplateProcessor
* @covers \PhpOffice\PhpWord\Template
* @coversDefaultClass \PhpOffice\PhpWord\Template
* @runTestsInSeparateProcesses * @runTestsInSeparateProcesses
*/ */
final class TemplateTest extends \PHPUnit_Framework_TestCase final class TemplateProcessorTest extends \PHPUnit_Framework_TestCase
{ {
/** /**
* Template can be saved in temporary location * Template can be saved in temporary location.
* *
* @covers ::save * @covers ::save
* @test * @test
*/ */
final public function testTemplateCanBeSavedInTemporaryLocation() final public function testTemplateCanBeSavedInTemporaryLocation()
{ {
$templateFqfn = __DIR__ . "/_files/templates/with_table_macros.docx"; $templateFqfn = __DIR__ . '/_files/templates/with_table_macros.docx';
$document = new Template($templateFqfn); $templateProcessor = new TemplateProcessor($templateFqfn);
$xslDOMDocument = new \DOMDocument(); $xslDOMDocument = new \DOMDocument();
$xslDOMDocument->load(__DIR__ . "/_files/xsl/remove_tables_by_needle.xsl"); $xslDOMDocument->load(__DIR__ . "/_files/xsl/remove_tables_by_needle.xsl");
foreach (array('${employee.', '${scoreboard.') as $needle) { foreach (array('${employee.', '${scoreboard.') as $needle) {
$document->applyXslStyleSheet($xslDOMDocument, array('needle' => $needle)); $templateProcessor->applyXslStyleSheet($xslDOMDocument, array('needle' => $needle));
} }
$documentFqfn = $document->save(); $documentFqfn = $templateProcessor->save();
$this->assertNotEmpty($documentFqfn, 'FQFN of the saved document is empty.'); $this->assertNotEmpty($documentFqfn, 'FQFN of the saved document is empty.');
$this->assertFileExists($documentFqfn, "The saved document \"{$documentFqfn}\" doesn't exist."); $this->assertFileExists($documentFqfn, "The saved document \"{$documentFqfn}\" doesn't exist.");
@ -70,9 +68,10 @@ final class TemplateTest extends \PHPUnit_Framework_TestCase
} }
/** /**
* XSL stylesheet can be applied * XSL stylesheet can be applied.
* *
* @param string $actualDocumentFqfn * @param string $actualDocumentFqfn
* @throws \Exception
* @covers ::applyXslStyleSheet * @covers ::applyXslStyleSheet
* @depends testTemplateCanBeSavedInTemporaryLocation * @depends testTemplateCanBeSavedInTemporaryLocation
* @test * @test
@ -99,7 +98,7 @@ final class TemplateTest extends \PHPUnit_Framework_TestCase
} }
/** /**
* XSL stylesheet cannot be applied on failure in setting parameter value * XSL stylesheet cannot be applied on failure in setting parameter value.
* *
* @covers ::applyXslStyleSheet * @covers ::applyXslStyleSheet
* @expectedException \PhpOffice\PhpWord\Exception\Exception * @expectedException \PhpOffice\PhpWord\Exception\Exception
@ -108,20 +107,20 @@ final class TemplateTest extends \PHPUnit_Framework_TestCase
*/ */
final public function testXslStyleSheetCanNotBeAppliedOnFailureOfSettingParameterValue() final public function testXslStyleSheetCanNotBeAppliedOnFailureOfSettingParameterValue()
{ {
$template = new Template(__DIR__ . "/_files/templates/blank.docx"); $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/blank.docx');
$xslDOMDocument = new \DOMDocument(); $xslDOMDocument = new \DOMDocument();
$xslDOMDocument->load(__DIR__ . "/_files/xsl/passthrough.xsl"); $xslDOMDocument->load(__DIR__ . '/_files/xsl/passthrough.xsl');
/* /*
* We have to use error control below, because \XSLTProcessor::setParameter omits warning on failure. * We have to use error control below, because \XSLTProcessor::setParameter omits warning on failure.
* This warning fails the test. * This warning fails the test.
*/ */
@$template->applyXslStyleSheet($xslDOMDocument, array(1 => 'somevalue')); @$templateProcessor->applyXslStyleSheet($xslDOMDocument, array(1 => 'somevalue'));
} }
/** /**
* XSL stylesheet can be applied on failure of loading XML from template * XSL stylesheet can be applied on failure of loading XML from template.
* *
* @covers ::applyXslStyleSheet * @covers ::applyXslStyleSheet
* @expectedException \PhpOffice\PhpWord\Exception\Exception * @expectedException \PhpOffice\PhpWord\Exception\Exception
@ -130,83 +129,88 @@ final class TemplateTest extends \PHPUnit_Framework_TestCase
*/ */
final public function testXslStyleSheetCanNotBeAppliedOnFailureOfLoadingXmlFromTemplate() final public function testXslStyleSheetCanNotBeAppliedOnFailureOfLoadingXmlFromTemplate()
{ {
$template = new Template(__DIR__ . "/_files/templates/corrupted_main_document_part.docx"); $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/corrupted_main_document_part.docx');
$xslDOMDocument = new \DOMDocument(); $xslDOMDocument = new \DOMDocument();
$xslDOMDocument->load(__DIR__ . "/_files/xsl/passthrough.xsl"); $xslDOMDocument->load(__DIR__ . '/_files/xsl/passthrough.xsl');
/* /*
* We have to use error control below, because \DOMDocument::loadXML omits warning on failure. * We have to use error control below, because \DOMDocument::loadXML omits warning on failure.
* This warning fails the test. * This warning fails the test.
*/ */
@$template->applyXslStyleSheet($xslDOMDocument); @$templateProcessor->applyXslStyleSheet($xslDOMDocument);
} }
/** /**
* Get variables and clone row * @civers ::setValue
* @covers ::cloneRow
* @covers ::saveAs
* @test
*/ */
public function testCloneRow() public function testCloneRow()
{ {
$template = __DIR__ . "/_files/templates/clone-merge.docx"; $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/clone-merge.docx');
$expectedVar = array('tableHeader', 'userId', 'userName', 'userLocation');
$docName = 'clone-test-result.docx';
$document = new Template($template); $this->assertEquals(
$actualVar = $document->getVariables(); array('tableHeader', 'userId', 'userName', 'userLocation'),
$document->setValue('tableHeader', utf8_decode('ééé')); $templateProcessor->getVariables()
$document->cloneRow('userId', 1); );
$document->setValue('userId#1', 'Test');
$document->saveAs($docName); $docName = 'clone-test-result.docx';
$templateProcessor->setValue('tableHeader', utf8_decode('ééé'));
$templateProcessor->cloneRow('userId', 1);
$templateProcessor->setValue('userId#1', 'Test');
$templateProcessor->saveAs($docName);
$docFound = file_exists($docName); $docFound = file_exists($docName);
unlink($docName); unlink($docName);
$this->assertEquals($expectedVar, $actualVar);
$this->assertTrue($docFound); $this->assertTrue($docFound);
} }
/** /**
* Replace variables in header and footer * @covers ::setValue
* @covers ::saveAs
* @test
*/ */
public function testVariablesCanBeReplacedInHeaderAndFooter() public function testVariablesCanBeReplacedInHeaderAndFooter()
{ {
$template = __DIR__ . "/_files/templates/header-footer.docx"; $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/header-footer.docx');
$expectedVar = array('documentContent', 'headerValue', 'footerValue');
$docName = 'header-footer-test-result.docx';
$document = new Template($template); $this->assertEquals(
$actualVar = $document->getVariables(); array('documentContent', 'headerValue', 'footerValue'),
$document->setValue('headerValue', 'Header Value'); $templateProcessor->getVariables()
$document->setValue('documentContent', 'Document text.'); );
$document->setValue('footerValue', 'Footer Value');
$document->saveAs($docName); $docName = 'header-footer-test-result.docx';
$templateProcessor->setValue('headerValue', 'Header Value');
$templateProcessor->setValue('documentContent', 'Document text.');
$templateProcessor->setValue('footerValue', 'Footer Value');
$templateProcessor->saveAs($docName);
$docFound = file_exists($docName); $docFound = file_exists($docName);
unlink($docName); unlink($docName);
$this->assertEquals($expectedVar, $actualVar);
$this->assertTrue($docFound); $this->assertTrue($docFound);
} }
/** /**
* Clone and delete block * @covers ::cloneBlock
* @covers ::deleteBlock
* @covers ::saveAs
* @test
*/ */
public function testCloneDeleteBlock() public function testCloneDeleteBlock()
{ {
$template = __DIR__ . "/_files/templates/clone-delete-block.docx"; $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/clone-delete-block.docx');
$expectedVar = array('DELETEME', '/DELETEME', 'CLONEME', '/CLONEME');
$this->assertEquals(
array('DELETEME', '/DELETEME', 'CLONEME', '/CLONEME'),
$templateProcessor->getVariables()
);
$docName = 'clone-delete-block-result.docx'; $docName = 'clone-delete-block-result.docx';
$templateProcessor->cloneBlock('CLONEME', 3);
$document = new Template($template); $templateProcessor->deleteBlock('DELETEME');
$actualVar = $document->getVariables(); $templateProcessor->saveAs($docName);
$document->cloneBlock('CLONEME', 3);
$document->deleteBlock('DELETEME');
$document->saveAs($docName);
$docFound = file_exists($docName); $docFound = file_exists($docName);
unlink($docName); unlink($docName);
$this->assertEquals($expectedVar, $actualVar);
$this->assertTrue($docFound); $this->assertTrue($docFound);
} }
} }