I have a docx file and I need to replace some text. This is done inside codeigniter framework; here is the code:
$this->load->library(\'word\');
$te
I found the solution in http://phpword.codeplex.com/workitem/57... the problem seems to be when word generates his own code.
for the solution, just rename extension your template to zip, check in a file named 'document.xml' into 'word' folder, and search your code value... it should be like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 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: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 wp14">
<w:body>
<w:p w:rsidR="00AC49DC" w:rsidRDefault="00243DD6">
<w:r>
<w:t>${test1}</w:t>
</w:r>
</w:p>
<w:p w:rsidR="00243DD6" w:rsidRDefault="00243DD6">
<w:r>
<w:t>${test2}</w:t>
</w:r>
</w:p>
<w:p w:rsidR="00243DD6" w:rsidRDefault="00243DD6">
<w:r>
<w:t>${test3}</w:t>
</w:r>
</w:p>
<w:p w:rsidR="00243DD6" w:rsidRDefault="00243DD6">
<w:r>
<w:t>${test4}</w:t>
</w:r>
</w:p>
<w:p w:rsidR="00243DD6" w:rsidRDefault="00243DD6">
<w:r>
<w:t>${</w:t>
</w:r>
<w:bookmarkStart w:id="0" w:name="_GoBack"/>
<w:bookmarkEnd w:id="0"/>
<w:r>
<w:t>test5}</w:t>
</w:r>
</w:p>
<w:sectPr w:rsidR="00243DD6" w:rsidSect="00C107D0">
<w:pgSz w:w="12240" w:h="15840" w:code="1"/>
<w:pgMar w:top="907" w:right="907" w:bottom="907" w:left="907" w:header="720" w:footer="720" w:gutter="0"/>
<w:cols w:space="720"/>
<w:docGrid w:linePitch="272"/>
</w:sectPr>
</w:body>
</w:document>
It turns out that when you add text to a word file, it sometimes creates extra tags in the underlying xml file. So (pseudo code) ${NAME}
can become <tag1>${</tag1><tag2>NAME</tag2><tag3>}</tag3>
Because of this, phpword can't find your needle.
I'm using a mac with microsoft word and i can just cut all the contents of the word file and paste them again.
Word will than recreate the underlying xml file in which ${NAME} is 1 tag instead of 3.
Just add 2 function to the file "TemplateProcessor.php"
public function setValueAdvanced($search_replace)
{
foreach ($this->tempDocumentHeaders as $index => $headerXML) {
$this->tempDocumentHeaders[$index] = $this->setValueForPartAdvanced($this->tempDocumentHeaders[$index], $search_replace);
}
$this->tempDocumentMainPart = $this->setValueForPartAdvanced($this->tempDocumentMainPart, $search_replace);
foreach ($this->tempDocumentFooters as $index => $headerXML) {
$this->tempDocumentFooters[$index] = $this->setValueForPartAdvanced($this->tempDocumentFooters[$index], $search_replace);
}
}
protected function setValueForPartAdvanced($documentPartXML, $search_replace)
{
$pattern = '/<w:t>(.*?)<\/w:t>/';
$rplStringBeginOffcetsStack = array();
$rplStringEndOffcetsStack = array();
$rplCleanedStrings = array();
$stringsToClean = array();
preg_match_all($pattern, $documentPartXML, $words, PREG_OFFSET_CAPTURE);
$bux_founded = false;
$searching_started = false;
foreach($words[1] as $key_of_words => $word)
{
$exploded_chars = str_split($word[0]);
foreach($exploded_chars as $key_of_chars => $char)
{
if ($bux_founded)
{
if ($searching_started)
{
if ($char == "}")
{
$bux_founded = false;
$searching_started = false;
array_push($rplStringEndOffcetsStack, ($word[1]+mb_strlen($word[0])+6));
}
}
else
{
if ($char == "{")
{
$searching_started = true;
}
else
{
$bux_founded = false;
array_pop($rplStringBeginOffcetsStack);
}
}
}
else
{
if ($char == "$")
{
$bux_founded = true;
array_push($rplStringBeginOffcetsStack, $word[1]-5);
}
}
}
}
for($index=0; $index<count($rplStringEndOffcetsStack); $index++)
{
$string_to_clean = substr($documentPartXML, $rplStringBeginOffcetsStack[$index], ($rplStringEndOffcetsStack[$index]-$rplStringBeginOffcetsStack[$index]));
array_push($stringsToClean, $string_to_clean);
preg_match_all($pattern, $string_to_clean, $words_to_concat);
$cleaned_string = implode("", $words_to_concat[1]);
$cleaned_string = preg_replace('/[${}]+/', '', $cleaned_string);
array_push($rplCleanedStrings, $cleaned_string);
}
for ($index=0; $index<count($rplCleanedStrings); $index++)
{
foreach($search_replace as $key_search => $replace)
{
if ($rplCleanedStrings[$index] == $key_search)
{
$documentPartXML = str_replace($stringsToClean[$index], "<w:t>".$replace."</w:t>", $documentPartXML);
break;
}
}
}
return $documentPartXML;
}
How to use: Use an array as the only parameter of function "setValueAdvanced", where "key" - a word, that we want to replace, and "value" - phrase we want to paste instead. Important: Inside a MS Word file use "${word_to_replace}" to "mark" a word that we want to replace, but key of an array should be "word_to_replace", without "${}"
Example code:
require_once 'PhpWord/Autoloader.php';
use PhpOffice\PhpWord\Autoloader;
use PhpOffice\PhpWord\Settings;
define('CLI', (PHP_SAPI == 'cli') ? true : false);
define('EOL', CLI ? PHP_EOL : '<br />');
define('SCRIPT_FILENAME', basename($_SERVER['SCRIPT_FILENAME'], '.php'));
define('IS_INDEX', SCRIPT_FILENAME == 'index');
Autoloader::register();
Settings::loadConfig();
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('your_file_name.docx');
$search_replace_array = array(
'msword_hello'=>'Hello', #inside a MS Word file ${msword_hello} will change to Hello
'msword_world'=>'World' #${msword_world} will change to World
);
$templateProcessor->setValueAdvanced($search_replace_array);
$templateProcessor->saveAs('your_file_name_changed.docx');
You can use new Template class with new method setValue()
<w:p w:rsidR="00243DD6" w:rsidRDefault="00243DD6">
<w:r>
<w:t>{</w:t>
</w:r>
<w:bookmarkStart w:id="0" w:name="_GoBack"/>
<w:bookmarkEnd w:id="0"/>
<w:r>
<w:t>test5}</w:t>
</w:r>
</w:p>
New method can replace it setValue('test5', 'MyValue');
Download new class: https://github.com/Arisse/PHPWord_CloneRow
here i try and working well,
setValue function does not work because it include the format of word after generate to web. so the text you might replace not found.
the solution is just type your ${replaceKey} to notepad and copy it to your_template.docx it working for me.
notepad make all word format not included to the template so the function setValue can found it as expected.
Hope it Helping you guys.
My solution
function replaceText($element, $variable, $value) {
$text_class = 'PhpOffice\PhpWord\Element\Text';
$table_class = 'PhpOffice\PhpWord\Element\Table';
foreach ($element as $e) {
if (get_class($e) !== $text_class && method_exists($e, 'getElements')) {
replaceText($e->getElements(), $variable, $value);
} elseif (get_class($e) === $text_class && ($match_count = substr_count($e->getText(), $variable))) {
for ($i = 1; $i <= $match_count; $i++) {
$e->setText(str_replace($variable, $value, $e->getText()));
}
} elseif (get_class($e) === $table_class && ($row_count = count($e->getRows()))) {
foreach ($e->getRows() as $row) {
foreach ($row->getCells() as $cell) {
replaceText($cell->getElements(), $variable, $value);
}
}
}
}
}
$path = public_path('test.docx'); // Path to template document. public_path() Laravel function
$objReader = \PhpOffice\PhpWord\IOFactory::createReader('Word2007'); // Here can be 'ODText' = .odt, 'Word2007' = .docx etc.
$docx = $objReader->load($path);
replaceText($docx->getSections(), '${text}', 'This will be in your document'); // '${text}' can be any text like 'Simple text'
$docx->save('test2.docx');