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.
Hi @dmelo. You can't do that with PHPWord right now. We'll take this as an enhancement request.
+1 : StackOverflow
+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
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
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.
+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
Thank you. I will test it soon.
I'm working on addChart feature now. I'll work something like this after that.
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 .....
@ivanlanin For the addChart, may be you can see from the side of phpoffice/phppowerpoint
@Progi1984 Thanks for the direction. Very useful. There's an increase in my daily workload, so the progress is slow :)
+1
Waiting for this enhancement!
+1 Waiting for this enhancement too! Would be great to be able to add (or replace) images of different sizes in a template.
+1
It would be very helpful to be able to use setValue to populate an image placeholder/search-pattern.
+1
Perhaps setValue can just expect a $section object and if that contains an image only it can use that?
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.
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?
+1 in this. It would save a lot of work
+1, this functionality will be very handy
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
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?
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
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();
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:
https://github.com/PHPOffice/PHPWord/pull/564
Hope this leads someone else who has more time than me to come with a better solution.
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!
I've been modifiying my template to actually get some data in and notice that yesterday I had an image in to the template to start with. When I took this out the and tried to populate the template with images from disk I was getting a corruption issue. Putting in the placeholder again solved the problem, this is to do the document.xml.rels and the content_types not knowing about the image types coming in. I've not come up with a solution around this yet apart from having an image in to start with.
@emirpolo have you forked the code base and implemented this? I'm not too sure if the implementation would work for my requirements as typically I have an array like;
$data = [
[
'tag_1' => 'Some data',
'tag_2' => 'Some description',
'img_tag => './some-image.jpg'
],
[
'tag_1' => 'Some data 2',
'tag_2' => 'Some description 2 ',
'img_tag => './some-image-2.jpg'
],
];
But if I can pull the code I can have a look and see if it would work,
this is mi Template.php:
class PHPWord_Template {
private $_objZip;
private $_tempFileName;
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'; // $path doesn't include the trailing slash - Custom code by Matt Bowden (blenderstyle) 04/12/2011
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) {
if (substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
$search = '${' . $search . '}';
}
if (!is_array($replace)) {
$replace = utf8_encode($replace);
}
$this->_documentXML = str_replace($search, $replace, $this->_documentXML);
$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 ){
$strKey = '${'.$strKey.'}';
if( !is_array($arrImgPath) )
$arrImgPath = array($arrImgPath);
$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:289px;height:108px"><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) );
if( in_array($imgExt, array('jpg', 'JPG') ) )
$imgExt = 'jpeg';
$imgName = 'img' . $this->_countRels . '.' . $imgExt;
$rid = 'rId' . $this->_countRels++;
$this->_objZip->addFile($img, 'word/media/' . $imgName);
$toAddImg .= str_replace('RID', $rid, $imgTmpl) ;
$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>' . $strKey . '</w:t>', $toAddImg, $this->_documentXML);
$this->_types = str_replace('</Types>', $toAddType, $this->_types) . '</Types>';
$this->_rels = str_replace('</Relationships>', $toAdd, $this->_rels) . '</Relationships>';
}
}
I add function to TemplateProcessor class
and replace:
_objZip -> zipClass
_documentXML ->temporaryDocumentMainPart
but doesn't work if you set 2 images with different tags
$document->replaceStrToImg('AreaImg1', $arrImagenes1);
$document->replaceStrToImg('AreaImg2', $arrImagenes2);
The document is corrupt
Thanks
this is a full example
https://drive.google.com/open?id=0B01uU2P1KMbBakkycTEyMW54M0U
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
i have discovery that the second image load a Content typel xlm file empty
$types = $this->zipClass->getFromName('[Content_Types].xml');
any suggestion?
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
public function replaceStrToImg( $strKey, $arrImgPath ){
//289x108
$strKey = '${'.$strKey.'}';
$relationTmpl = '
$imgTmpl = '
$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
function addimage($rid,$imgName){
return $xmlformat='<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0">
<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="'.$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>';
}
@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 https://github.com/OAFCROB/PHPWord/commit/ca52b0997a333e24a2ffe21eb93a9147f657dcf4 I found that I had to also fork the branch for my particular needs
this would also be a good feature that my app needed.. any updates on this? thanks.
this would also be a good feature that my app needed.. any updates on this? thanks.
I really need this too!!
+1
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://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('&', '<', '>', "\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);
}
}
}
}`
I'd also vote for $template->setValueHtml('placeholder','<p><b>ohai</b> <i>world</i>')
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://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.
any update related to this topic add image in template?
thanks
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
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.
@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
(Y)
@CodeJason Muchas Gracias, thank you very much =)
Thank you @CodeJason !!! Appreciate it! 馃憤
@CodeJason wow man! Really thanks! works like a charm.
@CodeJason : Thanks a million. Works. You saved me days of works
Did you contact guys from PHPOffice/PHPWord to merge with them ?
@CodeJason Thanks you.
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.
@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?
Gracias @CodeJason gaste horas buscando la solucion hasta que llegue a tu comentario
Could it be added to the main project?
Most helpful comment
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.