Can I use a template (TemplateProcessor) for odt?
I'm trying to create an odt file using a template, with this code:
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('/home/docs/template.odt');
$templateProcessor->setValue('title', $title);
$templateProcessor->saveAs('/home/docs/'.$titeldoc.'.odt');
however the file is damaged, and nothing has been replaced... I can't find much on using an odf template, does anyone have this working?
Thanks! -Michèlle
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
I'm having the same issue - using LibreOffice 5.2.0.4
TemplateProcessor can only deal with Word 2007 and higher :-/.
@Progi1984 to be closed
@cookiekiller - but it does work, just not properly - which means it can surely be addressed? Or perhaps it just happens to work, but not by design? And if that is the case, surely a warning shuld be issued or an exception thrown?
Scratch that - I apologise. Have used so many different ODT processors that I can't remember which one gave which result. Thst said, TemplateProcessor should still throw an exception for ODTs.
Hello, I'm working on a hack to deal with ODT templates and that I will submit. I have added tests like for Word, but with the documents saved in ODT format. It already work, except for images for now. What I would ask is about the manner to call the processor. For now, I have added a class TemplateProcessorOdt, keeping TemplateProcessor to deal with Word format. I created a class TemplateProcessorCommon which share the common methods and that TemplateProcessor and TemplateProcessorOdt inherits. Is it OK this way? This will leave the responsibility ta call the good class to the caller. Or should I include in TemplateProcessor some checks about file format to call specific methods? By this way, I don't see how to use inheritance.
@papoteur-mga Hi, I'm very interested in your work, because I want to output both docx and odt files with HTML input and using templates in a large documentation project. Hope you will succeed soon... ;-) Kind regards, Knut
Hello, You can have a look here : https://github.com/papoteur-mga/PHPWord/commits/develop
@papoteur-mga Thank you for sharing that.
@knulo If you mean this is fine, I can request a pull.
@papoteur-mga Please give me some time to test... BTW: Would be fine to have an output for PDF, right? ;-)
BTW: Would be fine to have an output for PDF, right? ;-)
Hmm, sorry, I don't understand what PDF has to do. templateProcessor takes a text document and replaces inside it the tags. If PDF is needed, this is to the developer to add a step which transform the generated text document to PDF or something telse. Now I propose templateProcessorOdt which does the job for ODT texts.
@papoteur-mga Was only an idea for getting output of template handeled stuff to PDF. ;-)
How do I instantiate respectively bind TemplateProcessorOdt to the phpWord instance?
At the moment I use $phpWord->loadTemplate(...) but I guess that wouldn't do the job.
What I use is for example:
$template = new \PhpOffice\PhpWord\TemplateProcessorOdt('mytemplate.odt');
$template->setValues($dataArray);
$template->saveAs('myoutput.odt');
This is meant to be used as templateProcessor in https://phpword.readthedocs.io/en/latest/templates-processing.html but replacing with templateProssessorOdt.
@papoteur-mga Thanks. I will discover that tomorrow. Kind regards, Knut
@papoteur-mga After some trouble with cloning the repo (my fault!), I'm now about to test your enhancements. Sorry to inform you that apparently the substitution of complex blocks doesn't work. In my template I have some 'simple' macros (they are replaced) and a block
${htmlblock}
${html}
${/htmlblock}
that is cloned for inserting the HTML input. As a result, only the 'simple' macros are replaced and only one page is created showing the macros. Any suggestions? Thanks in advance and kind regards, Knut
Thanks for your test. This is a part I've not yet dealt with. I come back later.
@papoteur-mga O.K. and good luck!
Hello, I have pushed some improvements. I think this is probably not robust, as I don't know which surprise can be included in a current text. However, blocks are dealt.
@papoteur-mga Thanks! I'll check your improvements next days and report my experience... ;-)
@papoteur-mga I have the following, given the template mentioned above (and there are simple macros in the header and footer section - they were processed with the former version):
$section = new \PhpOffice\PhpWord\Element\Section(1);
\PhpOffice\PhpWord\Shared\Html::addHtml($section, $htmlContent, false, true);
$containers = $section->getElements();
for($i=0; $i<count($containers); $i++) {
$templateProcessor->setComplexBlock('html#'.($i+1), $containers[$i]);
}
This results in one blank page, the block marks are gone, but not filled with the new (desired) content. And also header and footer aren't there anymore.
If I do additionally that after the above:
$textrun = $section->addTextRun(array('pageBreakBefore' => true));
$dim = @getimagesize($mf); // $mf = media file, an image filename
$w = 170; // mm
$h = $w * $dim[1] / $dim[0];
if ($h > 250) {
$h = 250;
$w = $h * $dim[0] / $dim[1];
}
$textrun->addText('Headline for image',
array('name' => 'Arial', 'size' => 14, 'bold' => true),
array('spaceAfter' => 250, 'spaceBefore' => 250));
$textrun->addTextBreak();
$textrun->addText('${img#'.($i+1).'}');
$textrun->addTextBreak();
$textrun->addText('some text '.basename($mf));
$templateProcessor->setComplexBlock('html#'.$i, $textrun);
$templateProcessor->setImageValue('img#'.($i+1),
array('path' => realpath(__DIR__.'/.'.$mf),
'width' => intval($w).'mm',
'height' => intval($h).'mm'));
This results in one page without header and footer and only with one line:
Headline for image${img#579}some text BMMH(5).jpg
So it seems, page breaks and setImageValue aren't handeled yet? Hope that helps you for further investigation and development. Unfortunately at the moment I haven't the time to assist your work by diving in the code... Kind regards Knut
@papoteur-mga I have the following, given the template mentioned above (and there are simple macros in the header and footer section - they were processed with the former version):
$section = new \PhpOffice\PhpWord\Element\Section(1); \PhpOffice\PhpWord\Shared\Html::addHtml($section, $htmlContent, false, true); $containers = $section->getElements(); for($i=0; $i<count($containers); $i++) { $templateProcessor->setComplexBlock('html#'.($i+1), $containers[$i]); }This results in one blank page, the block marks are gone, but not filled with the new (desired) content. And also header and footer aren't there anymore.
Can you give a complete example. I can't detect a problem. Did you included instructions like
$templateProcessor->cloneBlock('htmlblock', 3, true ,true);
before setComplexBlock ?
If I do additionally that after the above:
$textrun = $section->addTextRun(array('pageBreakBefore' => true)); $dim = @getimagesize($mf); // $mf = media file, an image filename $w = 170; // mm $h = $w * $dim[1] / $dim[0]; if ($h > 250) { $h = 250; $w = $h * $dim[0] / $dim[1]; } $textrun->addText('Headline for image', array('name' => 'Arial', 'size' => 14, 'bold' => true), array('spaceAfter' => 250, 'spaceBefore' => 250)); $textrun->addTextBreak(); $textrun->addText('${img#'.($i+1).'}'); $textrun->addTextBreak(); $textrun->addText('some text '.basename($mf)); $templateProcessor->setComplexBlock('html#'.$i, $textrun); $templateProcessor->setImageValue('img#'.($i+1), array('path' => realpath(__DIR__.'/.'.$mf), 'width' => intval($w).'mm', 'height' => intval($h).'mm'));This results in one page without header and footer and only with one line:
Headline for image${img#579}some text BMMH(5).jpgSo it seems, page breaks and setImageValue aren't handeled yet? Hope that helps you for further investigation and development. Unfortunately at the moment I haven't the time to assist your work by diving in the code... Kind regards Knut
The same here, did you clone blocks before?
@papoteur-mga Sorry for late reply... :pray:
- Yes, I have, just before the loop (sorry to miss this in my former code snippet):
$templateProcessor->cloneBlock('htmlblock', count($containers)+count($mediaFiles), true, true);
- see 1): count of containers plus media files
And the same code works with Docx template processor. So I guess an issue with template processing for Odt. Kind regards Knut
@knulo : no problem, I have no production constraints ;)
Can you check that the code$containers = $section->getElements(); provides what you expect?
And can you provide me something more complete, with your HTML code, for tests on my side?
@papoteur-mga
The statement $section->getElements() retrieves the right content (remember: the same code is working fine on Docx template processor).
I'll build a small testemonial, so I can provide you the base data - stay patient... :grin:
@papoteur-mga
Mea culpa!
While creating the testemonial I found out that I output a (not used) writer instance instead of template processor (commented out in code.php of the testemonial). So the result must be empty! Sorry for that!
Now the odt is generated with content (big surprise!) :grin: But besides that: No formatting (headlines, bold, italic) is done in case of Odt and the font (Arial) isn't process correct. The testemonial files are here
Kind regards and thanks for your patience Knut
Hi Knut, I have done some more investigation with your example, thanks for it. I think that this is in this portion of code in setComplexBlock:
$xmlWriter = new XMLWriter();
$xmlWriter->setIndent(false);
$elementWriter = new $objectClass($xmlWriter, $complexType, false);
$elementWriter->write();
$xmlWriter->getData();
At the last line, there is no formatting data. This is what is inserted in the document. Thus I would say that the XMLWriter for ODT doesn't provide the formatting you expect. This is the same code as for Word2007 templates. Thus, probably "not my fault" :/ I didn't build an testimonial to prove that.
Hello, I'm working on a hack to deal with ODT templates and that I will submit. I have added tests like for Word, but with the documents saved in ODT format. It already work, except for images for now. What I would ask is about the manner to call the processor. For now, I have added a class TemplateProcessorOdt, keeping TemplateProcessor to deal with Word format. I created a class TemplateProcessorCommon which share the common methods and that TemplateProcessor and TemplateProcessorOdt inherits. Is it OK this way? This will leave the responsibility ta call the good class to the caller. Or should I include in TemplateProcessor some checks about file format to call specific methods? By this way, I don't see how to use inheritance.
it's work, when i got missing watermark on convert docx to pdf with soffice. now i have odt generated file, then i convert odt to pdf and watermark is show 👍
Hello, You can have a look here : https://github.com/papoteur-mga/PHPWord/commits/develop
The "setImageValue" function did not work, the text with the key disappeared, but the image did not appear. In the docx it appears normal.