Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace or add an image on a template #260

Open
dmelo opened this issue Jun 3, 2014 · 55 comments
Open

Replace or add an image on a template #260

dmelo opened this issue Jun 3, 2014 · 55 comments

Comments

@dmelo
Copy link

dmelo commented Jun 3, 2014

I could't find how to add or replace an image on a template.

I make an instance of PhpWord and use loadTemplate. I'm able to use setValue but I can't find out how to replace an image or add an image.

Is there a way to add or replace an image inside a template?


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

@ivanlanin
Copy link
Contributor

Hi @dmelo. You can't do that with PHPWord right now. We'll take this as an enhancement request.

@Progi1984
Copy link
Member

+1 : StackOverflow

@ivanlanin ivanlanin added this to the Later milestone Jun 7, 2014
@jdespatis
Copy link

+1

I also need such a feature in order to insert some QRcode or so on in a Word document (and I always use a template as a rich base document)
Thanks @Progi1984 for external resources, gonna fork PHPWord in order to include it

@jdespatis
Copy link

FYI @dmelo @Progi1984 code in StackOverflow has been updated in order to be compatible with last phpWord (0.11)
Of course, it's just a hack and not a generic way to go (as the name of placeholder has to be known before loading the template), but it helps a lot, waiting for this feature to be fully implemented into phpOffice

jdespatis added a commit to jdespatis/PHPWord that referenced this issue Jun 9, 2014
@ivanlanin
Copy link
Contributor

Thanks @jdespatis. I think one of the simplest generic tool that we can develop is a document parser that can report the structure of a document, e.g. name of images and styles.

@jdespatis
Copy link

+1 @ivanlanin Would be awesome
I'll test it asap it's released

Really not handy the above hack when we have several images in the template... They must all be present and in a specific order
If one is just missing, the indexes of images are wrong

@dmelo
Copy link
Author

dmelo commented Jun 9, 2014

Thank you. I will test it soon.

@ivanlanin
Copy link
Contributor

I'm working on addChart feature now. I'll work something like this after that.

@jkeruzec
Copy link

Hi,

I'm interested in that function too ! For the moment i'am using the hack proposed on StackOverflow, but it forces you to add the right number of (different) images in your template .....

@Progi1984
Copy link
Member

@ivanlanin For the addChart, may be you can see from the side of phpoffice/phppowerpoint

@ivanlanin
Copy link
Contributor

@Progi1984 Thanks for the direction. Very useful. There's an increase in my daily workload, so the progress is slow :)

@viomedia-ru
Copy link

+1
Waiting for this enhancement!

@jcwatson11
Copy link

+1 Waiting for this enhancement too! Would be great to be able to add (or replace) images of different sizes in a template.

@EricSarmany
Copy link

+1
It would be very helpful to be able to use setValue to populate an image placeholder/search-pattern.

@meezaan
Copy link

meezaan commented Sep 10, 2014

+1

Perhaps setValue can just expect a $section object and if that contains an image only it can use that?

@cbloss
Copy link

cbloss commented Nov 14, 2014

I am trying to use the hack in StackOverflow, but I think I am misunderstanding how to use it. I am trying to use an image that was in my documents. Is that not right? Here's the function call:

First sig2.png is what the name of the image I inserted into the Word document. Second param is where the new sig image is located:
$document->setImageValue('sig2.png', public_path('docs/sig2.png'));

NEVERMIND! Saw a note somewhere to open it up in WinRAR to see what the actual image name is.

@barryvdh
Copy link

I'm also looking for this. Doesn't have to replace an image, could just be adding an image.

A kind of hacky way is to load the template, merge it, save it as new document and then load it again as a regular document and add an image to a section.
But this does seem to screw up the other images (I can only add one) and can't really decide where to put the image.

Is there some way to tell PhpWord to insert it into some specific section/table or anything, without screwing up other pictures?

@reinaldorauch
Copy link

+1 in this. It would save a lot of work

@kasinau
Copy link

kasinau commented Jun 5, 2015

+1, this functionality will be very handy

@kasinau
Copy link

kasinau commented Jun 5, 2015

I tried to use the hacks provided on stackoverflow, but first problem appears when calling "addFile" method, ZipArchive dont have it defined, I tried to call "pclzipAddFile" method instead, but here goes the seccond problem, a fatal error that the "add" method is not defined in "pclzipAddFile" method

@rfulcoli
Copy link

I'm trying to add an image to my template but when I open the document I receive an error message. Someone has a simple solution?

@kasinau
Copy link

kasinau commented Jun 12, 2015

As I know, adding images to templates isn't possible yet, you can set only string values. You can add images to an existing document by opening it first, the error may occur when you try to open the document but the file doesn't exist or you have no rights to do this action, check this because no error is thrown in this case, just the document isn't loaded

@OAFCROB
Copy link

OAFCROB commented Jun 29, 2015

The stackoverflow solution is all well and good if you're replacing a single image, but what if you need to repeat a row of data each with an image in?

I've been trying to do this simply buy using ZipArchive when I stumbled on this and thought I found the holy grail with the addImage method. However, as just like everyone else here I'm stuck when it comes to a template. I know the code to output an image into a word docx manualy, I know how to add an image, I just can't get the word/_rels?document.xml.rels to update in code.

So my next train of thought has been is it possible to load a docx file and open it in the standard PHP word and manually do the find and replacing for the template tags?

So far I've got
$phpWord = \PhpOffice\PhpWord\IOFactory::load('template.docx');
$sections = $phpWord->getSections();

@OAFCROB
Copy link

OAFCROB commented Jun 29, 2015

Okay, after spending the day doing this. I have now hacked together a solution which might help others, but please note I needed to output images on row data.

Basically this is what I'm doing adding a image tag to the template testPhoto which on a row would be testPhoto#1 and testPhoto#2. I pass in the photo name for the ZipArchive and the image source.

$templateProcessor->setImageValue('testPhoto#2', 'image4.png', 'my-image.jpg');

I then force in the word coding which isn't pretty but work, on the tag replacement, add the image to the folder and then recreate the word/_rels/document.xml.rels file on each call.

If I've done the pull request correctly it should be found at:

#564

Hope this leads someone else who has more time than me to come with a better solution.

OAFCROB added a commit to OAFCROB/PHPWord that referenced this issue Jun 29, 2015
A quick hack solution I came up with today. This isn't pretty in the
slightest and is my first pull request so apologise if I've done it
wrong. I normally work on BitBucket.

Example Usage
------------------

$templateProcessor = new
\PhpOffice\PhpWord\TemplateProcessor('C:\Users\Robert\Desktop\sample.docx');

$templateProcessor->cloneRow('rowValue', 10);
$templateProcessor->setValue('test#1', htmlspecialchars('Text goes
here'));
$templateProcessor->setImageValue('testPhoto#1', 'image3.png',
'my-other-image.jpg');
$templateProcessor->setValue('test#2', htmlspecialchars('Text2 goes
here'));
$templateProcessor->setImageValue('testPhoto#2', 'image4.png',
my-image.jpg');
@jcwatson11
Copy link

Wow! This looks like it could work. My solution was to just re-write the entire document without a template, which wouldn't work if my document wasn't dead simple. I haven't tried it yet, but it looks like you may have cracked it! Great work!

@emirpolo
Copy link

@fishwolf
Copy link

Thanks,
i understand what is the problem, you use a old PhpWord (Beta 0.6.3, 08.07.2011)
I use the last version,
can you update?
I did not succeed, after the document is corrupt

@fishwolf
Copy link

fishwolf commented Oct 8, 2015

i have discovery that the second image load a Content typel xlm file empty

$types = $this->zipClass->getFromName('[Content_Types].xml');

any suggestion?

@mansonkibe
Copy link

After having three days of trying to figure out why when i download a document from drive using google Drive APIs and trying to manipulate the replace image doesn't work i figured out you have to change the picture XML since using the provide solution won't work if your app download the docs from drive and then push back the document to drive without user interaction with office application

First google docs creates this xml

<w:t xml:space="preserve"> instead of <w:t>
public function replaceStrToImg( $strKey, $arrImgPath ){
//289x108
$strKey = '${'.$strKey.'}';
$relationTmpl = '';
$imgTmpl = '<w:pict><v:shape type="#_x0000_t75" style="width:WIDpx;height:HEIpx"><v:imagedata r:id="RID" o:title=""/>/v:shape/w:pict';
$typeTmpl = ' ';
$toAdd = $toAddImg = $toAddType = '';
$aSearch = array('RID', 'IMG');
$aSearchType = array('IMG', 'EXT');

   // var_dump($arrImgPath);die();
    foreach($arrImgPath as $img){
        $ext = explode('.', $img['img']);
        $imgExt = array_pop( $ext );
        if( in_array($imgExt, array('jpg', 'JPG') ) )
            $imgExt = 'jpeg';
        $imgName = 'img' . $this->_countRels . '.' . $imgExt;
        $rid = 'rId' . $this->_countRels++;

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

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

        $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->_documentXML = str_replace('<w:t xml:space="preserve">' . $strKey . '</w:t>', $this->addimage($rid,$imgName), $this->_documentXML);
    $this->_types       = str_replace('</Types>', $toAddType, $this->_types) . '</Types>';
    $this->_rels        = str_replace('</Relationships>', $toAdd, $this->_rels) . '</Relationships>';
}

then alternative to add

<w:pict><v:shape type="#_x0000_t75" style="width:WIDpx;height:HEIpx">

function addimage($rid,$imgName){
    return $xmlformat='<w:drawing>
          <wp:inline distT="0" distB="0" distL="0" distR="0">
            <a:graphic xmlns:a="http:https://schemas.openxmlformats.org/drawingml/2006/main">
              <a:graphicData uri="http:https://schemas.openxmlformats.org/drawingml/2006/picture">
                <pic:pic xmlns:pic="http:https://schemas.openxmlformats.org/drawingml/2006/picture">
                  <pic:nvPicPr>
                    <pic:cNvPr id="0" name="'.$imgName.'"/>
                    <pic:cNvPicPr/>
                  </pic:nvPicPr>
                  <pic:blipFill>
                    <a:blip r:embed="'.$rid.'"/>
                    <a:stretch>
                      <a:fillRect/>
                    </a:stretch>
                  </pic:blipFill>
                  <pic:spPr>
                    <a:xfrm>
                      <a:off x="0" y="0"/>
                      <a:ext cx="5943600" cy="3717290"/>
                    </a:xfrm>
                    <a:prstGeom prst="rect">
                      <a:avLst/>
                    </a:prstGeom>
                  </pic:spPr>
                </pic:pic>
              </a:graphicData>
            </a:graphic>
          </wp:inline>
        </w:drawing>';
}

@OAFCROB
Copy link

OAFCROB commented Nov 10, 2015

@mansonkibe glad you got it to work, I was meaning to comment but I forgot sorry. Your solution looks very similar to my earlier comment, see OAFCROB@ca52b09 I found that I had to also fork the branch for my particular needs

@grepollo
Copy link

this would also be a good feature that my app needed.. any updates on this? thanks.

1 similar comment
@rensi4rn
Copy link

this would also be a good feature that my app needed.. any updates on this? thanks.

@TimRutte
Copy link

TimRutte commented Apr 19, 2016

I really need this too!!

@AcademyLime
Copy link

+1

@jbriscoenc
Copy link

jbriscoenc commented Jul 19, 2016

I compiled a few different ideas for the template.php... It has a better setValue function and replaceStrToImg.

I had trouble with the original not recognizing some tags and also replacing a tag with an image didn't work correctly so I used a modified version of setValue to better find and replace the tags using preg_match and also with the replace StrToImg.... Hope this helps as much as it has me....

`class PHPWord_Template {

/**
* ZipArchive
*
* @var ZipArchive
*/
private $_objZip;

/**
* Temporary Filename
*
* @var string
*/
private $_tempFileName;

/**
* Document XML
*
* @var string
*/
private $_documentXML;

private $_header1XML;
private $_footer1XML;
private $_rels;
private $_types;
private $_countRels;

/**
* Create a new Template Object
*
* @param string $strFilename
*/
public function __construct($strFilename) {
    $path = dirname($strFilename);
    $this->_tempFileName = $path.DIRECTORY_SEPARATOR.time().'.docx';

    copy($strFilename, $this->_tempFileName); // Copy the source File to the temp File

    $this->_objZip = new ZipArchive();
    $this->_objZip->open($this->_tempFileName);

    $this->_documentXML = $this->_objZip->getFromName('word/document.xml');
             $this->_header1XML  = $this->_objZip->getFromName('word/header1.xml'); // Custom code by Matt Bowden (blenderstyle) 04/12/2011
    $this->_footer1XML  = $this->_objZip->getFromName('word/footer1.xml'); // Custom code by Matt Bowden (blenderstyle) 04/12/2011
    $this->_rels        = $this->_objZip->getFromName('word/_rels/document.xml.rels'); #erap 07/07/2015
    $this->_types       = $this->_objZip->getFromName('[Content_Types].xml'); #erap 07/07/2015
    $this->_countRels   = substr_count($this->_rels, 'Relationship') - 1; #erap 07/07/2015
}




/**
* Set a Template value
*
* @param mixed $search
* @param mixed $replace
*/
public function setValue($search, $replace, $limit=-1) {
        $replace = preg_replace('~\R~u', '</w:t><w:br/><w:t>', $replace);

    if(substr($search, 0, 1) !== '{' && substr($search, -1) !== '}') {
        $search = '{'.$search.'}';
    }
    preg_match_all('/\{[^}]+\}/', $this->_documentXML, $matches);
    foreach ($matches[0] as $k => $match) {
        $no_tag = strip_tags($match);
        if ($no_tag == $search) {
            $match = '{'.$match.'}';
            $this->_documentXML = preg_replace($match, $replace, $this->_documentXML, $limit);
                             $this->_header1XML = preg_replace($match, $replace, $this->_header1XML);
            if ($limit == 1) {
                break;
            }           
        }
    }

            preg_match_all('/\{[^}]+\}/', $this->_header1XML, $matches);
    foreach ($matches[0] as $k => $match) {
        $no_tag = strip_tags($match);
        if ($no_tag == $search) {
            $match = '{'.$match.'}';
                             $this->_header1XML = preg_replace($match, $replace, $this->_header1XML);
            if ($limit == 1) {
                break;
            }           
        }
    }



           //  $this->_header1XML = str_replace($search, $replace, $this->_header1XML); // Custom code by Matt Bowden (blenderstyle) 04/12/2011
    $this->_footer1XML = str_replace($search, $replace, $this->_footer1XML); // Custom code by Matt Bowden (blenderstyle) 04/12/2011

}

/**
* Save Template
*
* @param string $strFilename
*/
public function save($strFilename) {
    if(file_exists($strFilename)) {
        unlink($strFilename);
    }

    $this->_objZip->addFromString('word/document.xml', $this->_documentXML);
            $this->_objZip->addFromString('word/header1.xml', $this->_header1XML); // Custom code by Matt Bowden (blenderstyle) 04/12/2011
    $this->_objZip->addFromString('word/footer1.xml', $this->_footer1XML); // Custom code by Matt Bowden (blenderstyle) 04/12/2011
    $this->_objZip->addFromString('word/_rels/document.xml.rels', $this->_rels); #erap 07/07/2015
    $this->_objZip->addFromString('[Content_Types].xml', $this->_types); #erap 07/07/2015

    // Close zip file
    if($this->_objZip->close() === false) {
        throw new Exception('Could not close zip file.');
    }

    rename($this->_tempFileName, $strFilename);
}


     public function replaceImage($path, $imageName) {
    $this->_objZip->deleteName('word/media/' . $imageName);
    $this->_objZip->addFile($path, 'word/media/' . $imageName);
}

public function replaceStrToImg( $strKey, $arrImgPath ){
    //289x108
      $search = $strKey;
    $strKey = '{'.$strKey.'}';

    $relationTmpl = '<Relationship Id="RID" Type="http:https://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>';
    $typeTmpl = ' <Override PartName="/word/media/IMG" ContentType="image/EXT"/>';
    $toAdd = $toAddImg = $toAddType = '';
    $aSearch = array('RID', 'IMG');
    $aSearchType = array('IMG', 'EXT');

    foreach($arrImgPath as $img){
        $imgExt = array_pop( explode('.', $img['img']) );
        if( in_array($imgExt, array('jpg', 'JPG') ) )
            $imgExt = 'jpeg';
        $imgName = 'img' . $this->_countRels . '.' . $imgExt;
        $rid = 'rId' . $this->_countRels++;

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

        if( isset($img['width']) ){
            $w = $img['width'];
            $h = $img['height'];

        }
        else{
            $w = 289;
            $h = 108;
           // $w=150;
           // $h=35;
        }

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


     if(substr($search, 0, 1) !== '{' && substr($search, -1) !== '}') {
        $search = '{'.$search.'}';
    }

        preg_match_all('/\{[^}]+\}/', $this->_documentXML, $matches);
    foreach ($matches[0] as $k => $match) 
                {
        $no_tag = strip_tags($match);
        if ($no_tag == $search)
                        {
            $match = '{'.$match.'}';
            $this->_documentXML = preg_replace($match, $toAddImg, $this->_documentXML);


             }
         }


  //  $this->_documentXML = str_replace('<w:t>' . $strKey . '</w:t>', $toAddImg, $this->_documentXML);
    $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
    );
}



/**
* Clone Rows in tables
*
* @param string $search
* @param array $data
*/
public function cloneRow($search, $data=array()) {      
    // remove ooxml-tags inside pattern             
    foreach ($data as $nn => $fieldset) {
        foreach ($fieldset as $field => $val) {
            $key = '{'.$search.'.'.$field.'}';
            $this->setValue($key, $key, 1);
        }
    }
    // how many clons we need
    $numberOfClones = 0;
    if (is_array($data)) {
        foreach ($data as $colName => $dataArr) {
            if (is_array($dataArr)) {
                $c = count($dataArr);
                if ($c > $numberOfClones)
                    $numberOfClones = $c;
            }
        }
    }
    if ($numberOfClones > 0) {
        // read document as XML
        $xml = DOMDocument::loadXML($this->_documentXML, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING);

        // search for tables
        $tables = $xml->getElementsByTagName('tbl');
        foreach ($tables as $table) {
            $text = $table->textContent;
            // search for pattern. Like {TBL1.
            if (mb_strpos($text, '{'.$search.'.') !== false) {
                // search row for clone
                $patterns = array();
                $rows = $table->getElementsByTagName('tr');
                $isUpdate = false;
                $isFind = false;
                foreach ($rows as $row) {
                    $text = $row->textContent;
                    $TextWithTags = $xml->saveXML($row);
                    if (
                        mb_strpos($text, '{'.$search.'.') !== false // Pattern found in this row
                        OR
                        (mb_strpos($TextWithTags, '<w:vMerge/>') !== false AND $isFind) // This row is merged with upper row (Upper row have pattern)
                    )
                    {
                        // This row need to clone
                        $patterns[] = $row->cloneNode(true);
                        $isFind = true;
                    } else {
                        // This row don't have any patterns. It's table header or footer
                        if (!$isUpdate and $isFind) {
                            // This is table footer
                            // Insert new rows before footer                                
                            $this->InsertNewRows($table, $patterns, $row, $numberOfClones);
                            $isUpdate = true;
                        }
                    }
                }
                // if table without footer                  
                if (!$isUpdate and $isFind) {
                    $this->InsertNewRows($table, $patterns, $row, $numberOfClones);
                }
            }
        }
        // save document
        $res_string = $xml->saveXML();
        $this->_documentXML = $res_string;

        // parsing data
        foreach ($data as $colName => $dataArr) {
            $pattern = '{' . $search . '.' . $colName . '}';
            foreach ($dataArr as $value) {
                $this->setValue($pattern, $value, 1);
            }
        }
    }
}

/**
* Insert new rows in table
*
* @param object &$table
* @param object $patterns
* @param object $row
* @param int $numberOfClones
*/
protected function InsertNewRows(&$table, $patterns, $row, $numberOfClones) {
    for ($i = 1; $i < $numberOfClones; $i++) {
        foreach ($patterns as $pattern) {
            $new_row = $pattern->cloneNode(true);
            $table->insertBefore($new_row, $row);
        }
    }
}

}`

@grindhold
Copy link

grindhold commented Aug 19, 2016

I'd also vote for $template->setValueHtml('placeholder','<p><b>ohai</b> <i>world</i>')

@levieraf
Copy link

levieraf commented Feb 8, 2017

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 PhpOffice\PhpWord\TemplateProcessor add replace the above function for this:


    public function setImg($strKey, $img) {
        $strKey       = '${' . $strKey . '}';
        $relationTmpl = '<Relationship Id="RID" Type="http:https://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:https://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic xmlns:a="http:https://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http:https://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http:https://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:https://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.

@QaisarMahmood
Copy link

QaisarMahmood commented Mar 11, 2017

any update related to this topic add image in template?
thanks

@levieraf
Copy link

No, this is the last one at the moment, keep in mind that that does not support replace macros from header and footer. In me case on the body is ok

@CodeJason
Copy link

In case anyone comes here looking for a solution to insert the images into the header or footer, I've created a small repo here with the template processor file that can do it.
There's a few things to pick up on, but should be fine just to replace the file and go.

@reina3596
Copy link

@CodeJason you are my Hero¡¡¡¡ jajaj Looking for this solution for a long time. You really save my LIVE.

Thank You Very Much...¡¡¡¡

GOOD JOB

@levieraf
Copy link

(Y)

@calojad
Copy link

calojad commented Oct 27, 2017

@CodeJason Muchas Gracias, thank you very much =)

@cbloss
Copy link

cbloss commented Oct 27, 2017

Thank you @CodeJason !!! Appreciate it! 👍

@eliascolares
Copy link

@CodeJason wow man! Really thanks! works like a charm.

@ydakilux
Copy link

@CodeJason : Thanks a million. Works. You saved me days of works
Did you contact guys from PHPOffice/PHPWord to merge with them ?

@PaolaCuadros1
Copy link

@CodeJason Thanks you.

@lauhaixx
Copy link

still has problem for image style. I want this image stay in background or foreground, not inline.
Such as a stamp picture or signature.
I need to know the position of tags, then put this picture near around.

@PaulliDev
Copy link

PaulliDev commented May 4, 2018

@CodeJason Im trying to make this work with v0.14.0 but instead of image I'm getting just white space in size of the image or grey box with red x and message 'This image cannot currently be displayed'. Any ideas?

@sebgam
Copy link

sebgam commented Jan 17, 2019

Gracias @CodeJason gaste horas buscando la solucion hasta que llegue a tu comentario

@dreagor
Copy link

dreagor commented Jun 22, 2020

Could it be added to the main project?

@github-actions github-actions bot added the Stale label Nov 18, 2022
@Progi1984 Progi1984 removed the Stale label Nov 18, 2022
@PHPOffice PHPOffice deleted a comment from github-actions bot Sep 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests