PHPWord icon indicating copy to clipboard operation
PHPWord copied to clipboard

Can I use a template (TemplateProcessor) for odt?

Open vierkantemeter opened this issue 9 years ago • 27 comments

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.

vierkantemeter avatar Dec 01 '16 10:12 vierkantemeter

I'm having the same issue - using LibreOffice 5.2.0.4

mikerockett avatar Dec 01 '16 16:12 mikerockett

TemplateProcessor can only deal with Word 2007 and higher :-/.

@Progi1984 to be closed

msphn avatar Dec 14 '16 10:12 msphn

@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.

mikerockett avatar Dec 14 '16 10:12 mikerockett

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 avatar Jan 26 '21 11:01 papoteur-mga

@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

knulo avatar Jan 27 '21 12:01 knulo

Hello, You can have a look here : https://github.com/papoteur-mga/PHPWord/commits/develop

papoteur-mga avatar Jan 28 '21 14:01 papoteur-mga

@papoteur-mga Thank you for sharing that.

knulo avatar Jan 28 '21 15:01 knulo

@knulo If you mean this is fine, I can request a pull.

papoteur-mga avatar Jan 28 '21 16:01 papoteur-mga

@papoteur-mga Please give me some time to test... BTW: Would be fine to have an output for PDF, right? ;-)

knulo avatar Jan 28 '21 16:01 knulo

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 avatar Jan 28 '21 17:01 papoteur-mga

@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.

knulo avatar Jan 28 '21 18:01 knulo

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 avatar Jan 28 '21 20:01 papoteur-mga

@papoteur-mga Thanks. I will discover that tomorrow. Kind regards, Knut

knulo avatar Jan 28 '21 21:01 knulo

@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

knulo avatar Jan 29 '21 15:01 knulo

Thanks for your test. This is a part I've not yet dealt with. I come back later.

papoteur-mga avatar Feb 03 '21 17:02 papoteur-mga

@papoteur-mga O.K. and good luck!

knulo avatar Feb 03 '21 17:02 knulo

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 avatar Feb 05 '21 16:02 papoteur-mga

@papoteur-mga Thanks! I'll check your improvements next days and report my experience... ;-)

knulo avatar Feb 05 '21 16:02 knulo

@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

knulo avatar Feb 06 '21 10:02 knulo

@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).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

The same here, did you clone blocks before?

papoteur-mga avatar Feb 13 '21 18:02 papoteur-mga

@papoteur-mga Sorry for late reply... :pray:

  1. 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);
  1. 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 avatar Feb 16 '21 11:02 knulo

@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 avatar Feb 16 '21 13:02 papoteur-mga

@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:

knulo avatar Feb 16 '21 14:02 knulo

@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

knulo avatar Feb 16 '21 16:02 knulo

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.

papoteur-mga avatar Feb 21 '21 18:02 papoteur-mga

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 👍

ucenxyz avatar Oct 08 '21 10:10 ucenxyz

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.

kalmonv avatar Mar 27 '24 03:03 kalmonv