PhpWord doesn't replace text

一世执手 提交于 2020-07-05 02:43:06

问题


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');       
$template = $this->word->loadTemplate($_SERVER['DOCUMENT_ROOT'].'/doc/assets/doc3.docx');
$template->setValue('replacename', 'new');
$template->save($_SERVER['DOCUMENT_ROOT'].'/doc/assets/helloWorld.docx');

When I open the new file I still get "replacename" instad of "new". "replacename" is formatted with Verdana font, 9pt font size (no underline or bold). Why it doesn't work? Removing ${ } from setValue function (and from doc file) it works


回答1:


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.




回答2:


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>



回答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');



回答4:


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




回答5:


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.




回答6:


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');



回答7:


I created a circular template from the template where I filled out the fields with the required $ {Value} values. I merged and saved the result as a new template. So it accepted the valuables



来源:https://stackoverflow.com/questions/16794294/phpword-doesnt-replace-text

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!