PhpWord doesn't replace text

前端 未结 7 1780
闹比i
闹比i 2021-02-14 09:34

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:58

    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:59

    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)
  • 2021-02-14 10:02

    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:02

    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

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

    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 10:05

    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)
提交回复
热议问题