Merge branch 'develop' of https://github.com/PHPOffice/PHPWord into develop
This commit is contained in:
commit
8da62eb7b2
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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": {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ Format (RTF).
|
||||||
containers
|
containers
|
||||||
elements
|
elements
|
||||||
styles
|
styles
|
||||||
templates
|
templates-processing
|
||||||
writersreaders
|
writersreaders
|
||||||
recipes
|
recipes
|
||||||
faq
|
faq
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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``.
|
||||||
|
|
@ -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``.
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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.");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue