How to add/set images on PHPOffice/PHPWord Template?

Following code is the updated version of the one from TotPeRo (thanks again for your code!), for last phpOffice (0.11) that has evolved a little

/**
 * Set a new image
 *
 * @param string $search
 * @param string $replace
 */
public function setImageValue($search, $replace)
{
    // Sanity check
    if (!file_exists($replace))
    {
        return;
    }

    // Delete current image
    $this->zipClass->deleteName('word/media/' . $search);

    // Add a new one
    $this->zipClass->addFile($replace, 'word/media/' . $search);
}

Can be called with:

$document->setImageValue('image1.jpg', 'my_image.jpg');
karolkarp

If you want to add, remove or replace image in template.docx you can add this to your TemplateProcessor.php file, (it works for me with PhpWord 0.14.0 version):

1.

// add this in the class
protected $_rels;
protected $_types;

public function __construct($documentTemplate){
    // add this line to this function
    $this->_countRels=100; 
}

2.

public function save()
{
    //add this snippet to this function after $this->zipClass->addFromString('word/document.xml', $this->tempDocumentMainPart);
    if($this->_rels!=""){
        $this->zipClass->addFromString('word/_rels/document.xml.rels', $this->_rels);
    }
    if($this->_types!=""){
        $this->zipClass->addFromString('[Content_Types].xml', $this->_types);
    }
}

3. Add this function too:

public function setImg( $strKey, $img){
    $strKey = '${'.$strKey.'}';
    $relationTmpl = '<Relationship Id="RID" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/IMG"/>';

    $imgTmpl = '<w:pict><v:shape type="#_x0000_t75" style="width:WIDpx;height:HEIpx"><v:imagedata r:id="RID" o:title=""/></v:shape></w:pict>';

    $toAdd = $toAddImg = $toAddType = '';
    $aSearch = array('RID', 'IMG');
    $aSearchType = array('IMG', 'EXT');
    $countrels=$this->_countRels++;
    //I'm work for jpg files, if you are working with other images types -> Write conditions here
    $imgExt = 'jpg';
    $imgName = 'img' . $countrels . '.' . $imgExt;

    $this->zipClass->deleteName('word/media/' . $imgName);
    $this->zipClass->addFile($img['src'], 'word/media/' . $imgName);

    $typeTmpl = '<Override PartName="/word/media/'.$imgName.'" ContentType="image/EXT"/>';


    $rid = 'rId' . $countrels;
    $countrels++;
    list($w,$h) = getimagesize($img['src']);

    if(isset($img['swh'])) //Image proportionally larger side
    {
        if($w<=$h)
        {
            $ht=(int)$img['swh'];
            $ot=$w/$h;
            $wh=(int)$img['swh']*$ot;
            $wh=round($wh);
        }
        if($w>=$h)
        {
            $wh=(int)$img['swh'];
            $ot=$h/$w;
            $ht=(int)$img['swh']*$ot;
            $ht=round($ht);
        }
        $w=$wh;
        $h=$ht;
    }

    if(isset($img['size']))
    {
        $w = $img['size'][0];
        $h = $img['size'][1];           
    }

    $toAddImg .= str_replace(array('RID', 'WID', 'HEI'), array($rid, $w, $h), $imgTmpl) ;
    if(isset($img['dataImg']))
    {
        $toAddImg.='<w:br/><w:t>'.$this->limpiarString($img['dataImg']).'</w:t><w:br/>';
    }

    $aReplace = array($imgName, $imgExt);
    $toAddType .= str_replace($aSearchType, $aReplace, $typeTmpl) ;

    $aReplace = array($rid, $imgName);
    $toAdd .= str_replace($aSearch, $aReplace, $relationTmpl);


    $this->tempDocumentMainPart=str_replace('<w:t>' . $strKey . '</w:t>', $toAddImg, $this->tempDocumentMainPart);
    //print $this->tempDocumentMainPart;

    if($this->_rels=="")
    {
        $this->_rels=$this->zipClass->getFromName('word/_rels/document.xml.rels');
        $this->_types=$this->zipClass->getFromName('[Content_Types].xml');
    }

    $this->_types = str_replace('</Types>', $toAddType, $this->_types) . '</Types>';
    $this->_rels = str_replace('</Relationships>', $toAdd, $this->_rels) . '</Relationships>';
}

4. And this:

function limpiarString($str) {
    return str_replace(
            array('&', '<', '>', "\n"), 
            array('&amp;', '&lt;', '&gt;', "\n" . '<w:br/>'), 
            $str
    );
}

5. How to use:

//open your template  
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('files/template.docx');
//add image to selector
$templateProcessor->setImg('selector',array('src' => 'image.jpg','swh'=>'200', 'size'=>array(0=>$width, 1=>$height));
// You can also clone row if you need
//$templateProcessor->cloneRow('NAME_IN_TEMPLATE', NUMBER_OF_TABLE_RECORDS);
$templateProcessor->cloneRow('SELECTOR', 4);
//save
header("Content-Disposition: attachment; filename='helloWord.docx'");
$templateProcessor->saveAs('php://output');

his is pretty much untested. but this is working for me (although mine is slightly different):

add the following function to PHPWord/Template.php :

 public function save_image($id,$filepath,&$document=null) { 
        if(file_exists($filepath))
        {
            $this->_objZip->deleteName('word/media/'.$id);          
            $this->_objZip->addFile ($filepath,'word/media/'.$id);
            //$document->setValue($id.'::width', "300px");
            //$document->setValue($id.'::height', "300px");
        }   
    }

create a document with an actual image to be used as a place holder (this solution don't allow setting the with & height of the image or multiple extensions). unzip the the documnt and check for the file name in word/media folder. use this file name as the $id for the save_image function.

you can now use:

$document->save_image('image1',$image_path,$document);

I created some functions to extend Jerome's solution. The problem was that in order to change the picture we had to know it's reference name in the docx file. This could be difficult to find out for an average user (you have to "unZip" the docx, and find your picture manually).

My solution makes it possible to reference pictures by alt text (it needs to be in usual format: ${abc} ) so one doesn't have to know how word names the placeholder picture, the variable is enough. Here's a link about how to add alt texts: http://accessproject.colostate.edu/udl/modules/word/tut_alt_text.php?display=pg_2

I only modified TemplateProcessor.php

First add this to the class:

/**
     * Content of document rels (in XML format) of the temporary document.
     *
     * @var string
     */
    private $temporaryDocumentRels; 

The constructor function (public function __construct($documentTemplate)) needs to be extended at the end. Add this:

$this->temporaryDocumentRels = $this->zipClass->getFromName('word/_rels/document.xml.rels'); 

Now you can read stuff from document.xml.rels by using $this->temporaryDocumentRels

Keep Jerome's code:

/**
     * Set a new image
     *
     * @param string $search
     * @param string $replace
     */

    public function setImageValue($search, $replace){
        // Sanity check
        if (!file_exists($replace))
        {
            return;
        }

        // Delete current image
        $this->zipClass->deleteName('word/media/' . $search);

        // Add a new one
        $this->zipClass->addFile($replace, 'word/media/' . $search);
    }

This function returns with the rId of the image you labeled:

/**
     * Search for the labeled image's rId
     *
     * @param string $search
     */

    public function seachImagerId($search){
        if (substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
            $search = '${' . $search . '}';
        }
        $tagPos = strpos($this->temporaryDocumentMainPart, $search);
        $rIdStart = strpos($this->temporaryDocumentMainPart, 'r:embed="',$tagPos)+9;    
        $rId=strstr(substr($this->temporaryDocumentMainPart, $rIdStart),'"', true);
        return $rId;
    }

And this returns the images filename, if you know it's rId:

/**
     * Get img filename with it's rId
     *
     * @param string $rId
     */

    public function getImgFileName($rId){
        $tagPos = strpos($this->temporaryDocumentRels, $rId);
        $fileNameStart = strpos($this->temporaryDocumentRels, 'Target="media/',$tagPos)+14;
        $fileName=strstr(substr($this->temporaryDocumentRels, $fileNameStart),'"', true);
        return $fileName;
    }

The modifications in TemplateProcessor.php are done.

Now you can replace the image by calling this:

$templateProcessor->setImageValue($templateProcessor->getImgFileName($templateProcessor->seachImagerId("abc")),$replace);

You can also create a functionin TemplateProcessor.php that calls it like:

public function setImageValueAlt($searchAlt, $replace){
        $this->setImageValue($this->getImgFileName($this->seachImagerId($searchAlt)),$replace);
    }
labsevs labsev

I was looking for a solution to add an image. I read a lot of articles, I only found suitable solutions for older versions. Changing and finalized the decision to get the code.

Let us proceed to change the file TemplateProcessor.php

public function __construct($documentTemplate)
{
//add to this function

$this->_countRels=100; //start id for relationship between image and document.xml
}

public function save()
{
//add to this function after $this->zipClass->addFromString('word/document.xml', $this->tempDocumentMainPart);

if($this->_rels!="")
{
    $this->zipClass->addFromString('word/_rels/document.xml.rels', $this->_rels);
}
if($this->_types!="")
{
    $this->zipClass->addFromString('[Content_Types].xml', $this->_types);
}
}

//add function

public function setImg( $strKey, $img){
        $strKey = '${'.$strKey.'}';
        $relationTmpl = '<Relationship Id="RID" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/IMG"/>';

        $imgTmpl = '<w:pict><v:shape type="#_x0000_t75" style="width:WIDpx;height:HEIpx"><v:imagedata r:id="RID" o:title=""/></v:shape></w:pict>';

        $toAdd = $toAddImg = $toAddType = '';
        $aSearch = array('RID', 'IMG');
        $aSearchType = array('IMG', 'EXT');
        $countrels=$this->_countRels++;
        //I'm work for jpg files, if you are working with other images types -> Write conditions here
    $imgExt = 'jpg';
        $imgName = 'img' . $countrels . '.' . $imgExt;

            $this->zipClass->deleteName('word/media/' . $imgName);
            $this->zipClass->addFile($img['src'], 'word/media/' . $imgName);

            $typeTmpl = '<Override PartName="/word/media/'.$imgName.'" ContentType="image/EXT"/>';


            $rid = 'rId' . $countrels;
            $countrels++;
        list($w,$h) = getimagesize($img['src']);

 if(isset($img['swh'])) //Image proportionally larger side
 {
 if($w<=$h)
 {
    $ht=(int)$img['swh'];
    $ot=$w/$h;
    $wh=(int)$img['swh']*$ot;
    $wh=round($wh);
 }
 if($w>=$h)
 {
    $wh=(int)$img['swh'];
    $ot=$h/$w;
    $ht=(int)$img['swh']*$ot;
    $ht=round($ht);
 }
 $w=$wh;
 $h=$ht;
 }

if(isset($img['size']))
{
$w = $img['size'][0];
$h = $img['size'][1];           
}


            $toAddImg .= str_replace(array('RID', 'WID', 'HEI'), array($rid, $w, $h), $imgTmpl) ;
            if(isset($img['dataImg']))
            {
                $toAddImg.='<w:br/><w:t>'.$this->limpiarString($img['dataImg']).'</w:t><w:br/>';
            }

            $aReplace = array($imgName, $imgExt);
            $toAddType .= str_replace($aSearchType, $aReplace, $typeTmpl) ;

            $aReplace = array($rid, $imgName);
            $toAdd .= str_replace($aSearch, $aReplace, $relationTmpl);


        $this->tempDocumentMainPart=str_replace('<w:t>' . $strKey . '</w:t>', $toAddImg, $this->tempDocumentMainPart);
        //print $this->tempDocumentMainPart;



        if($this->_rels=="")
        {
            $this->_rels=$this->zipClass->getFromName('word/_rels/document.xml.rels');
            $this->_types=$this->zipClass->getFromName('[Content_Types].xml');
        }

        $this->_types       = str_replace('</Types>', $toAddType, $this->_types) . '</Types>';
                $this->_rels        = str_replace('</Relationships>', $toAdd, $this->_rels) . '</Relationships>';
}
//add function

function limpiarString($str) {
        return str_replace(
                array('&', '<', '>', "\n"), 
                array('&amp;', '&lt;', '&gt;', "\n" . '<w:br/>'), 
                $str
        );
}
//HOW TO USE???

$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('templ.docx');

//static zone
$templateProcessor->setValue('date', htmlspecialchars(date('d.m.Y G:i:s')));    
//$templateProcessor->cloneRow('NAME_IN_TEMPLATE', NUMBER_OF_TABLE_RECORDS);
$templateProcessor->cloneRow('AVTOR', 3);

//variant 1
//dynamic zone
$templateProcessor->setValue('AVTOR#1', htmlspecialchars('Garry'));
$templateProcessor->setValue('NAME#1', htmlspecialchars('Black Horse'));
$templateProcessor->setValue('SIZES#1', htmlspecialchars('100x300'));

/*$img = array(
        'src' => 'image.jpg',//path
    'swh'=>'350',//Image proportionally larger side
        'size'=>array(580, 280)
);*/
$templateProcessor->setImg('IMGD#1',array('src' => 'image.jpg','swh'=>'250'));

$templateProcessor->setValue('AVTOR#2', htmlspecialchars('Barry'));
$templateProcessor->setValue('NAME#2', htmlspecialchars('White Horse'));
$templateProcessor->setValue('SIZES#2', htmlspecialchars('200x500'));
$templateProcessor->setImg('IMGD#2',array('src' => 'image2.jpg','swh'=>'250'));

$templateProcessor->setValue('AVTOR#3', htmlspecialchars('Backer'));
$templateProcessor->setValue('NAME#3', htmlspecialchars('Another Side'));
$templateProcessor->setValue('SIZES#3', htmlspecialchars('120x430'));
$templateProcessor->setImg('IMGD#3',array('src' => 'image3.jpg','swh'=>'250'));
//variant 2

$templateProcessor->cloneRow('AVTOR', count($output['ID'])); 
        for($i=0;$i<count($output['ID']);$i++)
        {
            $templateProcessor->setValue('AVTOR'.'#'.($i+1), htmlspecialchars($output['AVTOR'][$i]));
            $templateProcessor->setValue('NAME'.'#'.($i+1), htmlspecialchars($output['PNAM'][$i]));
//GetImg($output['ID'][$i]) my function return image path
            $templateProcessor->setImg('IMGD'.'#'.($i+1), array('src'=>GetImg($output['ID'][$i]),'swh'=>'250'));
        }
//Save

$templateProcessor->saveAs('testTemplate.docx');
upbill

modify the name of the *.docx to *.zip

open the zip folder

view the "word/media/****" the imgage name is to replace name

for setImageValue($search, $replace)

$search must be equls $replace OR use $this->zipClass->renameName ($replace , $search);