PhpWord doesn't replace text

前端 未结 7 1475
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-02-14 09:10

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         


        
相关标签:
7条回答
  • 2021-02-14 09:45

    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>
    
    0 讨论(0)
  • 2021-02-14 09:47

    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.

    0 讨论(0)
  • 2021-02-14 09:49

    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');
    
    0 讨论(0)
  • 2021-02-14 09:53

    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

    0 讨论(0)
  • 2021-02-14 10:00

    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.

    0 讨论(0)
  • 2021-02-14 10:08

    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');
    
    0 讨论(0)
提交回复
热议问题