PHPWord 0.12.1 setImageValue - templateprocessor add image to docx

Created on 20 Jan 2016  路  10Comments  路  Source: PHPOffice/PHPWord

I was looking for a solution to add 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');


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

Most helpful comment

@labsev
I鈥檓 using app (unoconv) with PhpWord on laravel 5. I add an IMG to docx from PHPword TemplateProcesscer then using app unoconv to convert from docx to pdf but IMG is lost on PDF (still on DOCX) file. Could you help review your app is working normaly?
I attach files Docx and pdf in attachment, please help me
Demo.docx
Demo.pdf

All 10 comments

This works for you?

I want to change the header in the same way , you can ??

or the footer

@labsev
I鈥檓 using app (unoconv) with PhpWord on laravel 5. I add an IMG to docx from PHPword TemplateProcesscer then using app unoconv to convert from docx to pdf but IMG is lost on PDF (still on DOCX) file. Could you help review your app is working normaly?
I attach files Docx and pdf in attachment, please help me
Demo.docx
Demo.pdf

@finguer I have the same problem. We can use this update, but when we try to include an image in the header it doesn't do anything :(

Has anybody a solution to fix it????

thankssss xD

Hi guys, I was having an issue loading .docx from google, and I after research for some google does not handler images within shape objects, I did a change replacing the object shape image for a corresponding object image of word, I am happy to share with you my solution:

In your extended class PhpOfficePhpWordTemplateProcessor add replace the above function for this:

    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:drawing><wp:inline distT="0" distB="0" distL="0" distR="0" wp14:anchorId="6E59C072" wp14:editId="50C440CF"><wp:extent cx="WID" cy="HEI"/><wp:effectExtent l="0" t="0" r="12065" b="0"/><wp:docPr id="1" name="signature" descr=""/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="0" name="signature" descr=""/><pic:cNvPicPr><a:picLocks noChangeAspect="1" noChangeArrowheads="1"/></pic:cNvPicPr></pic:nvPicPr><pic:blipFill><a:blip r:embed="RID" cstate="print"><a:extLst><a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}"><a14:useLocalDpi xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" val="0"/></a:ext></a:extLst></a:blip><a:srcRect/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr bwMode="auto"><a:xfrm><a:off x="0" y="0"/><a:ext cx="WID" cy="HEI"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/><a:ln><a:noFill/></a:ln></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing>';

        $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['size'])){
            $w = (int)($img['size'][0] * (3 / 100) * 376653); //px * cm * em
            $h = (int)($img['size'][1] * (3 / 100) * 376653); //px * cm * em
        }

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

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

Now in your template.docx you must to have an macro like this

${macroNameImage}

In your .php action:

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

$image_path = 'your_image.png';

$templateProcessor->setImg('macroNameImage', array(
    'src'  => $image_path,
    'size' => array( 102, 40 ) //px
));

$templateProcessor->saveAs('output.docx');

I hope that this would help you, thanks for share your experiences.

Same problem, dont change image in Header or Footer :(

This works well when using saveAs but if I try and stream the file as a download to the browser, it gets corrupted. Anyone any idea why ?

If I locate the updated.docx on the server and open it in Word it's perfect, but the version sent to the browser won't open as it's corrupt.

I tried some different content-types but no difference. Has anyone got this to work ?

Code is:

$phpWord = new \PhpOffice\PhpWord\TemplateProcessor('c:/temp/test.docx');
        $image_path = 'download.jpg';

    $phpWord->setImg('macroNameImage', array(
      'src'  => $image_path,
      'size' => array( 150, 150 ) //px
    ));
;
    $phpWord->setValue('var1', 'test');
    $filename = "updated.docx";
    $phpWord->saveAs($filename);

     header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename='.$filename);
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Content-Length: ' . filesize($filename));
        flush();
        readfile($filename);

Thanks!

Hmm, I noticed that the docx was corrupt even without using setImg() and I traced the problem to this bit of code from the patch above. With that removed my simple $phpWord->setValue('var1', 'test'); works fine and the doc can be streamed and opened with the code I pasted above. With the below code in place, however, the streamed file is corrupt although can be opened on the server. Weird!. Any idea why that might be and how to fix it ?

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

If I remove this, then crea

I had the problem that my specs for the image replacement weren't sticking. I poured over the code, there's clearly something wrong so I made this simple shim to fix it.

search and replace this line of code:
foreach ($varsToReplace as $varNameWithArgs) {
with these three lines:
$tempshit = implode(':',$replace);
foreach ($varsToReplace as $varNameWithArgs) {
$varInlineArgs = $this->getImageArgs($tempshit);
then comment out:
$varInlineArgs = $this->getImageArgs($varNameWithArgs);

That's it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ahmednawazbutt picture ahmednawazbutt  路  3Comments

jt6a74 picture jt6a74  路  5Comments

cyrillkalita picture cyrillkalita  路  6Comments

dwalker109 picture dwalker109  路  6Comments

cedrictailly picture cedrictailly  路  5Comments