Merge pull request #1565 from troosan/set_complex_type_in_template
Set complex type in template
This commit is contained in:
commit
98026aefaf
|
|
@ -13,7 +13,6 @@ composer.phar
|
||||||
vendor
|
vendor
|
||||||
/report
|
/report
|
||||||
/build
|
/build
|
||||||
/samples/resources
|
|
||||||
/samples/results
|
/samples/results
|
||||||
/.settings
|
/.settings
|
||||||
phpword.ini
|
phpword.ini
|
||||||
|
|
|
||||||
|
|
@ -215,3 +215,32 @@ Applies the XSL stylesheet passed to header part, footer part and main part
|
||||||
$xslDomDocument = new \DOMDocument();
|
$xslDomDocument = new \DOMDocument();
|
||||||
$xslDomDocument->load('/path/to/my/stylesheet.xsl');
|
$xslDomDocument->load('/path/to/my/stylesheet.xsl');
|
||||||
$templateProcessor->applyXslStyleSheet($xslDomDocument);
|
$templateProcessor->applyXslStyleSheet($xslDomDocument);
|
||||||
|
|
||||||
|
setComplexValue
|
||||||
|
"""""""""""""""
|
||||||
|
Raplaces a ${macro} with the ComplexType passed.
|
||||||
|
See ``Sample_40_TemplateSetComplexValue.php`` for examples.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$inline = new TextRun();
|
||||||
|
$inline->addText('by a red italic text', array('italic' => true, 'color' => 'red'));
|
||||||
|
$templateProcessor->setComplexValue('inline', $inline);
|
||||||
|
|
||||||
|
setComplexBlock
|
||||||
|
"""""""""""""""
|
||||||
|
Raplaces a ${macro} with the ComplexType passed.
|
||||||
|
See ``Sample_40_TemplateSetComplexValue.php`` for examples.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$table = new Table(array('borderSize' => 12, 'borderColor' => 'green', 'width' => 6000, 'unit' => TblWidth::TWIP));
|
||||||
|
$table->addRow();
|
||||||
|
$table->addCell(150)->addText('Cell A1');
|
||||||
|
$table->addCell(150)->addText('Cell A2');
|
||||||
|
$table->addCell(150)->addText('Cell A3');
|
||||||
|
$table->addRow();
|
||||||
|
$table->addCell(150)->addText('Cell B1');
|
||||||
|
$table->addCell(150)->addText('Cell B2');
|
||||||
|
$table->addCell(150)->addText('Cell B3');
|
||||||
|
$templateProcessor->setComplexBlock('table', $table);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
use PhpOffice\PhpWord\Element\Field;
|
||||||
|
use PhpOffice\PhpWord\Element\Table;
|
||||||
|
use PhpOffice\PhpWord\Element\TextRun;
|
||||||
|
use PhpOffice\PhpWord\SimpleType\TblWidth;
|
||||||
|
|
||||||
|
include_once 'Sample_Header.php';
|
||||||
|
|
||||||
|
// Template processor instance creation
|
||||||
|
echo date('H:i:s'), ' Creating new TemplateProcessor instance...', EOL;
|
||||||
|
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('resources/Sample_40_TemplateSetComplexValue.docx');
|
||||||
|
|
||||||
|
$title = new TextRun();
|
||||||
|
$title->addText('This title has been set ', array('bold' => true, 'italic' => true, 'color' => 'blue'));
|
||||||
|
$title->addText('dynamically', array('bold' => true, 'italic' => true, 'color' => 'red', 'underline' => 'single'));
|
||||||
|
$templateProcessor->setComplexBlock('title', $title);
|
||||||
|
|
||||||
|
$inline = new TextRun();
|
||||||
|
$inline->addText('by a red italic text', array('italic' => true, 'color' => 'red'));
|
||||||
|
$templateProcessor->setComplexValue('inline', $inline);
|
||||||
|
|
||||||
|
$table = new Table(array('borderSize' => 12, 'borderColor' => 'green', 'width' => 6000, 'unit' => TblWidth::TWIP));
|
||||||
|
$table->addRow();
|
||||||
|
$table->addCell(150)->addText('Cell A1');
|
||||||
|
$table->addCell(150)->addText('Cell A2');
|
||||||
|
$table->addCell(150)->addText('Cell A3');
|
||||||
|
$table->addRow();
|
||||||
|
$table->addCell(150)->addText('Cell B1');
|
||||||
|
$table->addCell(150)->addText('Cell B2');
|
||||||
|
$table->addCell(150)->addText('Cell B3');
|
||||||
|
$templateProcessor->setComplexBlock('table', $table);
|
||||||
|
|
||||||
|
$field = new Field('DATE', array('dateformat' => 'dddd d MMMM yyyy H:mm:ss'), array('PreserveFormat'));
|
||||||
|
$templateProcessor->setComplexValue('field', $field);
|
||||||
|
|
||||||
|
// $link = new Link('https://github.com/PHPOffice/PHPWord');
|
||||||
|
// $templateProcessor->setComplexValue('link', $link);
|
||||||
|
|
||||||
|
echo date('H:i:s'), ' Saving the result document...', EOL;
|
||||||
|
$templateProcessor->saveAs('results/Sample_40_TemplateSetComplexValue.docx');
|
||||||
|
|
||||||
|
echo getEndingNotes(array('Word2007' => 'docx'), 'results/Sample_40_TemplateSetComplexValue.docx');
|
||||||
|
if (!CLI) {
|
||||||
|
include_once 'Sample_Footer.php';
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -18,6 +18,7 @@
|
||||||
namespace PhpOffice\PhpWord;
|
namespace PhpOffice\PhpWord;
|
||||||
|
|
||||||
use PhpOffice\Common\Text;
|
use PhpOffice\Common\Text;
|
||||||
|
use PhpOffice\Common\XMLWriter;
|
||||||
use PhpOffice\PhpWord\Escaper\RegExp;
|
use PhpOffice\PhpWord\Escaper\RegExp;
|
||||||
use PhpOffice\PhpWord\Escaper\Xml;
|
use PhpOffice\PhpWord\Escaper\Xml;
|
||||||
use PhpOffice\PhpWord\Exception\CopyFileException;
|
use PhpOffice\PhpWord\Exception\CopyFileException;
|
||||||
|
|
@ -249,6 +250,46 @@ class TemplateProcessor
|
||||||
return $subject;
|
return $subject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $search
|
||||||
|
* @param \PhpOffice\PhpWord\Element\AbstractElement $complexType
|
||||||
|
*/
|
||||||
|
public function setComplexValue($search, \PhpOffice\PhpWord\Element\AbstractElement $complexType)
|
||||||
|
{
|
||||||
|
$elementName = substr(get_class($complexType), strrpos(get_class($complexType), '\\') + 1);
|
||||||
|
$objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $elementName;
|
||||||
|
|
||||||
|
$xmlWriter = new XMLWriter();
|
||||||
|
/** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $elementWriter */
|
||||||
|
$elementWriter = new $objectClass($xmlWriter, $complexType, true);
|
||||||
|
$elementWriter->write();
|
||||||
|
|
||||||
|
$where = $this->findContainingXmlBlockForMacro($search, 'w:r');
|
||||||
|
$block = $this->getSlice($where['start'], $where['end']);
|
||||||
|
$textParts = $this->splitTextIntoTexts($block);
|
||||||
|
$this->replaceXmlBlock($search, $textParts, 'w:r');
|
||||||
|
|
||||||
|
$search = static::ensureMacroCompleted($search);
|
||||||
|
$this->replaceXmlBlock($search, $xmlWriter->getData(), 'w:r');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $search
|
||||||
|
* @param \PhpOffice\PhpWord\Element\AbstractElement $complexType
|
||||||
|
*/
|
||||||
|
public function setComplexBlock($search, \PhpOffice\PhpWord\Element\AbstractElement $complexType)
|
||||||
|
{
|
||||||
|
$elementName = substr(get_class($complexType), strrpos(get_class($complexType), '\\') + 1);
|
||||||
|
$objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $elementName;
|
||||||
|
|
||||||
|
$xmlWriter = new XMLWriter();
|
||||||
|
/** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $elementWriter */
|
||||||
|
$elementWriter = new $objectClass($xmlWriter, $complexType, false);
|
||||||
|
$elementWriter->write();
|
||||||
|
|
||||||
|
$this->replaceXmlBlock($search, $xmlWriter->getData(), 'w:p');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $search
|
* @param mixed $search
|
||||||
* @param mixed $replace
|
* @param mixed $replace
|
||||||
|
|
@ -685,6 +726,7 @@ class TemplateProcessor
|
||||||
public function cloneBlock($blockname, $clones = 1, $replace = true, $indexVariables = false, $variableReplacements = null)
|
public function cloneBlock($blockname, $clones = 1, $replace = true, $indexVariables = false, $variableReplacements = null)
|
||||||
{
|
{
|
||||||
$xmlBlock = null;
|
$xmlBlock = null;
|
||||||
|
$matches = array();
|
||||||
preg_match(
|
preg_match(
|
||||||
'/(<\?xml.*)(<w:p\b.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p\b.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
'/(<\?xml.*)(<w:p\b.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p\b.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
||||||
$this->tempDocumentMainPart,
|
$this->tempDocumentMainPart,
|
||||||
|
|
@ -724,6 +766,7 @@ class TemplateProcessor
|
||||||
*/
|
*/
|
||||||
public function replaceBlock($blockname, $replacement)
|
public function replaceBlock($blockname, $replacement)
|
||||||
{
|
{
|
||||||
|
$matches = array();
|
||||||
preg_match(
|
preg_match(
|
||||||
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
||||||
$this->tempDocumentMainPart,
|
$this->tempDocumentMainPart,
|
||||||
|
|
@ -865,6 +908,7 @@ class TemplateProcessor
|
||||||
*/
|
*/
|
||||||
protected function getVariablesForPart($documentPartXML)
|
protected function getVariablesForPart($documentPartXML)
|
||||||
{
|
{
|
||||||
|
$matches = array();
|
||||||
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
|
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
|
||||||
|
|
||||||
return $matches[1];
|
return $matches[1];
|
||||||
|
|
@ -893,6 +937,7 @@ class TemplateProcessor
|
||||||
|
|
||||||
$pattern = '~PartName="\/(word\/document.*?\.xml)" ContentType="application\/vnd\.openxmlformats-officedocument\.wordprocessingml\.document\.main\+xml"~';
|
$pattern = '~PartName="\/(word\/document.*?\.xml)" ContentType="application\/vnd\.openxmlformats-officedocument\.wordprocessingml\.document\.main\+xml"~';
|
||||||
|
|
||||||
|
$matches = array();
|
||||||
preg_match($pattern, $contentTypes, $matches);
|
preg_match($pattern, $contentTypes, $matches);
|
||||||
|
|
||||||
return array_key_exists(1, $matches) ? $matches[1] : 'word/document.xml';
|
return array_key_exists(1, $matches) ? $matches[1] : 'word/document.xml';
|
||||||
|
|
@ -1031,4 +1076,141 @@ class TemplateProcessor
|
||||||
|
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace an XML block surrounding a macro with a new block
|
||||||
|
*
|
||||||
|
* @param string $macro Name of macro
|
||||||
|
* @param string $block New block content
|
||||||
|
* @param string $blockType XML tag type of block
|
||||||
|
* @return \PhpOffice\PhpWord\TemplateProcessor Fluent interface
|
||||||
|
*/
|
||||||
|
protected function replaceXmlBlock($macro, $block, $blockType = 'w:p')
|
||||||
|
{
|
||||||
|
$where = $this->findContainingXmlBlockForMacro($macro, $blockType);
|
||||||
|
if (is_array($where)) {
|
||||||
|
$this->tempDocumentMainPart = $this->getSlice(0, $where['start']) . $block . $this->getSlice($where['end']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find start and end of XML block containing the given macro
|
||||||
|
* e.g. <w:p>...${macro}...</w:p>
|
||||||
|
*
|
||||||
|
* Note that only the first instance of the macro will be found
|
||||||
|
*
|
||||||
|
* @param string $macro Name of macro
|
||||||
|
* @param string $blockType XML tag for block
|
||||||
|
* @return bool|int[] FALSE if not found, otherwise array with start and end
|
||||||
|
*/
|
||||||
|
protected function findContainingXmlBlockForMacro($macro, $blockType = 'w:p')
|
||||||
|
{
|
||||||
|
$macroPos = $this->findMacro($macro);
|
||||||
|
if (0 > $macroPos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$start = $this->findXmlBlockStart($macroPos, $blockType);
|
||||||
|
if (0 > $start) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$end = $this->findXmlBlockEnd($start, $blockType);
|
||||||
|
//if not found or if resulting string does not contain the macro we are searching for
|
||||||
|
if (0 > $end || strstr($this->getSlice($start, $end), $macro) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array('start' => $start, 'end' => $end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the position of (the start of) a macro
|
||||||
|
*
|
||||||
|
* Returns -1 if not found, otherwise position of opening $
|
||||||
|
*
|
||||||
|
* Note that only the first instance of the macro will be found
|
||||||
|
*
|
||||||
|
* @param string $search Macro name
|
||||||
|
* @param int $offset Offset from which to start searching
|
||||||
|
* @return int -1 if macro not found
|
||||||
|
*/
|
||||||
|
protected function findMacro($search, $offset = 0)
|
||||||
|
{
|
||||||
|
$search = static::ensureMacroCompleted($search);
|
||||||
|
$pos = strpos($this->tempDocumentMainPart, $search, $offset);
|
||||||
|
|
||||||
|
return ($pos === false) ? -1 : $pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the start position of the nearest XML block start before $offset
|
||||||
|
*
|
||||||
|
* @param int $offset Search position
|
||||||
|
* @param string $blockType XML Block tag
|
||||||
|
* @return int -1 if block start not found
|
||||||
|
*/
|
||||||
|
protected function findXmlBlockStart($offset, $blockType)
|
||||||
|
{
|
||||||
|
$reverseOffset = (strlen($this->tempDocumentMainPart) - $offset) * -1;
|
||||||
|
// first try XML tag with attributes
|
||||||
|
$blockStart = strrpos($this->tempDocumentMainPart, '<' . $blockType . ' ', $reverseOffset);
|
||||||
|
// if not found, or if found but contains the XML tag without attribute
|
||||||
|
if (false === $blockStart || strrpos($this->getSlice($blockStart, $offset), '<' . $blockType . '>')) {
|
||||||
|
// also try XML tag without attributes
|
||||||
|
$blockStart = strrpos($this->tempDocumentMainPart, '<' . $blockType . '>', $reverseOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($blockStart === false) ? -1 : $blockStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the nearest block end position after $offset
|
||||||
|
*
|
||||||
|
* @param int $offset Search position
|
||||||
|
* @param string $blockType XML Block tag
|
||||||
|
* @return int -1 if block end not found
|
||||||
|
*/
|
||||||
|
protected function findXmlBlockEnd($offset, $blockType)
|
||||||
|
{
|
||||||
|
$blockEndStart = strpos($this->tempDocumentMainPart, '</' . $blockType . '>', $offset);
|
||||||
|
// return position of end of tag if found, otherwise -1
|
||||||
|
|
||||||
|
return ($blockEndStart === false) ? -1 : $blockEndStart + 3 + strlen($blockType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits a w:r/w:t into a list of w:r where each ${macro} is in a separate w:r
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function splitTextIntoTexts($text)
|
||||||
|
{
|
||||||
|
if (!$this->textNeedsSplitting($text)) {
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
$matches = array();
|
||||||
|
if (preg_match('/(<w:rPr.*<\/w:rPr>)/i', $text, $matches)) {
|
||||||
|
$extractedStyle = $matches[0];
|
||||||
|
} else {
|
||||||
|
$extractedStyle = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$unformattedText = preg_replace('/>\s+</', '><', $text);
|
||||||
|
$result = str_replace(array('${', '}'), array('</w:t></w:r><w:r>' . $extractedStyle . '<w:t xml:space="preserve">${', '}</w:t></w:r><w:r>' . $extractedStyle . '<w:t xml:space="preserve">'), $unformattedText);
|
||||||
|
|
||||||
|
return str_replace(array('<w:r>' . $extractedStyle . '<w:t xml:space="preserve"></w:t></w:r>', '<w:r><w:t xml:space="preserve"></w:t></w:r>', '<w:t>'), array('', '', '<w:t xml:space="preserve">'), $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if string contains a macro that is not in it's own w:r
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function textNeedsSplitting($text)
|
||||||
|
{
|
||||||
|
return preg_match('/[^>]\${|}[^<]/i', $text) == 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@
|
||||||
|
|
||||||
namespace PhpOffice\PhpWord;
|
namespace PhpOffice\PhpWord;
|
||||||
|
|
||||||
|
use PhpOffice\PhpWord\Element\Text;
|
||||||
|
use PhpOffice\PhpWord\Element\TextRun;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers \PhpOffice\PhpWord\TemplateProcessor
|
* @covers \PhpOffice\PhpWord\TemplateProcessor
|
||||||
* @coversDefaultClass \PhpOffice\PhpWord\TemplateProcessor
|
* @coversDefaultClass \PhpOffice\PhpWord\TemplateProcessor
|
||||||
|
|
@ -307,6 +310,59 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSetComplexValue()
|
||||||
|
{
|
||||||
|
$title = new TextRun();
|
||||||
|
$title->addText('This is my title');
|
||||||
|
|
||||||
|
$firstname = new Text('Donald');
|
||||||
|
$lastname = new Text('Duck');
|
||||||
|
|
||||||
|
$mainPart = '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<w:p>
|
||||||
|
<w:r>
|
||||||
|
<w:t xml:space="preserve">Hello ${document-title}</w:t>
|
||||||
|
</w:r>
|
||||||
|
</w:p>
|
||||||
|
<w:p>
|
||||||
|
<w:r>
|
||||||
|
<w:t xml:space="preserve">Hello ${firstname} ${lastname}</w:t>
|
||||||
|
</w:r>
|
||||||
|
</w:p>';
|
||||||
|
|
||||||
|
$result = '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<w:p>
|
||||||
|
<w:pPr/>
|
||||||
|
<w:r>
|
||||||
|
<w:rPr/>
|
||||||
|
<w:t xml:space="preserve">This is my title</w:t>
|
||||||
|
</w:r>
|
||||||
|
</w:p>
|
||||||
|
<w:p>
|
||||||
|
<w:r>
|
||||||
|
<w:t xml:space="preserve">Hello </w:t>
|
||||||
|
</w:r>
|
||||||
|
<w:r>
|
||||||
|
<w:rPr/>
|
||||||
|
<w:t xml:space="preserve">Donald</w:t>
|
||||||
|
</w:r>
|
||||||
|
<w:r>
|
||||||
|
<w:t xml:space="preserve"> </w:t>
|
||||||
|
</w:r>
|
||||||
|
<w:r>
|
||||||
|
<w:rPr/>
|
||||||
|
<w:t xml:space="preserve">Duck</w:t>
|
||||||
|
</w:r>
|
||||||
|
</w:p>';
|
||||||
|
|
||||||
|
$templateProcessor = new TestableTemplateProcesor($mainPart);
|
||||||
|
$templateProcessor->setComplexBlock('document-title', $title);
|
||||||
|
$templateProcessor->setComplexValue('firstname', $firstname);
|
||||||
|
$templateProcessor->setComplexValue('lastname', $lastname);
|
||||||
|
|
||||||
|
$this->assertEquals(preg_replace('/>\s+</', '><', $result), preg_replace('/>\s+</', '><', $templateProcessor->getMainPart()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers ::setValues
|
* @covers ::setValues
|
||||||
* @test
|
* @test
|
||||||
|
|
@ -675,4 +731,103 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
|
||||||
$variables = $templateProcessor->getVariablesForPart('<w:t>$</w:t></w:r><w:bookmarkStart w:id="0" w:name="_GoBack"/><w:bookmarkEnd w:id="0"/><w:r><w:t xml:space="preserve">15,000.00. </w:t></w:r><w:r w:rsidR="0056499B"><w:t>$</w:t></w:r><w:r w:rsidR="00573DFD" w:rsidRPr="00573DFD"><w:rPr><w:iCs/></w:rPr><w:t>{</w:t></w:r><w:proofErr w:type="spellStart"/><w:r w:rsidR="00573DFD" w:rsidRPr="00573DFD"><w:rPr><w:iCs/></w:rPr><w:t>variable_name</w:t></w:r><w:proofErr w:type="spellEnd"/><w:r w:rsidR="00573DFD" w:rsidRPr="00573DFD"><w:rPr><w:iCs/></w:rPr><w:t>}</w:t></w:r>');
|
$variables = $templateProcessor->getVariablesForPart('<w:t>$</w:t></w:r><w:bookmarkStart w:id="0" w:name="_GoBack"/><w:bookmarkEnd w:id="0"/><w:r><w:t xml:space="preserve">15,000.00. </w:t></w:r><w:r w:rsidR="0056499B"><w:t>$</w:t></w:r><w:r w:rsidR="00573DFD" w:rsidRPr="00573DFD"><w:rPr><w:iCs/></w:rPr><w:t>{</w:t></w:r><w:proofErr w:type="spellStart"/><w:r w:rsidR="00573DFD" w:rsidRPr="00573DFD"><w:rPr><w:iCs/></w:rPr><w:t>variable_name</w:t></w:r><w:proofErr w:type="spellEnd"/><w:r w:rsidR="00573DFD" w:rsidRPr="00573DFD"><w:rPr><w:iCs/></w:rPr><w:t>}</w:t></w:r>');
|
||||||
$this->assertEquals(array('variable_name'), $variables);
|
$this->assertEquals(array('variable_name'), $variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::textNeedsSplitting
|
||||||
|
*/
|
||||||
|
public function testTextNeedsSplitting()
|
||||||
|
{
|
||||||
|
$templateProcessor = new TestableTemplateProcesor();
|
||||||
|
|
||||||
|
$this->assertFalse($templateProcessor->textNeedsSplitting('<w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">${nothing-to-replace}</w:t></w:r>'));
|
||||||
|
|
||||||
|
$text = '<w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">Hello ${firstname} ${lastname}</w:t></w:r>';
|
||||||
|
$this->assertTrue($templateProcessor->textNeedsSplitting($text));
|
||||||
|
$splitText = $templateProcessor->splitTextIntoTexts($text);
|
||||||
|
$this->assertFalse($templateProcessor->textNeedsSplitting($splitText));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::splitTextIntoTexts
|
||||||
|
*/
|
||||||
|
public function testSplitTextIntoTexts()
|
||||||
|
{
|
||||||
|
$templateProcessor = new TestableTemplateProcesor();
|
||||||
|
|
||||||
|
$splitText = $templateProcessor->splitTextIntoTexts('<w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">${nothing-to-replace}</w:t></w:r>');
|
||||||
|
$this->assertEquals('<w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">${nothing-to-replace}</w:t></w:r>', $splitText);
|
||||||
|
|
||||||
|
$splitText = $templateProcessor->splitTextIntoTexts('<w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">Hello ${firstname} ${lastname}</w:t></w:r>');
|
||||||
|
$this->assertEquals('<w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">Hello </w:t></w:r><w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">${firstname}</w:t></w:r><w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve"> </w:t></w:r><w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">${lastname}</w:t></w:r>', $splitText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFindXmlBlockStart()
|
||||||
|
{
|
||||||
|
$toFind = '<w:r>
|
||||||
|
<w:rPr>
|
||||||
|
<w:rFonts w:ascii="Calibri" w:hAnsi="Calibri" w:cs="Calibri"/>
|
||||||
|
<w:lang w:val="en-GB"/>
|
||||||
|
</w:rPr>
|
||||||
|
<w:t>This whole paragraph will be replaced with my ${title}</w:t>
|
||||||
|
</w:r>';
|
||||||
|
$mainPart = '<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex" xmlns:cx2="http://schemas.microsoft.com/office/drawing/2015/10/21/chartex" xmlns:cx3="http://schemas.microsoft.com/office/drawing/2016/5/9/chartex" xmlns:cx4="http://schemas.microsoft.com/office/drawing/2016/5/10/chartex" xmlns:cx5="http://schemas.microsoft.com/office/drawing/2016/5/11/chartex" xmlns:cx6="http://schemas.microsoft.com/office/drawing/2016/5/12/chartex" xmlns:cx7="http://schemas.microsoft.com/office/drawing/2016/5/13/chartex" xmlns:cx8="http://schemas.microsoft.com/office/drawing/2016/5/14/chartex" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:aink="http://schemas.microsoft.com/office/drawing/2016/ink" xmlns:am3d="http://schemas.microsoft.com/office/drawing/2017/model3d" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 w15 w16se w16cid wp14">
|
||||||
|
<w:p w14:paraId="165D45AF" w14:textId="7FEC9B41" w:rsidR="005B1098" w:rsidRDefault="005B1098">
|
||||||
|
<w:r w:rsidR="00A045B2">
|
||||||
|
<w:rPr>
|
||||||
|
<w:rFonts w:ascii="Calibri" w:hAnsi="Calibri" w:cs="Calibri"/>
|
||||||
|
<w:lang w:val="en-GB"/>
|
||||||
|
</w:rPr>
|
||||||
|
<w:t xml:space="preserve"> ${value1} ${value2}</w:t>
|
||||||
|
</w:r>
|
||||||
|
<w:r>
|
||||||
|
<w:rPr>
|
||||||
|
<w:rFonts w:ascii="Calibri" w:hAnsi="Calibri" w:cs="Calibri"/>
|
||||||
|
<w:lang w:val="en-GB"/>
|
||||||
|
</w:rPr>
|
||||||
|
<w:t>.</w:t>
|
||||||
|
</w:r>
|
||||||
|
</w:p>
|
||||||
|
<w:p w14:paraId="330D1954" w14:textId="0AB1D347" w:rsidR="00156568" w:rsidRDefault="00156568">
|
||||||
|
<w:pPr>
|
||||||
|
<w:rPr>
|
||||||
|
<w:rFonts w:ascii="Calibri" w:hAnsi="Calibri" w:cs="Calibri"/>
|
||||||
|
<w:lang w:val="en-GB"/>
|
||||||
|
</w:rPr>
|
||||||
|
</w:pPr>
|
||||||
|
' . $toFind . '
|
||||||
|
</w:p>
|
||||||
|
</w:document>';
|
||||||
|
|
||||||
|
$templateProcessor = new TestableTemplateProcesor($mainPart);
|
||||||
|
$position = $templateProcessor->findContainingXmlBlockForMacro('${title}', 'w:r');
|
||||||
|
|
||||||
|
$this->assertEquals($toFind, $templateProcessor->getSlice($position['start'], $position['end']));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testShouldReturnFalseIfXmlBlockNotFound()
|
||||||
|
{
|
||||||
|
$mainPart = '<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||||
|
<w:p>
|
||||||
|
<w:r>
|
||||||
|
<w:rPr>
|
||||||
|
<w:lang w:val="en-GB"/>
|
||||||
|
</w:rPr>
|
||||||
|
<w:t xml:space="preserve">this is my text containing a ${macro}</w:t>
|
||||||
|
</w:r>
|
||||||
|
</w:p>
|
||||||
|
</w:document>';
|
||||||
|
$templateProcessor = new TestableTemplateProcesor($mainPart);
|
||||||
|
|
||||||
|
//non-existing macro
|
||||||
|
$result = $templateProcessor->findContainingXmlBlockForMacro('${fake-macro}', 'w:p');
|
||||||
|
$this->assertFalse($result);
|
||||||
|
|
||||||
|
//existing macro but not inside node looked for
|
||||||
|
$result = $templateProcessor->findContainingXmlBlockForMacro('${macro}', 'w:fake-node');
|
||||||
|
$this->assertFalse($result);
|
||||||
|
|
||||||
|
//existing macro but end tag not found after macro
|
||||||
|
$result = $templateProcessor->findContainingXmlBlockForMacro('${macro}', 'w:rPr');
|
||||||
|
$this->assertFalse($result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,16 @@ class TestableTemplateProcesor extends TemplateProcessor
|
||||||
return parent::fixBrokenMacros($documentPart);
|
return parent::fixBrokenMacros($documentPart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function splitTextIntoTexts($text)
|
||||||
|
{
|
||||||
|
return parent::splitTextIntoTexts($text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function textNeedsSplitting($text)
|
||||||
|
{
|
||||||
|
return parent::textNeedsSplitting($text);
|
||||||
|
}
|
||||||
|
|
||||||
public function getVariablesForPart($documentPartXML)
|
public function getVariablesForPart($documentPartXML)
|
||||||
{
|
{
|
||||||
$documentPartXML = parent::fixBrokenMacros($documentPartXML);
|
$documentPartXML = parent::fixBrokenMacros($documentPartXML);
|
||||||
|
|
@ -42,6 +52,24 @@ class TestableTemplateProcesor extends TemplateProcessor
|
||||||
return parent::getVariablesForPart($documentPartXML);
|
return parent::getVariablesForPart($documentPartXML);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findXmlBlockStart($offset, $blockType)
|
||||||
|
{
|
||||||
|
return parent::findXmlBlockStart($offset, $blockType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findContainingXmlBlockForMacro($macro, $blockType = 'w:p')
|
||||||
|
{
|
||||||
|
return parent::findContainingXmlBlockForMacro($macro, $blockType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSlice($startPosition, $endPosition = 0)
|
||||||
|
{
|
||||||
|
return parent::getSlice($startPosition, $endPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public function getMainPart()
|
public function getMainPart()
|
||||||
{
|
{
|
||||||
return $this->tempDocumentMainPart;
|
return $this->tempDocumentMainPart;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue